【问题标题】:Why does my objects lose its lighting when the camera gets far away?为什么当相机远离我的物体时,我的物体会失去光?
【发布时间】:2013-07-11 15:04:15
【问题描述】:

我正在 OpenGL 2.1 中构建一个 3D 场景,并使用带有 Phong 光照模型的定向光来照亮它。

近距离看起来一切正常,但是当相机远离模型时,它们会失去所有照明(环境光除外)。

什么会导致这种情况发生?

这是顶点着色器:

uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;
uniform vec3 lightDir;

out vec3 normal;
out vec3 lightDir_viewSpace;
out vec3 vertexPos_viewSpace;

void main(){
    normal = normalize( normalMatrix * gl_Normal );
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * gl_Vertex;

    vertexPos_viewSpace = - ( viewMatrix * modelMatrix * gl_Vertex ).xyz;;
    lightDir_viewSpace = normalize( viewMatrix * vec4(lightDir, 1) ).xyz;
}

这里是片段着色器:

uniform vec3 Ka;
uniform vec3 Kd;
uniform vec3 Ks;
uniform float Shininess;

in vec3 normal;
in vec3 lightDir_viewSpace;
in vec3 vertexPos_viewSpace;

float getdiffuseIntensity( vec3 N, vec3 L ){
    float intensity = clamp(dot(L , N), 0.0, 1.0);
    return intensity;
}

float getSpecularIntensity( vec3 N, vec3 L, vec3 vertexPos, float shine ){
    vec3 R = normalize( reflect( -L, N ) );
    vec3 V = normalize( vertexPos );
    float intensity = 0.0;
    if ( dot(N, L) > 0.0 ){
        float cosVR = clamp( dot(V, R), 0.0, 1.0 );
        intensity = pow( cosVR, shine );
    }
    return intensity;
}

void main(){
    vec3 normalNorm = normalize( normal );
    vec3 lightDirNorm = normalize( lightDir_viewSpace );
    vec3 vertexDirNorm = normalize( vertexPos_viewSpace );

    vec3 ilumAmbi = Ka;
    vec3 ilumDiff = Kd * getdiffuseIntensity( normalNorm, lightDirNorm );
    vec3 ilumEspec = Ks * getSpecularIntensity( normalNorm, lightDirNorm, vertexDirNorm, Shininess );

    gl_FragColor = vec4( ilumAmbi + ilumDiff + ilumEspec , 1.0 );
}

如果有人问;是的,这是一个学校项目
我做错了吗?

【问题讨论】:

    标签: opengl shader lighting


    【解决方案1】:

    问题出在这里:

    lightDir_viewSpace = normalize( viewMatrix * vec4(lightDir, 1) ).xyz
    

    它所做的是将lightDir 解释为一个点 (x,y,z,1) 而不是一个向量 (x,y,z,0)。您的代码会执行某种点光源,这就是光照随相机距离变化的原因。所以正确的代码是

    lightDir_viewSpace = normalize( viewMatrix * vec4(lightDir, 0) ).xyz
    

    但是,请注意,对于每个顶点和每个片段,此表达式始终计算为相同的向量。所以实际上最好在 CPU 上计算它,而不是在片段着色器中使用uniform vec3 lightDir_viewSpace

    这也意味着您不再需要单独的 viewMatrixmodelMatrix 制服。相反,在 CPU 上计算 modelViewMatrix = viewMatrix * modelMatrix,然后在顶点着色器中使用 uniform mat4 modelViewMatrix

    这也是why lighting in view space is fine的原因,在世界空间里没必要这么做。

    【讨论】:

    • 我不知道点和向量之间有区别,谢谢!
    【解决方案2】:

    始终确保您的向量都在同一个空间(模型空间、世界空间或视图空间)中。

    您的法线在世界空间中,而您的 lightDir 在视图空间中,因此您应该将 lightDir 更改为世界空间,这更容易作为您的输入 (uniform vec3 lightDir;) 已经在世界空间中。

    第二个错误是你的高光计算。在 Phong 模型中,镜面高光是通过使用理想反射矢量 (vec3 R = normalize( reflect( -L, N ) );) 与观察者方向之间的角度(dot(V, R) 所做的)来计算的。

    所以最终的代码是:

    uniform mat4 viewMatrix;
    uniform mat4 modelMatrix;
    uniform mat4 projectionMatrix;
    uniform mat3 normalMatrix;
    uniform vec3 viewPos; // the Position of your view in world coordinates
    
    out vec3 normal;
    out vec3 viewDir;
    
    void main(){
        normal = normalize( normalMatrix * gl_Normal );
        gl_Position = projectionMatrix * viewMatrix * modelMatrix * gl_Vertex;
        vec4 worldPos = modelMatrix * gl_Vertex;
    
        viewDir = worldPos.xyz - viewPos; // maybe you have to change the ordering, i'm not sure
    }
    

    和:

    uniform vec3 Ka;
    uniform vec3 Kd;
    uniform vec3 Ks;
    uniform float Shininess;
    uniform vec3 lightDir;
    
    in vec3 normal;
    in vec3 viewDir;
    
    float getdiffuseIntensity( vec3 N, vec3 L ){
        float intensity = clamp(dot(L , N), 0.0, 1.0);
        return intensity;
    }
    
    float getSpecularIntensity( vec3 N, vec3 L, vec3 vertexPos, float shine ){
        vec3 R = normalize( reflect( -L, N ) );
        vec3 V = normalize( vertexPos );
        float intensity = 0.0;
        if ( dot(N, L) > 0.0 ){
            float cosVR = clamp( dot(V, R), 0.0, 1.0 );
            intensity = pow( cosVR, shine );
        }
        return intensity;
    }
    
    void main(){
        vec3 normalNorm = normal; // no need to normalize
        vec3 viewNorm = normalize( viewDir );
    
        vec3 ilumAmbi = Ka;
        vec3 ilumDiff = Kd * getdiffuseIntensity( normalNorm, lightDir);
        vec3 ilumEspec = Ks * getSpecularIntensity( normalNorm, lightDir, viewDirNorm, Shininess );
    
        gl_FragColor = vec4( ilumAmbi + ilumDiff + ilumEspec , 1.0 );
    }
    

    不过我没有测试过。

    【讨论】:

    • vec3 normalNorm = normal; // no need to normalize 这是不正确的,normal 是在三角形上插值的,因此您确实需要标准化。 viewDir = worldPos.xyz - viewPos; 1. 正好相反,2. 你在做与 OP 完全相同的事情,只是在世界空间中。请注意,OP 的 vertexPos_viewSpace = - ( viewMatrix * modelMatrix * gl_Vertex ).xyz 实际上不是顶点位置,而是视图空间中从顶点到相机 (0,0,0) 的向量,因此归一化是您的 viewDir
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 2020-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-08
    相关资源
    最近更新 更多