Stuart Buchanan :

Attached is a small patch for 3D clouds.
It provide the following:
1) Proper spherical distribution of sprites (previously they were distributed cylindrically - whoops)
2) Better shading, so the bottom of the cloud is darker than the top.
3) Fixed a couple of texture sizing bugs.
This commit is contained in:
fredb
2008-11-23 12:14:56 +00:00
parent 98cecfe940
commit 27de1e271e
5 changed files with 73 additions and 36 deletions

View File

@@ -69,6 +69,7 @@ void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
extensions->glVertexAttrib1f(WIDTH, (GLfloat) (*t)->width);
extensions->glVertexAttrib1f(HEIGHT, (GLfloat) (*t)->height);
extensions->glVertexAttrib1f(SHADE, (GLfloat) (*t)->shade);
extensions->glVertexAttrib1f(CLOUD_HEIGHT, (GLfloat) (*t)->cloud_height);
glColor4f((*t)->position.x(), (*t)->position.y(), (*t)->position.z(), 1.0);
_geometry->draw(renderInfo);
}
@@ -114,13 +115,13 @@ bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
while (!fr.eof() && fr[0].getNoNestedBrackets() > entry) {
SGVec3f v;
int tx, ty;
float w, h, s;
float w, h, s, ch;
if (fr[0].getFloat(v.x()) && fr[1].getFloat(v.y())
&& fr[2].getFloat(v.z()) && fr[3].getInt(tx) && fr[3].getInt(ty) &&
fr[4].getFloat(w) && fr[4].getFloat(h)&& fr[4].getFloat(s)) {
&& fr[2].getFloat(v.z()) && fr[3].getInt(tx) && fr[4].getInt(ty) &&
fr[5].getFloat(w) && fr[6].getFloat(h)&& fr[7].getFloat(s) && fr[8].getFloat(ch)) {
fr += 5;
//SGVec3f* v = new SGVec3f(v.x(), v.y(), v.z());
geom._cloudsprites.push_back(new CloudShaderGeometry::CloudSprite(v, tx, ty, w, h,s));
geom._cloudsprites.push_back(new CloudShaderGeometry::CloudSprite(v, tx, ty, w, h,s,ch));
} else {
++fr;
}
@@ -144,8 +145,9 @@ bool CloudShaderGeometry_writeLocalData(const Object& obj, Output& fw)
++itr) {
fw.indent() << (*itr)->position.x() << " " << (*itr)->position.y() << " "
<< (*itr)->position.z() << " " << (*itr)->texture_index_x << " "
<< (*itr)->texture_index_y << " "
<< (*itr)->width << " " << (*itr)->height << " " << (*itr)->shade << std::endl;
<< (*itr)->texture_index_y << " " << (*itr)->width << " "
<< (*itr)->height << " " << (*itr)->shade
<< (*itr)->cloud_height << " "<< std::endl;
}
fw.moveOut();
fw.indent() << "}" << std::endl;

View File

@@ -47,6 +47,7 @@ class CloudShaderGeometry : public osg::Drawable
const static unsigned int WIDTH = 13;
const static unsigned int HEIGHT = 14;
const static unsigned int SHADE = 15;
const static unsigned int CLOUD_HEIGHT = 16;
CloudShaderGeometry()
{
@@ -66,8 +67,8 @@ class CloudShaderGeometry : public osg::Drawable
META_Object(flightgear, CloudShaderGeometry);
struct CloudSprite {
CloudSprite(SGVec3f& p, int tx, int ty, float w, float h, float s) :
position(p), texture_index_x(tx), texture_index_y(ty), width(w), height(h), shade(s)
CloudSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float ch) :
position(p), texture_index_x(tx), texture_index_y(ty), width(w), height(h), shade(s), cloud_height(ch)
{ }
SGVec3f position;
@@ -76,14 +77,15 @@ class CloudShaderGeometry : public osg::Drawable
float width;
float height;
float shade;
float cloud_height;
};
typedef std::vector<CloudSprite*> CloudSpriteList;
void insert(CloudSprite* t)
{ _cloudsprites.push_back(t); }
void insert(SGVec3f& p, int tx, int ty, float w, float h, float s)
{ insert(new CloudSprite(p, tx, ty, w, h, s)); }
void insert(SGVec3f& p, int tx, int ty, float w, float h, float s, float ch)
{ insert(new CloudSprite(p, tx, ty, w, h, s, ch)); }
unsigned getNumCloudSprite() const
{ return _cloudsprites.size(); }
@@ -102,7 +104,7 @@ class CloudShaderGeometry : public osg::Drawable
_geometry = geometry;
}
void addSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float cull)
void addSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float cull, float cloud_height)
{
// Only add the sprite if it is further than the cull distance to all other sprites
for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
@@ -116,7 +118,7 @@ class CloudShaderGeometry : public osg::Drawable
}
}
_cloudsprites.push_back(new CloudSprite(p, tx, ty, w, h, s));
_cloudsprites.push_back(new CloudSprite(p, tx, ty, w, h, s, cloud_height));
}
osg::ref_ptr<osg::Drawable> _geometry;

