2013-01-07 43 views
5

我有一個場景,包含幾個具有各自位置和旋轉的模型。給定法線,着色器將簡單的雙向照明應用於每個像素。在着色器中旋轉法線

這是我的頂點着色器。

#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;,但這適用於法線旋轉和變形。

那麼在我將它們發送到片段着色器進行照明之前,如何旋轉頂點着色器中的法線?什麼是常用方法?

回答

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

+0

謝謝。你會建議我發送一個單獨的正常矩陣(僅包括模型旋轉但不縮放或轉換)到着色器嗎?我在某處看到了這種做法,並想知道是否比着色器中的模型矩陣重新提取旋轉更好。 – danijar

+0

對不起,我的時間在編輯以前的評論時用完了......我認爲縮放是必要的,因爲它可以改變曲面到光源的距離。而且,由於您需要比例尺,因此需要使用上方3 x 3矩陣的逆轉置來校正比例。我確實將統一矩陣作爲統一發送給着色器,以便它只計算一次而不是每個頂點一次。儘管你可能需要一個數學庫來做這件事,因爲你的平臺可能不提供執行這些矩陣計算的函數。 – bwroga

+0

這是有道理的。順便說一下,我使用GLM。 – danijar

0

下面的解決方案適用於我的模型,但我對其背後的數學知之甚少。這是我的源:Adding Depth and Realism - Surface Normals

變換需要應用到正常的載體是通過記: N」 = N *(M -1牛逼

基本上,這意味着你將你的法線(N)乘以ModelView矩陣(M)的逆轉置。如果您的M矩陣是4x4,則應該使用得到的3x3左上象限(M -1T作爲正常乘法。

再一次,這對我很有效,但我無法很好地解釋數學。

+0

謝謝,我明白了。 – danijar