Struggling with casting cloud shadow on earth sphere in OpenGL

Started by
4 comments, last by Piyush Verma 8 years, 7 months ago

I am trying to do an earth simulation in OpenGL with GLSL shaders, and so far it's been going decent. Although I am stuck with a slightly small problem. Right now I have 3 spheres, one for ground level (earth), one for clouds and the third for the atmosphere (scattering effects). The earth sphere handles with most of the textures.

The cloud sphere is a slightly bigger sphere than the earth sphere, and is mapped with a cloud texture and normal mapped using one created with the photoshop plugin. One more thing to point out is, the rotation speed of the cloud sphere is slightly greater than the rotation speed of the earth sphere.

This is where things get confusing for me. I am trying to cast the shadow of the clouds onto the ground (earth) sphere by passing the cloud texture into the earth sphere's shader and subtracting the cloud's color from earth's color. But since the rotation speeds of the two sphere's are different, I figured if I multiplied the rotation matrix of the cloud sphere with the uv coordinates for the cloud texture, that should solve the problem. But sadly, the shadows and the clouds do not seem to rotate in sync. I was hoping if anyone can help me figure out the math to make the shadows and the cloud rotate in sync with each other, no matter how different the rotation speeds of the two sphere are.

Here is my fragment shader for the earth where I'm calculating the cloud's shadow:


#version 400 core

uniform sampler2D day;
uniform sampler2D bumpMap;
uniform sampler2D night;
uniform sampler2D specMap;
uniform sampler2D clouds;

uniform mat4 cloudRotation;


in vec3 vPos;

in vec3 lightVec;
in vec3 eyeVec;
in vec3 halfVec;

in vec2 texCoord;

out vec4 frag_color;

void main()
{

    vec3 normal = 2.0 * texture(bumpMap, texCoord).rgb - 1.0;
    //normal.z = 1 - normal.x * normal.x - normal.y * normal.y;
    normal = normalize ( normal );

    vec4 spec = vec4(1.0, 0.941, 0.898, 1.0);
    vec4 specMapColor = texture2D(specMap, texCoord);

    vec3 L = lightVec;
    vec3 N = normal;
    vec3 Emissive = normalize(-vPos);
    vec3 R = reflect(-L, N);
    float dotProd = max(dot(R, Emissive), 0.0);
    vec4 specColor = spec * pow(dotProd,6.0) * 0.5;
    float diffuse = max(dot(N, L), 0.0);

    vec2  cloudTexCoord         =   vec2(cloudRotation * vec4(texCoord, 0.0, 1.0));

    vec3 cloud_color            =   texture2D( clouds, cloudTexCoord).rgb;
    vec3 day_color              =   texture2D( day, texCoord ).rgb * diffuse + specColor.rgb * specMapColor.g - cloud_color * 0.25;// * (1 - cloud_color.r) + cloud_color.r * diffuse;
    vec3 night_color            =   texture2D( night, texCoord ).rgb * 0.5;// * (1 - cloud_color.r) * 0.5;

    vec3 color = day_color;
    if(dot(N, L) < 0.1)
        color = mix(night_color, day_color, (diffuse + 0.1) * 5.0);
    frag_color = vec4(color, 1.0);
}

Here's a sample output as a result of the above shader. Note that the shadows start out at the correct position, but the due to the wrong rotation speed, they tend to move ahead of the rotation of the cloud sphere.

OYrcQjI.gif

Again, it would be really helpful if anyone can help me figure out the math behind keep the shadow and the clouds in sync

Thanks in advance

Advertisement

Try generating a new rotation matrix just for the cloud shadow. Do not use the same one, because it will add its rotation to the earths rotation matrix.

Generate the new rotation matrix by subtracting the clouds rotation by the earths rotation, and then create a rotation matrix out of those newly calculated rotations. Plug it into your shader, and that should fix the problem.

View my game dev blog here!

so from what I'm understanding you get the value of a cloud texture over a certain point on earth and you subtract the cloud color from the day color, which in a way simulates the way shadow is cast, well its not a proper way to calculate the shadows, this wont result in shadows expected from a directional light (which in your case sun is a spot light rather than a directional like).

but any ways you shouldn't be rotating the coordinates of texture instead depending on the speed of rotation and the axis around which you're rotating the cloud sphere, you should add a value to the texture coordinates

.

lets say you're rotating the cloud sphere around the Y axis and you have a speed of 1degree/frame so your coordinates at the nth frame will be:

texture_coord+vec2(n/360 , 0);

(well this is if you wrap the texture around the sphere as a cylinder)

Sadly, taking the difference of the two rotations and rotating the uv coord didn't work either. But the method that IYP suggested worked :

you shouldn't be rotating the coordinates of texture instead depending on the speed of rotation and the axis around which you're rotating the cloud sphere, you should add a value to the texture coordinates

.

lets say you're rotating the cloud sphere around the Y axis and you have a speed of 1degree/frame so your coordinates at the nth frame will be:

texture_coord+vec2(n/360 , 0);

Although, I had to subtract the value instead of adding it and to the uv coord instead though, but that maybe because of the direction in which I'm rotating the sphere. But that works for now. Also you mentioned that this method won't work in case of a directional light, could you explain how would I go about implementing that so that the directional light always takes in account the angle at which the shadow is casted?

Thanks again for the help

well certainly normal shadow mapping methods aren't necessary here and they will introduce aliasing problem, which you don't want to deal with, i'd recommend a simple change in the same method you already use.

you can draw a none lit cloud sphere to a texture from sun's view, with orthogonal projection of course, (also apply face culling, you only need front faces). then you access that texture and then get the value and do the same thing you already do with the cloud value.

this is quite like shadow mapping but instead of depth you write cloud color values and instead of depth test you subtract the value of the cloud color from day color.

Ah got it! I'll try it out when I'm done adding other features in this project! Thanks for the suggestion! :)

This topic is closed to new replies.

Advertisement