着色器不是用来上色的
我第一次听到“着色器”这个词,以为它是个调色工具。后来才知道,它跟“上色”的关系,大概就像“路由器”跟“路”的关系——名字里带那个字,但干的活远不止那个字。
着色器,英文 Shader,本质是一段跑在 GPU 上的小程序。你写的不是“把这个模型涂成红色”,而是“每一个顶点、每一个片元、每一个计算线程,各自要执行什么”。GPU 会把这段代码同时派发给成千上万个线程,颜色只是执行结果之一,不是它的全部。
从“写死”到“可编程”
早期的图形硬件是固定管线。光 照怎么算、纹理怎么混、雾效怎么加,都是硬件里焊死的电路。开发者能调的只有参数,不能改逻辑。那个年代想做点非主流效果,要么绕路,要么等新硬件。
后来可编程管线出现,顶点着色器和片元着色器被开放出来。你终于可以在顶点阶段自己算坐标变换,在片元阶段自己写法线光照。再往后,几何着色器、细分着色器、计算着色器陆续加入,着色器的边界从“画三角形”一路扩到了通用计算。
所以“着色器”这个名字其实有点历史包袱。它诞生于图形渲染,但今天它处理的不只是颜色,还有几何、粒子、后处理、甚至物理模拟。
顶点和片元,两个老熟人
如果你只写过 WebGL 或图形学作业,最熟的是顶点着色器和片元着色器。
顶点着色器处理每个顶点:把模型空间坐标乘 MVP 矩阵 变到裁剪空间,顺便算法线、切线、UV、颜色这些要往下传的数据。它跑的次数等于顶点数,一般不会太多。
片元着色器处理光栅化后覆盖到的每个像素候选:采样纹理、算法线光照、做雾效、混合颜色。它跑的次数等于片元数,通常远大于顶点数,所以往往是性能大头。
两者之间隔着光栅化。顶点着色器输出的 varying 不会原样送到片元,而是经过插值。你写 varying vec2 vUv,片元里拿到的 UV 是三角形三个顶点 UV 的透视校正插值。很多诡异问题就出在这里:法线插值后不再归一化、切线空间基不正交、UV 接缝处采样错位。你以为是光照公式错了,其实是插值后的数据已经不是你想象的那个数据。
不止像素:几何、细分和计算
几何着色器能拿到一个图元,决定输出零个、一个或多个图元。粒子膨胀、线框渲染、简单几何增减都能做,但性能模型特殊,移动端支持不总是理想,日常项目用得不算多。
细分着色器用来把低模细分得更平滑,或者做位移贴图。地形、曲面、毛发这类需要动态细节的场景适合它,但管线更复杂,调试成本也高。
真正把着色器从“图形”拉到“通用计算”的是计算着色器。它不参与渲染管线,不需要顶点和片元,直接对一组线程做分派。图像后处理、粒子模拟、光照剔除、卷积、小型物理计算都能往里塞。写计算着色器时,你会更直接地感受到 GPU 的脾气:线程按 warp/wave 分组执行,分支发散要付代价,共享内存 和屏障用不好就是性能灾难。
语言很多,本质相通
OpenGL 系用 GLSL,DirectX 系用 HLSL,Vulkan 中间层是 SPIR-V,WebGPU 推 WGSL。语法有差异,但核心概念高度一致:输入输出、统一变量、纹理采样、内置函数、精度限定、线程模型。学会一门再迁移,主要是查表补差异,不是从零开始。
真正难的不是语法,是建立 GPU 的执行模型。CPU 代码可以按顺序想,着色器不行。你要想的是:这个片元着色器会被几百万个片元同时执行;每个线程有自己的寄存器,但纹理单元和内存带宽是共享的;一个 if 如果让同一组线程走不同分支,两组都要跑;一次纹理采样可能有几百个周期延迟,靠线程切换来掩盖。很多“玄学优化”,本质都是在照顾这个模型。
几个我踩过的坑
第一,别迷信分支。片元里写 if (alpha < 0.5) discard; 很常见,但 discard 会破坏早期深度 测试,也可能让 GPU 无法做某些合并优化,移动端尤其敏感。
第二,精度不是小事。highp、mediump、lowp 在桌面端可能看不出差别,在移动端直接影响带宽、功耗和正确性。把世界坐标塞进 mediump,远处就会出现肉眼可见的抖动。
第三,overdraw 比你想的贵。半透明、粒子、UI 一层层叠上去,片元着色器会被反复执行。能靠深度测试提前扔掉的,就别留到最后。
第四,纹理采样有缓存和布局问题。随机采样、跨 mip 采样、各向异性过滤都会让成本上升。只读数据塞 uniform buffer 还是纹理,也要看访问模式。
学着色器的一条务实路线
先别急着写 PBR。先画一个三角形,理解顶点到片元的坐标变换;再写 UV 纹理采样,理解插值和纹理坐标;再写最简单的 Lambert 光照,理解法线变换和归一化;接着做后处理,理解全屏三角形、帧缓冲和卷积;最后再碰计算着色器和更复杂的材质模型。
着色器好玩的地方在于,它把数学、硬件和视觉结果直接绑在一起。你改一个符号,屏幕上就会给出反馈。它也烦人的地方在于,同一个错误可能来自矩阵、插值、精度、驱动或者管线状态,而 GPU 不会给你一句像样的报错。
所以下次看到“着色器”三个字,别只想着上色。它更像是一段写给几千个并行线程的短诗,只不过这首诗的韵脚,落在帧缓冲里。
版权声明:本文为CSDN博主「迪浦西克」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45787934/article/details/169951889