【问题标题】:Issue converting a world space light position to model space in fragment shader在片段着色器中将世界空间光位置转换为模型空间的问题
【发布时间】:2021-02-14 15:47:18
【问题描述】:

在向具有变换模型的场景添加光时,我正在尝试解决模型与世界空间坐标的古老问题。

我有一个带有两个实例的房屋模型。一个头骨,我场景中的一个单点光源以及一些地形和一个天空盒。我注意到当对我的模型应用一些旋转时,点光源提供的照明会随之旋转。经过一番阅读,很明显我在模型空间中的着色器中进行了所有计算,但我的灯光位置/方向在世界空间中。

因此,我意识到我需要统一空间以进行计算,并且我认为将着色器保留在模型空间中并将灯光组件从世界转换为模型是有意义的。

当我不进行转换时,它看起来几乎是正确的(虽然我打赌的是不同空间的 b/c 并不完美)。我在灯光的位置渲染了一个小灯泡模型,房屋的顶部和地形都照我预期的相对较好。

当我将灯光位置转换回模型空间时,我预计灯光仍会从灯泡模型中照亮。但是它变得很不稳定。

我在模型上没有旋转,但有一些小的平移,所以我很困惑为什么光源似乎围绕 x 轴旋转了 90 度...

  <model name="blackSmith" mesh="black_smith/">
    <transform>
      <position x="2" y="1" z="-2"></position>
      <rotation x="0" y="0" z="0"></rotation>
      <scale x="1" y="1" z="1"></scale>
    </transform>
  </model>
  <model name="blackSmith2" mesh="black_smith/">
    <transform>
      <position x="-2" y="1" z="-2.5"></position>
      <rotation x="0" y="0" z="0"></rotation>
      <scale x="1" y="1" z="1"></scale>
    </transform>
  </model>

我将模型视图矩阵从我的顶点着色器传递到我的片段着色器,然后进行逆转置以将光位置的世界空间坐标转换为模型空间。我想就是这样,但显然我要么遗漏了什么,要么我做错了什么。

这是我的着色器(由标签分隔)。对于这个例子,它只是一个点光源,所以 u_isPointLight = trueu_isSpotLight = false

#type vertex
#version 330 core

layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoord;
layout(location = 3) in mat4 aInstanceModelMatrix;

out vec3 outNormal;
out vec2 outTexCoord;
out vec3 outPos;
out mat4 outMat;

uniform mat4 u_Projection;
uniform mat4 u_View;

void main() {
    vec4 worldPosition = aInstanceModelMatrix * vec4(a_Position, 1.0);
    gl_Position = u_Projection * u_View * worldPosition;
    outPos = vec3(aInstanceModelMatrix * vec4(a_Position, 1.0));
    outNormal = aNormal;
    outTexCoord = aTexCoord;
    outMat = u_View * aInstanceModelMatrix;
}

#type fragment
#version 330 core

out vec4 color;

in vec3 outNormal; in vec2 outTexCoord; in vec3 outPos;
in mat4 outMat;

uniform bool u_DiffuseTextureValid;
uniform sampler2D u_DiffuseTexture;
uniform vec3 u_DiffuseColour;

uniform vec3 u_LightPosition;
uniform vec3 u_LightAttenuation;
uniform vec4 u_LightParams;
uniform vec3 u_SpotDirection;
uniform bool u_isSpotLight;
uniform bool u_isPointLight;
uniform float u_SpotInnerAngle;
uniform float u_SpotOuterAngle;

uniform vec4 uAmbientLight;

uniform bool u_AmbientTextureValid;
uniform sampler2D u_AmbientTexture;

uniform bool u_SpecularTextureValid;
uniform sampler2D u_SpecularTexture;
uniform vec4 u_Specular;

uniform sampler2D u_BumpTexture;
uniform bool u_BumpTextureValid;

uniform float u_MaterialAlpha;

