【问题标题】:GLSL/GL2.1 Lighting: Transforming to Eye SpaceGLSL/GL2.1 照明:转换到眼睛空间
【发布时间】:2011-12-27 21:32:07
【问题描述】:

那么,在进行光照计算之前将所有内容转换为眼睛空间会有所帮助吗?我在转换部分遇到问题。我已经正确地转换了法线,但是当我应用平移时(当对象不在世界坐标系的中心时),光照保持完全相同。

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

【问题讨论】:

  • 我应该做哪些我没有做的事情来将我的变量转换到眼睛空间?

标签: opengl glsl shader


【解决方案1】:

眼睛空间是场景在通过投影矩阵之前转换到的空间。这就是ftransform() 方便地包装的内容(我的意思是从模型空间到眼睛空间(模型视图变换)到剪辑空间(投影变换)的完整路径)。

模型视图矩阵包含从对象局部到眼睛空间的完整转换。但是,您的灯光不会在(每个)对象的本地空间中,而是在世界空间中。所以我们在这里处理两个不同的转换:

  • World 的本地对象 - 这是 model 模型视图的一部分
  • World to Eye - 这是 view 部分

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

相反,您应该在将灯光位置传递给着色器(制服)之前将它们预先转换为眼睛空间。那么如何做到这一点。首先,我强烈建议你摆脱旧的 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 上并未触及对象的顶点,而是为绘图设置了着色器的参数。

【讨论】:

  • “这就是 ftransform() 方便地包装的内容。”不,它没有。 ftransform 一直转换到剪辑空间。规范对此非常清楚:使用它的正确方法是使用以下语句:gl_Position = ftransform();
  • @NicolBolas:实际上我的意思是整个管道一直到剪辑空间,尽管我承认我确实制​​定它很不开心。
  • 我认为您的第一个要点中的意思是“世界本地对象”而不是“眼睛本地对象”。
  • @Mortennobel:没错。将修复
【解决方案2】:
L[i] = gl_NormalMatrix * normalize(vec3(lPositions[i] - gl_Vertex));

仅当lPositions模型 空间中时,此代码才有意义。这是极不可能的。

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

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

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

【讨论】:

  • 如何将它们传递到眼睛空间?我需要重新规范化 L 和 E?它们的长度是否通过插值改变?如果我不在眼睛空间中传递它们,是否可以在我的着色器中将它们转换为它?
  • @MilesRufat-Latre:您将它们转换成眼睛空间并将它们粘在制服中。大概,你把你的灯放在世界空间里。因此,使用您的相机矩阵将它们转换为眼睛空间。这将要求您将矩阵从 OpenGL 中拉出并自己进行一些向量/矩阵乘法。
  • @MilesRufat-Latre:插值不知道也不关心值是否是归一化矢量方向。它独立于其余部分对向量的每个分量进行插值。所以完成后不会被归一化。
  • 相机矩阵由什么组成,如果不是简单的 gl_ModelViewMatrix?有什么方法可以在着色器中而不是在我的 C++ 中转换我的数据?
  • @MilesRufat-Latre:为什么要在着色器中进行?每个顶点的眼睛空间光位置都相同。这只是浪费时间。顾名思义,模型-视图矩阵从模型空间转换为视图/眼睛/相机空间。您可能使用 gluLookAt 或类似的东西来定义您的世界空间在哪里;这就是转换灯光所需的矩阵。
猜你喜欢
  • 2011-06-22
  • 2011-08-05
  • 2012-06-11
  • 2012-03-14
  • 1970-01-01
  • 1970-01-01
  • 2020-09-14
  • 1970-01-01
  • 2014-12-21
相关资源
最近更新 更多