法线贴图根据方向而不同

Sog*_*omn 7 opengl shader glsl light game-engine

我试图使用法线贴图而不是顶点法线,我无法弄清楚我做错了哪一步.我想将光线方向转换为切线空间并从那里进行计算.首先,我将矢量法线和切线转换为视图空间.

vec3 norm_viewsp = mat3(view) * normalize(norm);
vec3 tangent_viewsp = mat3(view) * normalize(tangent);
vec3 bitangent_viewsp = cross(tangent_viewsp, norm_viewsp);
Run Code Online (Sandbox Code Playgroud)

视图空间中的切线

接下来,我构建矩阵,该矩阵应执行从视图空间到切线空间的变换,并使用它来计算切线空间中光线的距离.

mat3 tbn = transpose(mat3(tangent_viewsp, bitangent_viewsp, norm_viewsp));
vec3 light_dir = light_pos - pos.xyz;
vec3 light_dir_viewsp = mat3(view) * light_dir;

v_light_dir_tansp = tbn * light_dir_viewsp;
Run Code Online (Sandbox Code Playgroud)

切线空间中的光线方向

在片段着色器中,我然后从法线贴图中采样法线并将其与光线方向相乘.

vec3 norm = texture(normal_map, v_tex_pos).rgb * 2.0 - 1.0;
float diffuse = dot(normalize(norm), normalize(v_light_dir_tansp));
Run Code Online (Sandbox Code Playgroud)

法线贴图肯定正确加载.

法线贴图

结果左上角太暗,左下角太亮.
我认为问题出在光线方向的某处,但我无法发现错误.

结果

Nore,在上面的图片中,值介于-1和1之间,这就是为什么例如法线贴图看起来太暗.

完整的着色器代码

编辑:灯光位于模型的中心.如果使用顶点法线,则照明工作正常.在视图空间而不是切线空间中进行计算没有任何区别.

Rab*_*d76 5

首先,您必须计算出切线空间矩阵,这在右手系统中是这样完成的:

vec3 norm_viewsp      = mat3(view) * normalize(norm);
vec3 tangent_viewsp   = mat3(view) * normalize(tangent);
vec3 bitangent_viewsp = cross(norm_viewsp, tangent_viewsp);
mat3 tbn              = mat3(tangent_viewsp, bitangent_viewsp, norm_viewsp));
Run Code Online (Sandbox Code Playgroud)

当然,您需要切线空间中的法线贴图和视图空间中的光源的法线向量:

vec3 norm_map_tbn     = normalize(texture(normal_map, v_tex_pos).xyz * 2.0 - 1.0);
vec3 light_dir_viewsp = normalize(mat3(view) * (light_pos - pos.xyz));
Run Code Online (Sandbox Code Playgroud)

之后,您有2种可能性。由于来自法线贴图的法线向量在切线空间中,因此光向量也可以转换为切线空间,并且光计算也可以在切线空间中进行。为此,必须通过反正切空间矩阵来转换光矢量。逆矩阵可以通过GLSL函数来计算,inverse而不能通过GLSL函数来计算transpose(请参见“矩阵逆”和“矩阵转置”之间的区别是什么?)。

vec3 light_dir_tbn    = inverse(tbn) * light_dir_viewsp;
float diffuse         = max(0.0, dot(norm_map_tbn, light_dir_tbn);
Run Code Online (Sandbox Code Playgroud)

但是您也可以采用其他方法。来自法线贴图的法线向量可以转换到视图空间,并且可以在视图空间中进行光照计算。为此,必须通过切线空间矩阵来转换法线向量。这将避免昂贵的inverse操作。将法线向量替换为tbn[2](切线空间矩阵的Z轴),以可视化效果,而不受法线贴图的影响。

vec3 norm_map_viewsp  = tbn * norm_map_tbn;
float diffuse         = max(0.0, dot(norm_map_viewsp, light_dir_viewsp);
Run Code Online (Sandbox Code Playgroud)


请注意,通常按以下方式计算朗伯散射光模型:

f_lambertian = max( 0.0, dot(N, L ))
Run Code Online (Sandbox Code Playgroud)


如果光源离物体很近,则光矢量和人脸的法线矢量之间的角度可能在较大的左上边缘和右下边缘之间不同。这将导致在更暗和更亮的区域,因为朗伯散射光模型线性依赖于泰西角的余弦。
将法线向量替换为tbn[2](切线空间矩阵的Z轴),以可视化效果,而不受法线贴图的影响。


注意,必须确保所有法线向量指向相同的方向,切线指向相同的方向。参见示例,该示例演示了如果将法向向量之一反转,会发生什么:

vec3 norm_viewsp      = mat3(view) * normalize(norm);
vec3 tangent_viewsp   = mat3(view) * normalize(tangent);
vec3 bitangent_viewsp = cross(norm_viewsp, tangent_viewsp);
mat3 tbn              = mat3(tangent_viewsp, bitangent_viewsp, norm_viewsp));
Run Code Online (Sandbox Code Playgroud)
vec3 norm_map_tbn     = normalize(texture(normal_map, v_tex_pos).xyz * 2.0 - 1.0);
vec3 light_dir_viewsp = normalize(mat3(view) * (light_pos - pos.xyz));
Run Code Online (Sandbox Code Playgroud)