3、片元着色器之基础光照模型:兰伯特和半兰伯特光照模型

1、什么是兰伯特光照模型?

兰伯特光照模型(Lambertian Lighting Model)是一种漫反射光照模型 ,基于兰伯特余弦定律。原理是:表面接收到的光强度与入射光线和表面发现之间的夹角的余弦值成正比。公式如下:
I = k d ⋅ I L ⋅ m a x ( 0 , c o s θ ) I=k_d·I_L·max(0,cosθ) I=kd⋅IL⋅max(0,cosθ)

其中:
I 表示最终的光照强度 k d 是漫反射系数,表示表面对光的反射程度 I L 是光源的强度 θ 是入射光线和表面法线之间的夹角 c o s θ 可通过法线向量和光源向量的点积计算得出 : c o s θ = N ⋅ L I表示最终的光照强度\\ k_d是漫反射系数,表示表面对光的反射程度 \\ I_L是光源的强度\\ θ是入射光线和表面法线之间的夹角\\ cosθ可通过法线向量和光源向量的点积计算得出:cosθ=N·L I表示最终的光照强度kd是漫反射系数,表示表面对光的反射程度IL是光源的强度θ是入射光线和表面法线之间的夹角cosθ可通过法线向量和光源向量的点积计算得出:cosθ=N⋅L
兰伯特模型适合粗糙、无光泽的表面,因为它不会产生高光反射效果。

用兰伯特模型模拟3D球体:

cpp 复制代码
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    // 将像素坐标标准化为 [-1, 1] 的范围
    vec2 uv = (fragCoord - iResolution.xy * 0.5) / iResolution.y;
    
    // 定义球体的中心和半径
    vec2 sphereCenter = vec2(0.0, 0.0); // 屏幕空间球体中心
    float sphereRadius = 0.5; // 球体半径(屏幕空间)

    // 计算片段到球体中心的2D距离
    float dist2D = length(uv - sphereCenter);

    // 判断是否在球体内
    if (dist2D < sphereRadius) {
        // 使用球体方程计算深度(z坐标),假设球体的 z 轴为正
        float z = sqrt(sphereRadius * sphereRadius - dist2D * dist2D);

        // 计算3D法线
        vec3 normal = normalize(vec3(uv - sphereCenter, z));

        // 定义光源方向
        vec3 lightDir = normalize(vec3(4.0, 5.0, -1.0));

        // 漫反射系数
        float kd=1.0;
        // 光源的强度
        float lightIntensity=1.0;

        // 使用Lambert光照模型计算亮度
        float intensity = kd * lightIntensity * max(dot(normal, lightDir), 0.0);

        // 设置球体颜色
        vec3 color = vec3(1.0, 0.5, 0.3) * intensity;
        fragColor = vec4(color, 1.0);
    } else {
        // 背景颜色
        fragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }
}


2、什么是半兰伯特光照模型?

它是在兰伯特模型基础上的改进,主要用于避免在光照角度较小时出现过暗的情况,从而使物体的暗面更加明亮,看起来更自然。公示如下:
I = k d ⋅ I L ⋅ ( 0.5 ⋅ c o s θ + 0.5 ) = k d ⋅ I L ⋅ ( 0.5 ⋅ ( N ⋅ L ) + 0.5 ) I=k_d·I_L·(0.5·cosθ+0.5)=k_d·I_L·(0.5·(N·L)+0.5) I=kd⋅IL⋅(0.5⋅cosθ+0.5)=kd⋅IL⋅(0.5⋅(N⋅L)+0.5)
与兰伯特模型的区别在于,将光强度的余弦值进行平移并缩放到 0 到 1 的范围内,即使夹角较大时仍能获得一定的光照。

cpp 复制代码
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    // 将像素坐标标准化为 [-1, 1] 的范围
    vec2 uv = (fragCoord - iResolution.xy * 0.5) / iResolution.y;
    
    // 定义球体的中心和半径
    vec2 sphereCenter = vec2(0.0, 0.0); // 屏幕空间球体中心
    float sphereRadius = 0.5; // 球体半径(屏幕空间)

    // 计算片段到球体中心的2D距离
    float dist2D = length(uv - sphereCenter);

    // 判断是否在球体内
    if (dist2D < sphereRadius) {
        // 使用球体方程计算深度(z坐标),假设球体的 z 轴为正
        float z = sqrt(sphereRadius * sphereRadius - dist2D * dist2D);

        // 计算3D法线
        vec3 normal = normalize(vec3(uv - sphereCenter, z));

        // 定义光源方向
        vec3 lightDir = normalize(vec3(4.0, 5.0, -1.0));

        // 漫反射系数
        float kd=1.0;
        // 光源的强度
        float lightIntensity=1.0;

        // 使用Lambert光照模型计算亮度
        float intensity = kd * lightIntensity * (0.5*dot(normal, lightDir)+0.5);

        // 设置球体颜色
        vec3 color = vec3(1.0, 0.5, 0.3) * intensity;
        fragColor = vec4(color, 1.0);
    } else {
        // 背景颜色
        fragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }
}
相关推荐
郑寿昌12 小时前
UE5与UE6在Lumen和Nanite的差异解析
游戏引擎·图形渲染·着色器
threelab1 天前
Three.js 动态旋转同心圆着色器 | 三维可视化效果
开发语言·javascript·着色器
♡すぎ♡2 天前
ShaderLab:海面——顶点变换,程序化生成无需贴图
计算机图形学·opengl·着色器
UTwelve9 天前
【UE】Gerstner Waves 水体模拟 4 :[颜色构成阶段3、4] - 实现NAP+CDOM
ue5·着色器
Yasin Chen9 天前
Unity TMP_SDF 分析(五)片元着色器
unity·游戏引擎·着色器
AIminminHu11 天前
OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(10):从“像素画师”到“硅基神明”:一个CAD开发者穿越GPU着色器管线的十年进化史)
着色器·片段着色器·顶点着色器·opengl 1.0·顶点/片段着色器
AIminminHu14 天前
OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(7):从“显卡不听话”到“GPU秒懂你”:一个CAD老兵的着色器驯服史))
着色器·编译流程·着色器语言 glsl·创建着色器对象·glcreateshader·gluseprogram·glcreateprogram
♡すぎ♡14 天前
ShaderLab:线条几何体旋转
unity·计算机图形学·着色器·shaderlab
AIminminHu16 天前
OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(3):GPU 着色器进化史:从傻瓜相机到 AI 画师,你的显卡里藏着一场战争)
人工智能·着色器
UQ_rookie20 天前
【Unity3D】在URP渲染管线下使用liltoon插件出现粉色无法渲染情况的解决方案
unity·游戏引擎·shader·urp·着色器·vrchat·liltoon