在着色器中旋转法线

时间:2013-01-07 12:45:58

标签: glsl shader lighting vertex-shader normals

我有一个场景,有几个模型,有个别位置和旋转。给定法线,着色器将简单的双向光照应用于每个像素。

这是我的顶点着色器。

#version 150

in vec3 position;
in vec3 normal;
in vec2 texcoord;

out vec3 f_normal;
out vec2 f_texcoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;

void main()
{
    mat4 mvp = proj * view * model;

    f_normal   = normal;
    f_texcoord = texcoord;

    gl_Position = mvp * vec4(position, 1.0);
}

这是片段着色器。

#version 150

in vec3 f_normal;
in vec2 f_texcoord;

uniform sampler2D tex;

vec3 sun = vec3(0.5, 1.0, 1.5);

void main()
{
    vec3 light = max(0.0, dot(normalize(f_normal), normalize(sun)));

    gl_FragColor = texture(tex, f_texcoord) * vec4(light, 1.0);
}

对于没有旋转的对象,这可以正常工作。但对于旋转模型,灯光也会旋转,当然,情况并非如此。

原因是法线不会旋转。我已经尝试了f_normal = model * normal;,但这适用于法线的旋转和转换。

那么在将它们发送到片段着色器进行照明之前,如何在顶点着色器中旋转法线呢?什么是常用方法?

2 个答案:

答案 0 :(得分:4)

您需要通过模型 - 视图 - 投影矩阵的上3行/列来变换法线。 (如果您正在执行任何缩放,则需要使用此矩阵的逆转置。See this article)。

mat3 normalMatrix = mat3(mvp);
normalMatrix = inverse(normalMatrix);
normalMatrix = transpose(normalMatrix);
f_normal = normalize(normal * normalMatrix);
// You should also send your tranformed position to the fragment shader
f_position = vec3(mvp * vec4(position, 1.0));

在片段着色器中,您需要计算光源到片段的距离并进行标准化。找到法线和光矢量的点积,并将其乘以浅色。

vec3 light = normalize(sun - f_position);
light = max(dot(f_normal, light), 0.0) * vec3(1.0, 1.0, 1.0);
gl_FragColor = texture(tex, f_texcoord) * vec4(light, 1.0);

我的代码肯定有优化空间。

我推荐这本书OpenGL 4.0 Shading Language Cookbook

答案 1 :(得分:0)

以下解决方案适用于我的模型,但我对其背后的数学没有非常深刻的理解。这是我的来源:Adding Depth and Realism - Surface Normals

您需要应用于法线向量的变换表示为: N'= N *(M -1 T

基本上,这意味着您将法线(N)乘以ModelView矩阵(M)的反转置。如果你的M矩阵是4x4,你应该使用得到的3x3左上象限(M -1 T 进行正常乘法。

同样,这对我有用,但我不能很好地解释数学。