]> git.mxchange.org Git - simgear.git/commitdiff
Stuart Buchanan :
authorfredb <fredb>
Sun, 23 Nov 2008 12:14:56 +0000 (12:14 +0000)
committerfredb <fredb>
Sun, 23 Nov 2008 12:14:56 +0000 (12:14 +0000)
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
simgear/scene/sky/CloudShaderGeometry.hxx
simgear/scene/sky/cloudfield.cxx
simgear/scene/sky/cloudfield.hxx
simgear/scene/sky/newcloud.cxx

index 3d8f4320253f2b848a65b26ddc40b25a4ee3c70a..87905434ec31489760dc55211330f2a4e60f10c3 100755 (executable)
@@ -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;
index 1f0dd10d413b27aae12356b6a78e6084db43c11e..6e6ac4d203416e21501ead75f4be54d6b6804625 100755 (executable)
@@ -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;
index 5012d942a2a2fa4dca7479dec900bd86e6743b2e..0d225a0a3ae05dd1d857f420c1aacf966deb4316 100644 (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);
index cd99f8afb230a8ea2bd45c27ddbb7246f566c406..40333d6d81d1abce007ee85d3956279e0306a3ae 100644 (file)
@@ -109,7 +109,7 @@ public:
 
        static float density;
        static double timer_dt;
-       static double fieldSize;
+       static float fieldSize;
        
         bool defined3D;
 
index bf2ffca4de769d9c4b2d1c454e94083456d62a64..1ee3c5b3305ced0f75d18d4a7e479b5de27a3b86 100644 (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);