You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 19:24:04