void main() {
    const float kGamma = 0.4545454;
    const float kInverseGamma = 2.2;

    // WorldSpace to ModelSpace conversion
    vec3 lightPosition = (transpose(inverse(outMat)) * vec4(u_LightPosition, 1.0)).xyz;

    vec3 norm;
    if (u_BumpTextureValid) {
        norm = normalize(texture(u_BumpTexture, outTexCoord).xyz);
    } else {
        norm = normalize(outNormal);
    }

    vec3 lightDir;
    if (u_isPointLight || u_isSpotLight) {
        lightDir = normalize(lightPosition - outPos);
    } else {
        lightDir = normalize(-u_SpotDirection);
    }

    vec3 viewFragmentDirection = normalize(outPos);
    vec3 viewNormal = normalize(outNormal);

    float lightDistance = length(lightPosition - outPos);
    float attenuation = 1.0;
    if (u_isPointLight) {
        attenuation = 1.0 / (u_LightAttenuation.x + (lightDistance * u_LightAttenuation.y) + (lightDistance * lightDistance * u_LightAttenuation.z));
    }

    vec3 diffuse = u_LightParams.w * u_DiffuseColour * max(dot(norm, lightDir), 0.0) * u_LightParams.xyz;
    vec3 reflectedLightDirection = reflect(-lightDir, viewNormal);

    vec3 specular;
    if (u_SpecularTextureValid) {
        float spec = pow(max(dot(viewNormal, reflectedLightDirection), 0.0), u_Specular.w);
        specular = u_LightParams.xyz * spec * vec3(texture(u_SpecularTexture, outTexCoord));
    } else {
        float specularStrength = max(0.0, dot(viewFragmentDirection, reflectedLightDirection));
        specular = u_LightParams.w * u_Specular.rgb * pow(u_LightParams.w, u_Specular.w);
    }

    float spotFade = 1.0;
    if (u_isSpotLight && u_isPointLight) {
        float spotlightTheta = dot(lightDir, normalize(-u_SpotDirection));
        spotFade = (u_SpotInnerAngle - spotlightTheta) / (u_SpotOuterAngle - u_SpotInnerAngle);
        if (spotlightTheta < u_SpotInnerAngle) {
            specular = vec3(0, 0, 0);
            diffuse = vec3(0, 0, 0);
        }
    }

    vec4 objectColor;
    if (u_DiffuseTextureValid) {
        objectColor = texture(u_DiffuseTexture, outTexCoord);
    } else {
        objectColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
    objectColor.rgb = pow(objectColor.rgb, vec3(kInverseGamma));

    color.a = objectColor.a * u_MaterialAlpha;
    vec3 ambient = uAmbientLight.a * uAmbientLight.rgb;

    ambient *= attenuation;
    diffuse *= attenuation * spotFade;
    specular *= attenuation * spotFade;

    if (u_DiffuseTextureValid) {
        color.rgb = pow(objectColor.rgb + ambient + diffuse + specular, vec3(kGamma));
    } else {
        color.rgb = pow(objectColor.rgb * (ambient + diffuse) + specular, vec3(kGamma));
    }
}

我的问题是:

  1. 我是否正确地将灯光位置从世界空间转换到模型空间?
  2. 如果我正在做一个纯粹的directional light(即 u_isSpotLight 和 u_isPointLight 都是错误的),那么通过相同的方法将 u_SpotDirection 转换为模型空间应该可以工作......对吗?
  3. 假设 1 是正确的,谁能告诉我为什么灯光似乎出现了奇怪的旋转?

【问题讨论】:

    标签: opengl glsl


    【解决方案1】:

    模型矩阵从模型空间转换到世界空间。因此,您在世界空间中进行光照模型的计算。

    outPos = vec3(aInstanceModelMatrix * vec4(a_Position, 1.0));
    

    但是,您还需要将法线向量从模型空间转换到世界空间:
    (见Why transforming normals with the transpose of the inverse of the modelview matrix?
    In which cases is the inverse matrix equal to the transpose?)

    outNormal = transpose(inverse(mat3(aInstanceModelMatrix))) * aNormal;
    

    outMat 可以完全省略。

    viewFragmentDirection 向量是从片段到相机的向量。在 Viewscece 中,这很容易计算,因为相机位置是 (0, 0, 0)。但在世界空间中,您需要计算从片段到相机位置的向量:

    vec3 viewFragmentDirection = normalize(outPos);

    vec3 viewFragmentDirection = normalize(cameraPos - outPos);
    

    您可以使用制服作为相机的位置。相机位置也是逆视图矩阵的平移:

    vec3 cameraPos = vec3(inverse(u_View)[3]);
    

    【讨论】:

    • 哇,太棒了。这非常有效。我永远不会想到错误的是法线,而不是垂直位置。 A+
    猜你喜欢
    • 2018-02-04
    • 1970-01-01
    • 2016-01-07
    • 1970-01-01
    • 1970-01-01
    • 2018-10-03
    • 2012-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多