Opengl着色器问题 - 奇怪的光反射伪影

5

我已经在苦苦挣扎了好几天。 我认为问题最终可以归结为每个顶点切线的问题,但我不确定该如何修复。

背景是iPhone应用,使用自己的引擎opengl es2。我的着色器是一种凹凸贴图(法线贴图),使用提供的每个顶点切线创建TBN矩阵。顶点着色器将光线向量和眼睛向量转换为切线空间,将它们传递到片段着色器并计算灯光。但是我的前两个测试模型中的一些几何体现出奇怪的照明伪影。在高光部分最容易看到这一点。

在尝试调试时,我用一个平面法线PNG替换了法线贴图。所有像素都是128,128,255。我还硬编码颜色。

我的第一个模型是一个按钮形状。它显示为外环上有齿状的镜面反射。值得注意的是,这里的UV映射方法是'flat',以使垂直于映射的侧面基本上沿纹理横跨。我认为这将使得难以计算该几何体的切线,因为其中两个点将具有完全相同的纹理坐标。

我尝试了调试渲染,将gl_FragColor设置为不同的变量。当将它设置为每个顶点法线时,我们看到齿状消失了,这表明法线是正确的。但当将其设置为每个顶点切线时,你会看到齿状存在。

enter image description here

下一个形状是一个简单的球体。在模型的顶部和底部就是伪影出现的地方。在线框图中,您会看到这是几个三角形在一个顶点汇合的地方。我无法理解这对于切线意味着什么,因为每个三角形在那里都有完全不同的UV映射。

我想如果不展示着色器代码的话,可能会受到很多批评...

顶点着色器:

v_fragmentTexCoord0 = a_vertexTexCoord0;

gl_Position = u_modelViewProjectionMatrix * vec4(a_vertexPosition,1.0);

vec3 normal = normalize(u_normalMatrix * a_vertexNormal);
vec3 tangent = normalize(u_normalMatrix * a_vertexTangent);
vec3 bitangent = cross(normal, tangent);
mat3 TBNMatrix = mat3(tangent, bitangent, normal);

v_eyeVec =  -a_vertexPosition.xyz;
v_eyeVec *= TBNMatrix;

v_lightVec = u_lightPosition - a_vertexPosition.xyz;
v_lightVec *= TBNMatrix;

v_normal = a_vertexTangent;

片元着色器:

vec3 norm = texture2D(u_normalSampler, v_fragmentTexCoord0).rgb * 2.0 - 1.0;
vec4 baseColor = vec4(0.6015625,0.0,0.0,1.0); // is normally texture2D(u_textureSampler,v_fragmentTexCoord0);

float dist = length(v_lightVec);
vec3 lightVector = normalize(v_lightVec);
float nxDir = max(0.0, dot(norm, lightVector));
vec4 diffuse = u_lightColorDiffuse * nxDir;
float specularPower = 0.0;
if(nxDir != 0.0)
{
    vec3 cameraVector = v_eyeVec;
    vec3 halfVector = normalize(v_lightVec + cameraVector);
    float nxHalf = max(0.0,dot(norm, halfVector));
    specularPower = pow(nxHalf, u_shininess);
}
vec4 specular = u_lightColorSpecular * specularPower;

gl_FragColor = (diffuse * vec4(baseColor.rgb,1.0)) + specular;

在试图理解着色器并查看所有可找到的样例和在线教程时,我困惑于为什么凹凸贴图着色器不会只扭曲由3D模型提供的法线贴图。如果没有某种参考点,你怎么知道如何修改模型的法线呢?通过什么方向?我想这就是TBN矩阵的作用。但在我的测试中,法线贴图中的法向量是0,0,1 - 直立向上的。所以似乎不应该对其进行任何修改。任何数乘以1仍然是1。但是,在数学或TBN矩阵中肯定存在某些问题,导致它无法生成与3D模型中的法线相同的法线。然后我想到了一个想法...在我的顶点着色器中,我还首先将顶点法线乘以normalMatrix,以将其放入模型空间中。但是,那些调试渲染是在变换之前的顶点法线。

是否有其他方法可以扭曲模型的法线而不使用TBN矩阵?使用Phong着色器进行渲染而不使用法线贴图不会显示出这种现象。

更新:我几乎可以确定问题是导入应用程序创建的预计算切线。尝试使用具有不同UV映射的不同模型时,我发现类似的问题,有时是暗淡而不是亮点。因此,除非我可以应用没有TBM矩阵或转换为切线空间的法线贴图,否则我将需要找到另一个导入器。

更新#2:我刚刚发现了这个听起来可能是真正问题线索的其他问题。3D图形、单位向量和正交矩阵

重要的是要注意,这些奇怪的光线只在顶点之间而不是在顶点上发生。即使在我们看到的球体上也有一个环,它位于顶部顶点和直接下面的顶点之间。我开始相信这是一个插值问题。只取上面的其中一个三角形:

enter image description here

先忽略错误的副切线,因为我正在重新计算它。但是切线具有相反的极性。因此,在两种状态之间进行插值时,您将得到指向各处的向量。

新问题是如何解决它?修复切线?修复着色器来处理它?


对于一个着色器来说,这没什么了不起的,特别是与像“pow”函数之类的东西相比。但正如你所说,这可能不是问题的根源。 - Brett Hale
@BrettHale 我本可以将vec3 norm硬编码作为调试的一种形式,但是提供的法线采样器已经向着着色器提供了0,0,1。 - badweasel
@Jessy 这个物体的UV映射看起来很棒,即使有奇怪的额外高光反射。足够好,以至于我一开始甚至没有注意到问题。但这不是正确的方式,在大多数情况下会引起问题。 - badweasel
我们可以在这里讨论,没问题。虽然我不确定谁会从这次讨论中受益。或者我可以将所有内容迁移到我很少使用的博客文章中。对我来说也可以。另外,请注意此相关问题:https://dev59.com/2W_Xa4cB1Zd3GeqP37O5 - badweasel
我不确定是否应该在这里回答自己的问题。我还没有解决照明问题,但我非常确定这只是由我的obj导入器提供的错误切线。这个周末我打算在导入器中添加代码来重新计算切线。关于eyeVec...是的,我有一个版本。对我来说,光在没有它的情况下对相机移动的响应更好。这并没有太大的区别,老实说我的相机也不会移动。所以从这个应用程序的循环节省来看,没有它也没问题。我知道这可能不适用于所有应用程序。 - badweasel
显示剩余13条评论
1个回答

1
有时候最简单的答案就是正确的答案。这些正切线很糟糕,它们与法向量不垂直。我手动重新计算了所有正切线和副切线,使用了此处描述的方法:

http://www.terathon.com/code/tangent.html

那个问题就这样消失了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接