Shader开发(十)编写第一个片元着色器

在上一节中,我们成功编写了第一个顶点着色器,学会了如何处理顶点位置变换。现在,让我们进入渲染管线的下一个关键阶段------片元着色器。如果说顶点着色器决定了"在哪里画",那么片元着色器就决定了"画什么颜色"。

片元着色器(Fragment Shader)是图形渲染管线中的关键阶段,负责确定每个像素(或称为"片元")的最终颜色。本文将引导你编写一个简单的片元着色器,在 openFrameworks 中将三角形网格渲染为红色,帮助你快速掌握 GLSL 的 out 关键字和片元着色器的基本原理。


片元着色器概念

什么是片元?

在深入代码之前,让我们先理解一个重要概念:片元(Fragment)

  • 片元是计算机图形学术语,指在屏幕上填充1个像素所需的所有信息

  • 包含位置、颜色、深度、纹理坐标等数据

  • 最终转换为屏幕上可见的像素

💡 简单理解:可以把片元想象成"准备好的像素",它包含了渲染一个像素所需的全部信息。

片元着色器的工作原理

片元着色器具有以下特点:

  • 单一职责:确定每个片元应该显示什么颜色

  • 逐片元处理:每次只处理一个片元,独立计算

  • 颜色控制:完全控制像素的最终颜色输出

  • 并行执行:GPU同时处理成千上万个片元

处理流程:

光栅化 → 片元生成 → 片元着色器处理 → 像素输出 → 屏幕显示


创建第一个片元着色器

按照约定,片元着色器使用 .frag 扩展名。在项目的 bin/data 目录中创建名为 first_fragment.frag 的新文件,并添加以下代码:

cpp 复制代码
#version 410                           // ① GLSL版本声明

out vec4 outColor;                     // ② 声明输出颜色变量

void main(){
    outColor = vec4(1.0, 0.0, 0.0, 1.0);  // ③ 输出红色
}

代码解析

① GLSL版本声明

  • 与顶点着色器保持一致,使用OpenGL 4.1对应的GLSL 410版本

② 输出变量声明

  • out vec4 outColor:声明一个4分量向量作为颜色输出

  • 变量名 outColor 可以自定义,但要与应用程序代码匹配

③ 颜色赋值

  • vec4(1.0, 0.0, 0.0, 1.0):RGBA格式的红色

    • R (红色): 1.0 (最大值)

    • G (绿色): 0.0 (最小值)

    • B (蓝色): 0.0 (最小值)

    • A (透明度): 1.0 (完全不透明)


GLSL的out关键字

与顶点着色器中的 in 关键字用于接收输入数据不同,out 关键字用于定义片元着色器的输出数据。这些数据将被传递到渲染管线的后续阶段(例如,帧缓冲区,用于最终显示)。

在上述代码中,outColor 是一个 vec4 变量,存储片元的 RGBA 颜色值。片元着色器需要显式声明输出变量,因为现代 OpenGL(包括版本 4.1)不再提供内置的颜色输出变量。这种设计赋予开发者更大的灵活性,例如输出多种颜色或非颜色数据。


片元着色器与顶点着色器的区别

  • 顶点着色器 :处理顶点位置和属性,输出到内置变量 gl_Position

  • 片元着色器 :处理光栅化后的像素颜色,需自定义输出变量(如 outColor)。

  • 灵活性:片元着色器在现代图形编程中用途广泛,可用于复杂效果(如光照、纹理映射)或非颜色输出(如深度值)。


颜色值

RGBA颜色模型

GLSL中的颜色使用RGBA模型,每个分量的取值范围是 [0.0, 1.0]:

复制代码
// 基本颜色示例
vec4 red    = vec4(1.0, 0.0, 0.0, 1.0);  // 纯红色
vec4 green  = vec4(0.0, 1.0, 0.0, 1.0);  // 纯绿色
vec4 blue   = vec4(0.0, 0.0, 1.0, 1.0);  // 纯蓝色
vec4 white  = vec4(1.0, 1.0, 1.0, 1.0);  // 白色
vec4 black  = vec4(0.0, 0.0, 0.0, 1.0);  // 黑色

常用颜色参考

颜色 RGBA值 说明
红色 (1.0, 0.0, 0.0, 1.0) 纯红色,完全不透明
橙色 (1.0, 0.5, 0.0, 1.0) 红色+一半绿色
黄色 (1.0, 1.0, 0.0, 1.0) 红色+绿色
紫色 (1.0, 0.0, 1.0, 1.0) 红色+蓝色
半透明红 (1.0, 0.0, 0.0, 0.5) 50%透明度的红色

扩展

尝试不同颜色

修改代码中的颜色值,体验不同的视觉效果:

cpp 复制代码
// 实验1:蓝色
outColor = vec4(0.0, 0.0, 1.0, 1.0);

// 实验2:紫色
outColor = vec4(0.8, 0.2, 1.0, 1.0);

// 实验3:半透明绿色
outColor = vec4(0.0, 1.0, 0.0, 0.5);

动态颜色生成

虽然目前我们使用固定颜色,但片元着色器的强大之处在于可以动态计算颜色:

cpp 复制代码
// 未来可能的代码示例(需要额外的输入)
void main(){
    // 基于位置生成渐变色
    float factor = gl_FragCoord.x / 1024.0;
    outColor = vec4(factor, 0.0, 1.0 - factor, 1.0);
}

下一步

现在我们已经分别创建了顶点着色器和片元着色器,但它们还没有组合在一起工作。在下一节中,我们将学习:

  • 着色器程序组合:将顶点和片元着色器链接成完整程序
  • openFrameworks集成:在应用中加载和使用自定义着色器
  • 渲染管线完整流程:从顶点到像素的完整渲染过程