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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
|
||||
static float density;
|
||||
static double timer_dt;
|
||||
static double fieldSize;
|
||||
static float fieldSize;
|
||||
|
||||
bool defined3D;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user