GLSL - 计算表面法线

时间:2012-03-06 04:24:48

标签: glsl

我有一个简单的顶点着色器,用GLSL编写,我想知道是否有人可以帮助我计算表面的法线。我正在'升级'平面,所以当前的光模型看起来......很奇怪。这是我目前的代码:

varying vec4 oColor;
varying vec3 oEyeNormal;
varying vec4 oEyePosition;

uniform float Amplitude;     // Amplitude of sine wave
uniform float Phase;         // Phase of sine wave
uniform float Frequency;     // Frequency of sine wave

varying float sinValue;

void main()
{
    vec4 thisPos = gl_Vertex;

    thisPos.z = sin( ( thisPos.x + Phase ) * Frequency) * Amplitude;

    // Transform normal and position to eye space (for fragment shader)
    oEyeNormal    = normalize( vec3( gl_NormalMatrix * gl_Normal ) );
    oEyePosition  = gl_ModelViewMatrix * thisPos;       

    // Transform vertex to clip space for fragment shader
    gl_Position   = gl_ModelViewProjectionMatrix * thisPos;

    sinValue = thisPos.z;
}

有没有人有任何想法?

2 个答案:

答案 0 :(得分:6)

好的,让我们从微分几何角度来看这个。你有一个带参数s和t的参数曲面:

X(s,t) = ( s, t, A*sin((s+P)*F) )

所以我们首先计算这个曲面的切线,它是我们两个参数之后的偏导数:

Xs(s,t) = ( 1, 0, A*F*cos((s+P)*F) )
Xt(s,t) = ( 0, 1, 0 )

然后我们只需计算这些产品的叉积就可以得到正常值:

N = Xs x Xt = ( -A*F*cos((s+P)*F), 0, 1 )

因此,您的法线可以完全分析,您实际上并不需要gl_Normal属性:

float angle = (thisPos.x + Phase) * Frequency;
thisPos.z = sin(angle) * Amplitude;
vec3 normal = normalize(vec3(-Amplitude*Frequency*cos(angle), 0.0, 1.0));

// Transform normal and position to eye space (for fragment shader)
oEyeNormal    = normalize( gl_NormalMatrix * normal );

normal的归一化可能不是必要的(因为我们无论如何都要对变换后的法线进行归一化),但此刻我还不确定在非均匀缩放的情况下非正规化法线是否会表现正常。当然,如果你希望法线指向负z方向,你需要否定它。


那么,在太空中的表面上的路是不必要的。我们也可以用x-z平面内的正弦曲线来思考,因为法线的y部分无论如何都是零,因为只有z取决于x。因此,我们只取曲线z=A*sin((x+P)*F)的切线,其斜率是z的导数,即xz向量(1, A*F*cos((x+P)*F)),其正常就是(-A*F*cos((x+P)*F), 1)(切换坐标和否定一个),是(非标准化)正常的x和z。好吧,没有3D矢量和偏导数,但结果是一样的。

答案 1 :(得分:-1)

此外,你应该调整你的表现:

oEyeNormal = normalize(vec3(gl_NormalMatrix * gl_Normal));
  1. 由于gl_NormalMatrix是3x3矩阵,因此无需将其强制转换为vec3。
  2. 您无需在顶点着色器中对传入法线进行标准化,因为您不会在其中进行任何基于长度的计算。一些消息来源说,应用程序应始终对传入法线进行标准化,以便在顶点着色器中根本不需要它。但是,由于这不受着色器开发人员的控制,我在计算基于顶点的照明(gouraud)时仍将其标准化。