什么是着色器 (Shaders)#
着色器本质上是运行在 GPU(显卡)上的程序。在 OpenGL 绘图流程中,我们需要在 GPU 上运行代码来处理我们发送过去的顶点数据。
- 当你调用 glCompileShader(id) 时,显卡驱动(运行在 CPU 上)会获取这个字符串。显卡驱动里内置了一个编译器。它会将你的 GLSL 源代码翻译成该显卡硬件能听懂的机器码(微指令)。由于不同品牌的显卡(NVIDIA, AMD, Intel)底层指令集完全不同,所以 Shader 必须在你的电脑上“现场编译”,而不能像 C++ 那样预先编译成 .exe。编译好的二进制指令会被发送并存储在 GPU 的显存中。当你执行 glDrawArrays 时,GPU 会激活这些指令,大规模并行地处理你传进去的顶点数据。
- 我们需要显卡的能力在屏幕上绘制图形,所以需要程序完全在显卡上运行,当然部分代码需要在CPU上运行
- 着色器的本质,就是告诉GPU如何处理CPU发送给他的数据
渲染管线概述 (The Graphics Pipeline)#
解释了数据从 CPU 发送到 GPU 后的处理过程。渲染管线是一系列将 3D 坐标转换为屏幕上 2D 像素的步骤。
渲染管道:在CPU上写了一堆数据,之后向显卡发送了一些数据,然后发送了DrawCall指令(发送指令前绑定了一些状态),最后进入了着色器的阶段,GPU实际处理DrawCall指令并在屏幕上绘制一些东西
着色器#
顶点着色器和片段着色器是顺着管道的两种不同的着色器类型,发出DrawCall指令后,顶点着色器被执行,然后再经过一些阶段后 是片段着色器
顶点着色器 把 3D 空间的坐标转换成屏幕上的 2D 坐标(术语叫标准化设备坐标) ->几何着色器 增减图形:可以把一个点变成一个三角形,或者把一个三角形删掉。这里,几何着色器根据这 3 个点,计算出了第 4 个点的位置,并把它们重新组合。输出:它“发射”出了足够的顶点,组成了两个独立的三角形图元。 ->图元组装 把处理好的点“连线” ->光栅化 计算这个三角形到底盖住了屏幕上哪些像素格子 ->片段着色器 根据光照、纹理、材质,给这个像素算出一个最终的 RGBA 值 ->测试与混合 深度测试 (Depth Test):检查这个像素是不是被别的物体挡住了。模板测试:一些特殊遮罩效果。混合 (Blending):如果是半透明的,就把它和背景颜色融合
drawCall->顶点着色器->片段着色器->像素
- 顶点着色器 (Vertex Shader)
- 职责:处理每个顶点的数据。
- 核心任务:确定顶点在屏幕上的最终位置(设置
gl_Position)。 - 它会针对每个传入的顶点执行一次
比如三角形三个顶点就是三次。 - 但是缓冲区的顶点,会包括好几个属性(位置是其中之一)
- 片元着色器 (Fragment/Pixel Shader)
- 职责:决定每个像素(片元)最终显示的颜色。
- 核心任务:为光栅化后生成的每个像素填充颜色(输出颜色向量)。
- 它会针对需要绘制的每个像素执行一次。
可能有几十万次上百万次,比如1080p,就是 1920x1080 约 207万 像素。
光栅化光栅化并不直接计算颜色,它的核心任务是坐标转换和插值 (Interpolation):- 确定片元: 它把顶点着色器输出的几何图形(三角形)映射到屏幕像素网格上,确定哪些像素被图形覆盖。
- 属性插值: 关键点在这里。它会根据三角形三个顶点的属性(如颜色、法线、纹理坐标),通过重心坐标算法计算出三角形内部每个像素点对应的属性值。
- 例如: 顶点 A 是红色,顶点 B 是蓝色,光栅化会计算出中间某个像素应该是“紫红色”。
- 性能
- 片段着色器执行的次数是像素次数,顶点着色器执行的次数的顶点个数。因此如果考虑性能优化,关键操作尽量在顶点着色器中处理,把数据处理后再从顶点着色器传递到片段着色器
- 但是有些东西显然需要按像素计算,比如光源,每个像素都有一个颜色值,这个值由很多东西决定的,比如光源、环境、纹理等,一起确定了像素的正确颜色。还取决于相机在哪。片段着色器所有因素决定了单个像素的颜色
GLSL (OpenGL Shading Language)#
介绍用于编写着色器的语言 GLSL。它的语法非常接近 C 语言,专门为==向量运算和并行处理==而设计。
- 为什么需要着色器 在现代 OpenGL(Core Profile)中,没有默认的着色器。如果你不编写顶点和片元着色器,屏幕上将不会渲染任何内容。这与旧式的固定函数管线(Fixed-Function Pipeline)有本质区别。
- 数据传递:从顶点到着色器 解释了之前课程中设置的顶点缓冲区(Vertex Buffer)和布局(Layout)是如何进入顶点着色器的。
- 着色器在代码中的基本结构 演示了如何在 C++ 中==以字符串形式定义着色器源码,并初步提及了编译(Compile)和链接(Link)着色器程序==的必要性。
- 总结与下集预告 强调了理解==“顶点处理”与“颜色处理”分离==的重要性。下一集将实际编写并编译这些代码。
学习建议: 这一集偏重理论。重点在于理解顶点着色器决定“在哪里画”,而片元着色器决定“画什么颜色”。这对后面学习==矩阵变换(MVP)和光照模型==至关重要。