在顶点着色器中定位顶点后如何更新法线?

Posted

技术标签:

【中文标题】在顶点着色器中定位顶点后如何更新法线?【英文标题】:How do I update normals after positioning vertices in vertex shader? 【发布时间】:2014-02-03 04:00:57 【问题描述】:

短版:我在顶点着色器中操作顶点的位置,但是当我根据顶点position计算法线时,法线是根据原始顶点位置计算的.顶点着色器不应该知道新顶点在哪里吗?

长版:我正在基于three.js normalmap 着色器在three.js R.58 中编写自定义着色器。我将平面纹理(0.5、0.5、1.0 lavender)作为tNormal 传递给着色器,然后在顶点着色器中设置planeGeometry 的顶点位置以将其弯曲成球体。

这是计算法线的顶点着色器的位:

vec3 newPosition = mix( position, goalPosition, mixAmount );

vec4 mvPosition = modelViewMatrix * vec4( newPosition, 1.0 );
vViewPosition = -mvPosition.xyz;

vNormal = normalize( normalMatrix * normal );

//tangent and binormal vectors
vTangent = normalize( normalMatrix * tangent.xyz );

vBinormal = cross( vNormal, vTangent ) * tangent.w;
vBinormal = normalize( vBinormal );

此代码适用于默认的 sphereGeometry 和 planeGeometry。然而,当我将一个平面变形为一个球体时,着色无法按预期工作——片段着色器似乎认为球体仍然是一个平面——或者其他什么。

这是一个带有点光源的球体,显示了扭曲的高光:

演示:http://meetar.github.io/planebending/plane_bending.html

只有当原始平面面向点光源时才会出现 - 在其他旋转时,无论相机在哪里,球体都是黑色的,这表明平面法线仍在以某种方式被计算,就好像顶点没有被计算一样重新定位。

我设置了geometry.dynamic = true 以及geometry.normalsNeedUpdategeometry.tangentsNeedUpdate,我正在调用geometry.computeTangents(),但似乎没有任何效果。

我的印象是,在 GLSL 中,用于计算法线的场景组件(例如 normalMatrixtangent)会考虑顶点着色器的任何顶点操作。我错过了什么?这是three.js特有的吗?

编辑:

在控制台中检查,我看到变形平面中的每个面仍然具有 (0, 0, 1) 的世界空间法线,就像它在未变形状态下所做的那样,而不是 sphereGeometry 实例,其法线因关于他们的方向。

这是另一个演示:这两个对象具有相同的材料。左边是SphereGeometry,右边是变形的PlaneGeometry。当平面变形时,法线似乎不会更新以反映面的新方向,并且高光不会正确显示。

演示:http://meetar.github.io/planebending/plane_bending_06.html

【问题讨论】:

在顶点着色器中没有自动完成;这实际上是重点。之后有些事情会自动发生(例如透视分割),但输出变化的规范化不是其中之一。在固定功能管道中,有一个自动重新规范化的功能,但这不适用于 WebGL。 也就是说,通常在顶点中,您解决此问题的方法是使用 Tangent、Bitangent 和 Normal 向量将光照向量转换为切线空间。然后所有的光照计算都在片段着色器中在与法线采样相同的坐标空间(切线空间)中完成。还有一些替代方法,例如在片段着色器中将法线转换为世界/视图空间,但这种方法更昂贵,主要用于延迟着色。 不幸的是,如果不包括与此问题相关的着色器,我无法写出实际答案。 嗯,好的。我将修改我的问题以使其更有意义。 这与您之前的问题***.com/questions/17528878/… 有何不同?另外,我建议您更新到当前版本的three.js。 没有。在这种情况下,数据是单向的——从 CPU 到 GPU。 【参考方案1】:

短版:你不能!

长版:即使顶点着色器在移动顶点,它也只知道当前顶点的更新位置——要计算新的面法线,它会知道所有相邻顶点的更新位置,到目前为止,WebGL 不允许这样做。

虽然顶点着色器确实计算法线,但它们都基于原始法线,它们与原始顶点位置一起传递给顶点着色器。这些计算大多只是片段着色器所需的标准法线相关事物,例如考虑光照位置、变换等,并且它们都假设顶点将在对象空间中相对于彼此保持静止。

使用 CPU 更新法线并将其传递给顶点着色器相对容易,但如果必须在顶点着色器中进行,则有一些偷偷摸摸的方法来伪造它,例如使用 bump maps;如果顶点是基于任何类型的参数计算移动的,你可以generate some neighbors on the fly and check the normal between them,这绝对是作弊。

来源:

https://github.com/mrdoob/three.js/issues/1478#issuecomment-4450787 http://www.lighthouse3d.com/tutorials/glsl-tutorial/the-normal-matrix/

【讨论】:

GLSL 实际上确实允许您在几何着色器中描述的内容。为此目的,有一种称为邻接的原语。但既然我们在讨论 WebGL,你应该忘记我什至提到了 GS。 关于使用函数计算顶点着色器中的法线,我不确定它是否是“作弊”。这不是问题的通用解决方案。不过有用的链接。

以上是关于在顶点着色器中定位顶点后如何更新法线?的主要内容,如果未能解决你的问题,请参考以下文章

在顶点着色器中将法线转换为视图空间

顶点着色器中的 Phong 光照

OpenGL顶点着色器的NormalMatrix?

从顶点数据中获取像素法线

着色器中的 OpenGL 法线变换

我可以使用顶点着色器来显示模型法线吗?