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

时间:2014-01-14 21:44:42

标签: opengl-es three.js glsl webgl shader

短版本:我正在操纵顶点着色器中顶点的位置,但是当我根据顶点position计算法线时,法线会根据原始值计算顶点位置。顶点着色器不应该知道新顶点的位置吗?

长版本:我正在基于three.js normalmap着色器在three.js R.58中编写自定义着色器。我将平面纹理(0.5,0.5,1.0淡紫色)作为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。然而,当我将一个平面变形为一个球体时,阴影不能按预期工作 - 片段着色器似乎认为球体仍然是一个平面 - 或者其他东西。

这是带有pointLight的球体,显示了扭曲的镜面高光:

enter image description here

演示: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。当平面变形时,法线似乎不会更新以反映面部的新方向,并且镜面反射不会正确显示。

enter image description here

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

1 个答案:

答案 0 :(得分:5)

简短版本:你不能!

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

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

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

来源: