GLSL - computing normals of fragments
I'm trying to create a program that displays a globe with terrain, and does all Lat/Long/Alt to XYZ (ECEF) on the GPU.
I've already written a working vertex shader that translates XYZ that represents Longitude, Latitude and Altitude (respectively) to their correct XYZ (using ECEF).
What I can't get done right is the lighting.
I've managed to light each vertex correctly using directional lights as long as there is no terrain data. Terrain data is not lighted correctly and I can't get different slopes to have correct shades.
This is the vertex shader I'm using:
const float a = 6378137.0;
const float f = 0.003352810664747480719845528618;
varying vec3 Normal;
varying vec3 ecPos;
vec3 LatLonAltToEcef(in vec3 latLonAlt)
{
vec3 v = latLonAlt;
float cosLat = cos(radians(v.y));
float sinLat = sin(radians(v.y));
float nfs = (1.0 - f) * (1.0 - f);
float C = 1.0 / (sqrt(cosLat * cosLat + nfs * sinLat * sinLat));
float S = nfs * C;
float lon = radians(v.x);
float h = v.z;
v.x = (a * C + h) * cosLat * cos(lon) / a;
v.y = (a * C + h) * cosLat * sin(lon) / a;
v.z = (a * S + h) * sinLat / a;
return v;
}
vec4 LatLonAltToEcef(in vec4 latLonAlt)
{
vec3 ecef = LatLonAltToEcef(latLonAlt.xyz);
return vec4(ecef.x, ecef.y, ecef.z, latLonAlt.w);
}
void main(void)
{
vec4 v = LatLonAltToEcef(gl_Vertex); //x=lon, y=lat, z=alt
ecPos = vec3(gl_ModelViewMatrix * v);
Normal = normalize(gl_NormalMatrix * v.xyz);
vec4 lightPos = LatLonAltToEcef(gl_LightSource[0].position);
vec3 lightDir = normalize(gl_NormalMatrix * lightPos.xyz);
float NdotL = max(dot(Normal, lightDir), 0.0);
vec4 diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
gl_FrontColor = NdotL * diffuse;开发者_如何学Python
gl_Position = gl_ModelViewProjectionMatrix * v;
}
In order to draw a globe grid this is what needs to be done:
for (float lat = -90; lat < 90; lat += 5)
{
glBegin(GL_LINE_LOOP);
for (float lon = -180; lon < 180; lon += 5)
glVertex3f(lon, lat, 0);
glEnd();
}
for (float lon = -180; lat < 180; lon += 5)
{
glBegin(GL_LINE_LOOP);
for (float lat = -90; lon < 90; lon += 5)
glVertex3f(lon, lat, 0);
glEnd();
}
Can any one please direct me how to shade the terrain correctly?
I feel that you need to study more computer graphics, as there seem to be some gaps in your basic knowledge about computer rendered geometry.
Anyway:
Getting the normal (surface direction) at any point usually require looking at the surroundings, if you don't have slope information from the beginning.
Secondly, you probably want to draw triangles, not lines.
Triangles have a surface. Surfaces has normals. Normals are vectors, that can have angles towards other vectors. Light direction is a vector. The dot product is your friend. :)
So, having a set of triangles connecting your vertices will help you in getting somewhere.
Also, avoid computing stuff in a shader that can be computed once and for all before any rendering.
Your problem is here :
Normal = normalize(gl_NormalMatrix * v.xyz);
By luck, you found a rare case where using the position of the vertex also gives you the normal : the sphere ( but since Earth is not a sphere, your lighting isn't 100% correct though)
When you add mountains, this isn't true anymore. Normals are perpendicular to the ground, so on a mountain, it's bent.
You need a line like this :
Normal = normalize(gl_NormalMatrix * gl_Normal.xyz);
gl_Normal is given by calls to glNormal3f, just before glVertex3f. But you don't say how you give terrain data so you'll have to find parameters to glNormal3f yourself. They must be expressed in model space (i.e. NOT lon/lat, but XYZ relative to the center of the earth, in a cartesian space. Again : a cartesian space.)
精彩评论