在OpenGL Shader中如何获取或计算法线实现卡通着色效果
Toon Shader法线获取与修改方案
你使用glNormal3f设置的顶点法线可以直接通过OpenGL兼容模式的内置变量在着色器中获取,只需要修改顶点着色器完成法线的空间转换和插值传递,修改片元着色器接收插值后的法线即可,完整修改后的代码如下:
1. 顶点着色器修改
#shader vertex #version 330 compatibility // 输出到片元着色器的视图空间法线 out vec3 viewNormal; void main() { // 将模型空间的法线通过内置法线矩阵转换到视图空间,保证旋转缩放后法线正确性 viewNormal = normalize(gl_NormalMatrix * gl_Normal); // 顶点坐标MVP变换,这是之前漏掉的必要步骤 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; }
说明:
- 版本声明换为
compatibility兼容模式,才能访问固定管线内置的gl_Normal(法线输入)、gl_NormalMatrix(法线变换矩阵)、gl_Vertex(顶点坐标输入)等变量 - 法线需要转换到和光照计算相同的视图空间,避免模型变换导致计算错误
2. 片元着色器修改
#shader fragment #version 330 compatibility // 接收顶点着色器传过来的插值后法线 in vec3 viewNormal; void main() { // 视图空间的光源位置,原写法是正确的 vec3 viewLightPos = gl_LightSource[0].position.xyz; // 单位化法线和光源方向,保证点乘结果在[-1,1]区间 vec3 normal = normalize(viewNormal); vec3 lightDir = normalize(viewLightPos); // 计算漫反射光照强度,clamp避免负数 float intensity = clamp(dot(normal, lightDir), 0.0, 1.0); // 色阶计算 vec4 rampColor; if (intensity > 0.95) rampColor = vec4(1.0); else if (intensity > 0.75) rampColor = vec4(0.8); else if (intensity > 0.50) rampColor = vec4(0.6); else if (intensity > 0.25) rampColor = vec4(0.4); else rampColor = vec4(0.2); // 最终颜色 = 材质漫反射颜色 * 色阶,原材质三属性相加逻辑会偏亮,可根据需求调整 gl_FragColor = gl_FrontMaterial.diffuse * rampColor; }
说明:
- 所有参与点乘计算的向量都要做归一化处理,避免光照强度计算错误
- 法线经过图元光栅化的插值后,在片元着色器需要再做一次归一化保证精度
- 原有C++代码不需要做任何修改,
glNormal3f设置的法线会自动填充到着色器的gl_Normal内置输入变量中
内容的提问来源于stack exchange,提问作者mbattaloglu
相关产品推荐
相关产品推荐

