光照模型:Gouraud模型

Gouraud Shading(高洛德着色)= "逐顶点算光照,逐像素做插值上色"。它不是一个新的光照模型(不改变 Phong/Blinn-Phong 的公式),这两种光照模型的不同就是在不同的阶段计算光照。

  • 逐片元(Phong shading 思路):在片元着色器对每个像素算 ambient + diffuse + specular,细节最丰富。
  • Gouraud:在顶点着色器只对网格的每个顶点算一次光照,得到每个顶点的颜色 C0,C1,C2...;光栅化时对三角形内部的像素颜色做线性插值,片元着色器直接输出插值后的颜色。

Gouraud 的"贵"变"便宜"------把很多次的片元光照计算,变成少量的顶点光照计算。

核心流程 : 以一个三角形为例,顶点为 A,B,C

  1. 顶点阶段(Vertex Shader)
    • 把顶点位置变换到世界空间 P
    • 把法线正确变换到世界空间 N(用 normal matrix)
    • 根据光源、相机位置算光照,得到该顶点颜色 C_A
  2. 光栅化阶段(Rasterizer 自动做)
    • 对每个像素求重心坐标 (α, β, γ),满足 α+β+γ=1
    • 插值颜色:C_pixel = α*C_A + β*C_B + γ*C_C
  3. 片元阶段(Fragment Shader)
    • 不再算光照,只输出 C_pixel

优缺点

项目 Gouraud Shading(逐顶点光照) Phong Shading(逐片元光照)
光照计算位置 顶点着色器:每个顶点算一次光照颜色/强度 片元着色器:每个像素算一次光照
插值对象 插值"颜色/光照结果" 插值"法线/相关向量",再逐像素算光照
计算量/性能 低(顶点数通常远小于像素数) 高(像素多时开销显著)
高光表现(镜面反射) 容易丢失或断续:高光若落在三角形内部且顶点未命中则插值不出高光 准确、连续:高光按像素计算,不易丢失
对网格密度依赖 强:三角形越大越容易出现误差;加密网格可改善 相对弱:即使网格不密,高光与明暗变化也更细腻
明暗过渡 平滑但"受限于顶点采样";细节变化可能被抹平 平滑且细节丰富;能表现更细的光照变化
典型伪影 "Specular Missing"(高光消失)、高光漂移、大片三角形上光照显得不真实 主要是性能成本;若法线质量差会暴露法线问题但不属于插值缺陷
适用场景 移动端/低端硬件、物体很多、以漫反射为主、网格较密的模型 高质量渲染、强高光材质(金属/塑料/车漆等)、网格较粗也要细节的情况
与光照模型关系 可搭配 Lambert/Phong/Blinn-Phong;只是"在哪算" 同样可搭配 Lambert/Phong/Blinn-Phong;只是"在哪算"
一句话总结 省算力,用插值"糊"出光照 更真实,用每像素计算保细节

实现:
vertex shader

复制代码
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTex;

uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;

uniform vec3 uViewPos;
uniform vec3 uLightPos;
uniform vec3 uLightColor;
uniform vec3 uObjectColor;
uniform float uShininess;

out vec3 vColor;

void main()
{
    vec4 worldPos4 = uModel * vec4(aPos, 1.0);
    vec3 worldPos = worldPos4.xyz;

    mat3 normalMat = mat3(transpose(inverse(uModel)));
    vec3 N = normalize(normalMat * aNormal);

    vec3 L = normalize(uLightPos - worldPos);
    vec3 V = normalize(uViewPos - worldPos);
    vec3 H = normalize(L + V);

    float diff = max(dot(N, L), 0.0);

    float spec = 0.0;
    if (diff > 0.0) {
        spec = pow(max(dot(N, H), 0.0), uShininess);
    }

    vec3 ambient  = 0.1 * uLightColor;
    vec3 diffuse  = diff * uLightColor;
    vec3 specular = 0.3 * spec * uLightColor;

    vColor = (ambient + diffuse + specular) * uObjectColor;

    gl_Position = uProjection * uView * worldPos4;
}

fragment shader

cpp 复制代码
#version 330 core
in vec3 vColor;
out vec4 FragColor;

void main()
{
    FragColor = vec4(vColor, 1.0);
}


相关推荐
2401_8914821719 小时前
多平台UI框架C++开发
开发语言·c++·算法
无敌昊哥战神19 小时前
【LeetCode 257】二叉树的所有路径(回溯法/深度优先遍历)- Python/C/C++详细题解
c语言·c++·python·leetcode·深度优先
Darkwanderor20 小时前
三分算法的简单应用
c++·算法·三分法·三分算法
2401_8319207420 小时前
分布式系统安全通信
开发语言·c++·算法
2401_8772742421 小时前
从匿名管道到 Master-Slave 进程池:Linux 进程间通信深度实践
linux·服务器·c++
汉克老师21 小时前
GESP5级C++考试语法知识(八、链表(三)循环链表)
c++·约瑟夫问题·循环链表·gesp5级·gesp五级
阿贵---21 小时前
C++中的RAII技术深入
开发语言·c++·算法
PiKaMouse.1 天前
navigation2-humble从零带读笔记第一篇:nav2_core
c++·算法·机器人
lightqjx1 天前
【算法】二分算法
c++·算法·leetcode·二分算法·二分模板
Irissgwe1 天前
进程间通信
linux·服务器·网络·c++·进程间通信