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);
    }
}
相关推荐
无敌最俊朗@5 天前
14.模型,纹理,着色器
unity·着色器
你疯了抱抱我14 天前
【VRChat · 改模】Unity工程导入人物模型;并添加着色器教程;
unity·游戏引擎·vr·着色器·vrchat
贵州晓智信息科技14 天前
Three.js实现动态水泡效果逐步解析GLSL着色器
开发语言·javascript·着色器
Yang-Never19 天前
Shader -> BitmapShader贴图着色器详解
android·开发语言·kotlin·android studio·贴图·着色器
Yang-Never21 天前
Shader -> SweepGradient扫描渐变着色器详解
android·java·kotlin·android studio·着色器
周振超的21 天前
使用Qt和OpenGL实现一个旋转的各面颜色不一致的立方体及知识点分析
开发语言·qt·着色器
山楂树の24 天前
Threejs 自定义片元着色器 做UV动画
3d·图形渲染·webgl·着色器·uv
吃豆腐长肉1 个月前
着色器 (三)
opengl·着色器
吃豆腐长肉1 个月前
opengl 着色器 (四)最终章收尾
opengl·着色器
浅陌sss2 个月前
UnityShaderLab 实现黑白着色器效果
着色器