那么,在进行光照计算之前,它有助于将所有内容转换为眼睛空间吗?我在转换部分遇到了麻烦。我已经将法线正确转换,但是当我应用平移时(当对象不在世界坐标系的中心时),灯光仍然完全相同。
我已经确认任何C ++代码都没有问题。 我会粘贴我的着色器......
问题:我想知道我没有改变什么,以及我应该如何改造它。
顶点着色器......
const int MAXLIGHTS = 4;
uniform int lightcount;
uniform vec4 lPositions[MAXLIGHTS];
//V = transformed vertex
//N = transformed normal
//E = eye vector
//L = vector from vertex to light
varying vec3 V, N, E, L[MAXLIGHTS];
void main()
{
int lcount = lightcount > MAXLIGHTS ? MAXLIGHTS : lightcount;
V = vec3(gl_ModelViewMatrix * gl_Vertex);
N = gl_NormalMatrix * gl_Normal;
E = normalize(-V);
for(int i = 0; i < lcount; i++)
{
L[i] = gl_NormalMatrix * normalize(vec3(lPositions[i] - gl_Vertex));
}
gl_FrontColor = gl_Color;
gl_Position = ftransform();
}
片段着色器......
const int MAXLIGHTS = 4;
uniform int lightcount;
uniform vec4 lDiffuses[MAXLIGHTS];
uniform vec4 lAmbients[MAXLIGHTS];
varying vec3 V, N, E, L[MAXLIGHTS];
uniform bool justcolor;
void main()
{
if(justcolor)
{
gl_FragColor = gl_Color;
return;
}
int lcount = lightcount > MAXLIGHTS ? MAXLIGHTS : lightcount;
vec4 ambient;
vec4 diffuse;
vec4 specular = vec4(0.0, 0.0, 0.0, 0.0);
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
vec3 H;
float NL;
float NH;
for(int i = 0; i < lcount; i++)
{
specular = vec4(0.0, 0.0, 0.0, 0.0);
ambient = lAmbients[i];
NL = dot(N, L[i]);
diffuse = lDiffuses[i] * max(NL, 0.0);
if(NL > 0.0)
{
H = normalize(E + L[i]);
NH = max(0.0, dot(N, H));
specular = pow(NH, 40.0) * vec4(0.3, 0.3, 0.3, 1.0);
}
color += gl_Color * (diffuse + ambient) + specular;
}
gl_FragColor = color;
}
答案 0 :(得分:3)
眼睛空间是场景在通过投影矩阵之前转换到的空间。这就是ftransform()
方便地包装的内容(通过这个我的意思是从模型空间到眼睛空间(模型视图变换)到剪辑空间(投影变换)的完整路径)。
模型视图矩阵包含从对象本地到眼睛空间的完整转换。但是,您的灯光不会位于(每个)对象的本地空间中,而是位于世界空间中。所以我们在这里处理两个不同的转换:
因此从技术上讲,通过将分解的模型视图作为模型提供并查看均匀矩阵输入,可以在顶点着色器中变换光源和对象顶点。然后你只需要通过视图部分变换光位置,然后按模型变换对象的顶点,然后查看部分。但我建议不这样做。着色器单元的计算资源应保留给每个顶点输入具有不同结果的计算。光位置变换不会这样做。
相反,您应该将光线位置预先转换为眼睛空间,然后再将它们传递到着色器(制服)。那怎么做呢。首先,我强烈建议你摆脱旧的OpenGL矩阵操作函数(glRotate,glTranslate,glScale,...和GLU助手,如gluPerspective,......)。没有它们会变得更容易,而且它们已经从以后的OpenGL版本中删除了。
那么怎么做呢。假设你有一个矩阵库,比如GLM,但任何其他都可以工作。要渲染场景,请遵循该方案(类似Python的伪代码)
render_scene:
projection_matrix = Matrix.Perspective(…)
view_matrix = Matrix.LookAt(…)
light_positions = []
for i, light in enumerate(scene.lights):
if i > MAX_LIGHTS:
break
light_positions.append( view_matrix * light.position )
glUniform3fv(glGetUniformLocation("lPositions"), light_positions)
for object in scene.objects:
obj_modelview = view_matrix * object.transform
# This is using deprecated functionality
glMatrixMode(GL_MODELVIEW); glLoadMatrix(obj_modelview)
object.draw()
如您所见,通过使用view_matrix将灯光“手动”转换为眼睛空间,而不是在GPU上触摸对象的顶点,而是为绘图设置着色器的参数。
答案 1 :(得分:2)
L[i] = gl_NormalMatrix * normalize(vec3(lPositions[i] - gl_Vertex));
如果lPositions
位于模型空间,则此代码才有意义。这是非常不可能的。
这种方法的一般方法是在眼睛空间中传递光位置,因此无需对其进行变换。
此外,L和E完全是多余的。通过在片段着色器中计算这些结果,您将获得更准确的结果。计算非常简单和便宜,并且因为你需要在片段着色器中重新规范化它们(你没有这样做),你实际上并没有得到任何东西。
L只是眼睛空间光位置 - 眼睛空间表面位置。 E只是从眼睛到位置的方向,这是眼睛空间表面位置的归一化否定。