为什么我的物体在相机远离时会失去光线?

时间:2013-07-12 23:55:12

标签: opengl shader lighting

我正在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 );
}

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

2 个答案:

答案 0 :(得分:2)

问题在于:

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的原因,没有必要在世界空间中这样做。

答案 1 :(得分:0)

始终确保您的矢量全部位于同一空间(模型空间,世界空间或视图空间)。

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

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

所以最终的代码是:

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 );
}

我没有测试过它。