View File

@@ -61,9 +61,10 @@ using std::vector;
#include <ieeefp.h>
#endif
double SGCloudField::fieldSize = 50000.0;
float SGCloudField::density = 100.0;
float SGCloudField::fieldSize = 50000.0f;
float SGCloudField::density = 100.0f;
double SGCloudField::timer_dt = 0.0;
int reposition_count = 0;
sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
void SGCloudField::set_density(float density) {
@@ -76,6 +77,11 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
{
osg::Matrix T, LON, LAT;
// Calculating the reposition information is expensive.
// Only perform the reposition every 60 frames.
reposition_count = (reposition_count + 1) % 60;
if ((reposition_count != 0) || !defined3D) return false;
SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
double dist = SGGeodesy::distanceM(cld_pos, pos);

View File

@@ -109,7 +109,7 @@ public:
static float density;
static double timer_dt;
static double fieldSize;
static float fieldSize;
bool defined3D;

View File

@@ -70,6 +70,7 @@ static char vertexShaderSource[] =
"attribute float wScale;\n"
"attribute float hScale;\n"
"attribute float shade;\n"
"attribute float cloud_height;\n"
"void main(void)\n"
"{\n"
" gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);\n"
@@ -88,18 +89,19 @@ static char vertexShaderSource[] =
" gl_Position.xyz += gl_Vertex.y * r * hScale;\n"
" gl_Position.xyz += gl_Vertex.z * w;\n"
" gl_Position.xyz += gl_Color.xyz;\n"
// Determine a lighting normal based on the sprites position from the
// center of the cloud.
// Determine a lighting normal based on the vertex position from the
// center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
" float n = dot(normalize(gl_LightSource[0].position.xyz), normalize(mat3x3(gl_ModelViewMatrix) * gl_Position.xyz));\n"
// Determine the position - used for fog and shading calculations
" vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);\n"
" float fogCoord = abs(ecPosition.z);\n"
" float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);\n"
// Final position of the sprite
" gl_Position = gl_ModelViewProjectionMatrix * gl_Position;\n"
// Limit the normal range from [0,1.0], and apply the shading (vertical factor)
" n = min(smoothstep(-0.5, 0.5, n), shade);\n"
" n = min(smoothstep(-0.5, 0.5, n), shade * (1.0 - fract) + fract);\n"
// This lighting normal is then used to mix between almost pure ambient (0) and diffuse (1.0) light
" vec4 backlight = 0.8 * gl_LightSource[0].ambient + 0.2 * gl_LightSource[0].diffuse;\n"
" vec4 backlight = 0.9 * gl_LightSource[0].ambient + 0.1 * gl_LightSource[0].diffuse;\n"
" gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);\n"
" gl_FrontColor += gl_FrontLightModelProduct.sceneColor;\n"
// As we get within 100m of the sprite, it is faded out
@@ -199,7 +201,7 @@ SGNewCloud::SGNewCloud(const SGPath &tex_path,
// Generate the shader etc, if we don't already have one.
if (!program.valid()) {
alphaFunc = new AlphaFunc;
alphaFunc->setFunction(AlphaFunc::GREATER,0.002f);
alphaFunc->setFunction(AlphaFunc::GREATER,0.001f);
program = new Program;
baseTextureSampler = new osg::Uniform("baseTexture", 0);
Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource);
@@ -209,6 +211,7 @@ SGNewCloud::SGNewCloud(const SGPath &tex_path,
program->addBindAttribLocation("wScale", CloudShaderGeometry::WIDTH);
program->addBindAttribLocation("hScale", CloudShaderGeometry::HEIGHT);
program->addBindAttribLocation("shade", CloudShaderGeometry::SHADE);
program->addBindAttribLocation("cloud_height", CloudShaderGeometry::CLOUD_HEIGHT);
Shader* fragment_shader = new Shader(Shader::FRAGMENT, fragmentShaderSource);
program->addShader(fragment_shader);
@@ -294,25 +297,44 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
Geode* geode = new Geode;
CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y);
// Determine how big this specific cloud instance is. Note that we subtract
// the sprite size because the width/height is used to define the limits of
// the center of the sprites, not their edges.
float width = min_width + sg_random() * (max_width - min_width) - max_sprite_width;
float height = min_height + sg_random() * (max_height - min_height) - max_sprite_height;
float width = min_width + sg_random() * (max_width - min_width) - min_sprite_width;
float height = min_height + sg_random() * (max_height - min_height) - min_sprite_height;
// Determine the cull distance. This is used to remove sprites that are too close together.
// The value is squared as we use vector calculations.
float cull_distance_squared = min_sprite_height * min_sprite_height * 0.1f;
float cull_distance_squared = min_sprite_height * min_sprite_height * 0.05f;
for (int i = 0; i < num_sprites; i++)
{
// Determine the position of the sprite. Rather than being completely random,
// sprites are placed on a squashed sphere.
double theta = sg_random() * SGD_2PI;
float x = width * cos(theta) * 0.5f;
float y = width * sin(theta) * 0.5f;
float z = height * cos(sg_random() * SGD_2PI) * 0.5f;
// we place them on the surface of a distorted sphere. However, we place
// the first and second sprites on the top and bottom, and the third in the
// center of the sphere (and at maximum size) to ensure good coverage and
// reduce the chance of there being "holes" in our cloud.
float x, y, z;
if (i == 0) {
x = 0;
y = 0;
z = height * 0.5f;
} else if (i == 1) {
x = 0;
y = 0;
z = - height * 0.5f;
} else if (i == 2) {
x = 0;
y = 0;
z = 0;
} else {
double theta = sg_random() * SGD_2PI;
double elev = sg_random() * SGD_PI;
x = width * cos(theta) * 0.5f * sin(elev);
y = width * sin(theta) * 0.5f * sin(elev);
z = height * cos(elev) * 0.5f;
}
SGVec3f *pos = new SGVec3f(x, y, z);
@@ -320,12 +342,10 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
float sprite_width = 1.0f + sg_random() * (max_sprite_width - min_sprite_width) / min_sprite_width;
float sprite_height = 1.0f + sg_random() * (max_sprite_height - min_sprite_height) / min_sprite_height;
// The shade varies from bottom_shade to 1.0 non-linearly
float shade;
if (z > 0.0f) {
shade = 1.0f;
} else {
shade = ((2 * z + height) / height) * (1 - bottom_shade) + bottom_shade;
if (i == 2) {
// The center sprite is always maximum size to fill up any holes.
sprite_width = 1.0f + (max_sprite_width - min_sprite_width) / min_sprite_width;
sprite_height = 1.0f + (max_sprite_height - min_sprite_height) / min_sprite_height;
}
// Determine the sprite texture indexes;
@@ -335,7 +355,14 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
int index_y = (int) floor(sg_random() * num_textures_y);
if (index_y == num_textures_y) { index_y--; }
sg->addSprite(*pos, index_x, index_y, sprite_width, sprite_height, shade, cull_distance_squared);
sg->addSprite(*pos,
index_x,
index_y,
sprite_width,
sprite_height,
bottom_shade,
cull_distance_squared,
height * 0.5f);
}
sg->setGeometry(quad);