From 27de1e271e780a0e680ff34a61541e1d43f294b1 Mon Sep 17 00:00:00 2001 From: fredb Date: Sun, 23 Nov 2008 12:14:56 +0000 Subject: [PATCH] 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. --- simgear/scene/sky/CloudShaderGeometry.cxx | 14 +++-- simgear/scene/sky/CloudShaderGeometry.hxx | 14 +++-- simgear/scene/sky/cloudfield.cxx | 10 +++- simgear/scene/sky/cloudfield.hxx | 2 +- simgear/scene/sky/newcloud.cxx | 69 ++++++++++++++++------- 5 files changed, 73 insertions(+), 36 deletions(-) diff --git a/simgear/scene/sky/CloudShaderGeometry.cxx b/simgear/scene/sky/CloudShaderGeometry.cxx index 3d8f4320..87905434 100755 --- a/simgear/scene/sky/CloudShaderGeometry.cxx +++ b/simgear/scene/sky/CloudShaderGeometry.cxx @@ -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; diff --git a/simgear/scene/sky/CloudShaderGeometry.hxx b/simgear/scene/sky/CloudShaderGeometry.hxx index 1f0dd10d..6e6ac4d2 100755 --- a/simgear/scene/sky/CloudShaderGeometry.hxx +++ b/simgear/scene/sky/CloudShaderGeometry.hxx @@ -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 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 _geometry; diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index 5012d942..0d225a0a 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -61,9 +61,10 @@ using std::vector; #include #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); diff --git a/simgear/scene/sky/cloudfield.hxx b/simgear/scene/sky/cloudfield.hxx index cd99f8af..40333d6d 100644 --- a/simgear/scene/sky/cloudfield.hxx +++ b/simgear/scene/sky/cloudfield.hxx @@ -109,7 +109,7 @@ public: static float density; static double timer_dt; - static double fieldSize; + static float fieldSize; bool defined3D; diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index bf2ffca4..1ee3c5b3 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -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 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 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 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); -- 2.39.5