GLSL - 计算片段的法线
我正在尝试创建一个程序来显示带有地形的地球仪,并在 GPU 上执行所有纬度/经度/Alt 到 XYZ (ECEF) 的操作。
我已经编写了一个可用的顶点着色器,它将代表经度、纬度和高度(分别)的 XYZ 转换为正确的 XYZ(使用 ECEF)。
我无法正确完成的是照明。
只要没有地形数据,我就设法使用定向光正确照亮每个顶点。地形数据没有正确照亮,我无法让不同的坡度具有正确的阴影。
这是我正在使用的顶点着色器:
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;
gl_Position = gl_ModelViewProjectionMatrix * v;
}
为了绘制地球网格,需要执行以下操作:
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();
}
任何人都可以指导我如何正确地对地形进行着色吗?
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;
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我觉得你需要学习更多的计算机图形学,因为你对计算机渲染几何的基础知识似乎存在一些差距。
无论如何:
如果您从一开始就没有坡度信息,那么在任何点获取法线(表面方向)通常需要查看周围环境。
其次,您可能想绘制三角形,而不是直线。
三角形有一个表面。曲面具有法线。法线是向量,可以与其他向量成角度。光的方向是一个矢量。点积是你的朋友。 :)
因此,拥有一组连接顶点的三角形将帮助您到达某个地方。
另外,避免在着色器中计算可以在任何渲染之前一次性计算的内容。
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.
你的问题在这里:
幸运的是,你发现了一种罕见的情况,使用顶点的位置也给你法线:球体(但由于地球不是球体,所以你的照明不是100%正确)
当你添加山,这不再是真的了。法线垂直于地面,因此在山上,它是弯曲的。
你需要这样的一行:
gl_Normal是通过调用glNormal3f给出的,就在glVertex3f之前。但是您没有说明如何提供地形数据,因此您必须自己找到 glNormal3f 的参数。它们必须在模型空间中表示(即不是经度/纬度,而是相对于地球中心的 XYZ,在笛卡尔空间中。再次强调:笛卡尔空间。)
Your problem is here :
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 :
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.)