2011-11-13 31 views
1

因此,它有助于在进行照明计算之前将所有东西都转换为眼睛空间?我在转换部分遇到问题。我已经正确地转换了法线,但是当我应用翻译时(当对象不在世界坐标系的中心时),照明保持完全相同。GLSL/GL2.1照明:转换到眼睛空间

我已确认任何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

你的问题是什么? –

+0

我该怎么做,我没有把我的变量转换成眼睛空间? – Miles

回答

2
L[i] = gl_NormalMatrix * normalize(vec3(lPositions[i] - gl_Vertex)); 

这段代码才有意义,如果lPositions模型空间。这是不太可能的。

这种工作方式的一般方法是,您可以在眼睛空间中传递光照位置,因此无需转换它们。

此外,L和E是完全多余的。通过在片段着色器中计算这些结果,您将获得更准确的结果。计算非常简单而且便宜,而且因为无论如何你都需要在片段着色器中重新规格化它们(你不这么做),所以你并没有真正得到任何东西。

L只是眼睛的光线位置 - 眼睛的空间位置。 E仅仅是从眼睛到位置的方向,这是眼睛空间表面位置的归一化否定。

+0

我怎样才能让他们进入眼睛空间?我需要重新正常化L和E?他们的长度是否因插值而改变?如果我没有将它们传入眼睛空间,是否有可能在我的着色器中将它们转换为它? – Miles

+0

@ MilesRufat-Latre:你可以将它们转换成眼睛空间,并将它们粘在一起。据推测,你可以将灯光置于世界空间。所以用你的相机矩阵把它们转换成眼睛空间。这将需要你从OpenGL中抽取矩阵,并自己做一些向量/矩阵乘法。 –

+0

@ MilesRufat-Latre:插值不知道或在意值是否是归一化的矢量方向。它独立于其他内容插入矢量的每个组件。所以它在完成时不会被标准化。 –

3

眼睛空间是您的场景在穿过投影矩阵之前被转换的空间。这就是ftransform()方便包装(由此我意味着从模型空间到眼睛空间(模型视图变换)到剪辑空间(投影变换))的完整路径。

模型视图矩阵包含从对象局部到眼睛空间的完整转换。然而,你的灯不会在(每个)物体的本地空间中,而是在世界空间中。因此,我们正在处理两个不同的转换位置:

  • 对象本地的世界 - 这是模型视图的模型部分
  • 世界到眼睛 - 这是视图部分

所以从技术上讲,可以通过提供分解的模型视图作为模型并查看统一的矩阵输入,来转换顶点着色器中的光源和对象顶点。然后,您只需通过视图部分来转换光照位置,然后通过模型转换对象的顶点,然后查看部分。但我建议不要那样做。着色器单元的计算资源应该保留给每个顶点输入具有不同结果的计算。光位置转换不会这样做。

相反,您应该在将光线位置传递给着色器(制服)之前将其转换为眼睛空间。那么如何做到这一点。首先我强烈建议你摆脱旧的OpenGL矩阵操作函数(glRotate,glTranslate,glScale,...和gluPerspective等GLU帮助程序)。如果没有它们,情况会变得更加容易,而且它们已经从后来的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

“这就是ftransform()方便打包的内容。”不,它没有。 'ftransform'一直转换到剪辑空间。这个规范很清楚:使用它的正确方法是使用下面的语句:'gl_Position = ftransform();' –

+0

@NicolBolas:其实我的意思是整个管道直通剪辑空间,尽管我承认我确实制​​定了它很不高兴。 – datenwolf

+0

我认为你的意思是'对象局部于世界'而不是'在眼睛中局部对象'在你的第一个要点。 – Mortennobel