在上一节中,我们成功编写了第一个顶点着色器,学会了如何处理顶点位置变换。现在,让我们进入渲染管线的下一个关键阶段------片元着色器。如果说顶点着色器决定了"在哪里画",那么片元着色器就决定了"画什么颜色"。
片元着色器(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集成:在应用中加载和使用自定义着色器
- 渲染管线完整流程:从顶点到像素的完整渲染过程