中EN
概念页

像素着色器

Pixel Shader

更新于 2026-10-15 约 6 分钟GPU着色器像素延迟渲染

像素着色器(OpenGL 里叫片元着色器)对光栅化产生的每个片元运行,用插值后的顶点数据算出颜色,也可以输出不透明度、修改深度或直接丢弃片元。多渲染目标让它一次输出多组数据,催生了延迟着色。它通常只能写自己所在的像素,但能通过 2×2 quad 拿到相邻像素的导数。

输入

  • 顶点着色器的输出在三角形上插值后,就成了像素着色器的输入。
  • 插值方式由像素着色器指定,通常用透视校正插值:越远的地方,相邻像素之间对应的世界空间距离越大。比如通向地平线的铁轨,越远枕木看起来越密。也可以选择不考虑透视的屏幕空间插值。
  • 其他输入:片元的屏幕位置(Shader Model 3.0 起),以及三角形哪一面朝向相机的标志,可以在一个 pass 里给正反面不同材质。

输出

  • 片元颜色,可选的不透明度,可选地修改深度。
  • 模板值通常不能修改,直接传给合并阶段(DirectX 11.3 起允许修改)。
  • 雾和 alpha 测试在 SM 4.0 起从合并阶段移到了像素着色器里。
  • 丢弃 (discard):不输出任何结果。例如自定义裁剪平面,可以自由决定多个裁剪体之间是"与"还是"或"的关系。

多渲染目标 (MRT) 与延迟着色

  • 每个片元可以同时输出多组值,写进不同的缓冲区,每个缓冲区叫一个渲染目标,一般是 4 个或 8 个。
  • 渲染目标通常尺寸相同;一些架构要求位深甚至数据格式相同。
  • 例如一个 pass 同时输出颜色、物体 ID 和世界空间距离。
  • 这催生了延迟着色 (Deferred Shading):第一个 pass 只把每个像素的位置和材质信息存下来,后续 pass 再高效地计算光照和其他效果(RTR4 第 20.1 节)。

限制:通常看不到邻居

像素着色器一般只能写自己所在的位置,也读不到相邻像素刚算出的结果。但这个限制没那么严重:一个 pass 的输出可以在下一个 pass 里任意读取,再用图像处理的方法处理相邻像素(RTR4 第 12.1 节)。

例外:导数和 2×2 quad

  • 像素着色器可以拿到任意插值量沿屏幕 x、y 方向每像素的变化量(梯度或导数),这对纹理过滤非常重要,用来判断一个像素覆盖了多少纹理。
  • 所有现代 GPU 都把片元按 2×2 的 quad 处理,导数就是同一个 quad 里相邻片元的差值。比如书中例子:左下像素的 v 是 17,右边是 6,上面是 19,那么 dv/dx = 6 − 17 = −11,dv/dy = 19 − 17 = 2。
  • 后果一:即使 quad 里有的像素不在三角形内,GPU 也要处理它们,只为了算导数。所以大量细小三角形会浪费像素着色。
  • 后果二:在动态流程控制的代码里(if 或者次数不固定的循环)不能取导数,因为 quad 里四个片元必须执行同样的指令,结果才有意义。离线渲染器也有同样的限制。

UAV、原子操作和 ROV

  • UAV(无序访问视图,DirectX 11;OpenGL 4.3 叫 SSBO):可以写入任意位置的缓冲区,所有像素着色器调用共享,执行顺序任意。
  • 数据竞争:两个调用同时读同一个值、各自加一、再写回,结果只加了一次。GPU 用专门的原子单元避免这个问题,代价是有的着色器要排队等待。
  • ROV(光栅顺序视图,DirectX 11.3):和 UAV 一样可读写,但保证按正确的顺序访问。比如先画远处的蓝色半透明三角形、再画近处的红色,ROV 能保证混合顺序正确,从而可以在像素着色器里写自己的混合方式,不需要合并阶段。代价是检测到乱序时,调用要停下来等之前的三角形处理完。

在游戏开发中的意义

  • 延迟渲染是很多引擎的基础架构,它来自 MRT。
  • 在分支里采样纹理时,要用不依赖隐式导数的采样函数,或者把导数先算在分支外面,否则可能出错。
  • 小三角形过多不只增加顶点开销,也会因为 2×2 quad 浪费像素着色。

GEA 的视角:像素着色器的调用方式(卷 II 11.4.6.1)

  • 名称:OpenGL 叫片元着色器,GEA 认为这个名字更准确,因为它处理的是三角形的片元而不是帧缓冲里的像素;但书中仍沿用更普遍的"像素着色器"。
  • 调用频率:可以每个被覆盖的像素调用一次(像素频率),也可以在 MSAA 下每个采样调用一次(采样频率,即超采样)。
  • 2×2 quad 与"假调用":光栅化以 2×2 的 quad 为单位进行,以便算出纹理过滤所需的梯度,所以管线可能为三角形外的像素触发"假的"像素着色器调用。这与本页「例外:导数和 2×2 quad」一致。
  • 输入插值方式:属性可以按屏幕空间或透视校正方式插值,也可以设为每图元常量,整个三角形(或整条线)的所有调用拿到同一个值。
  • 输出:一个或多个四元向量,通常是一个 RGBA 颜色;配置为多渲染目标 (MRT) 时可输出漫反射与镜面反射率、法线、速度等,构成 G-buffer(见 延迟着色)。
  • 保守光栅化:开启后,凡是可能被三角形任何部分覆盖的像素都会调用像素着色器,并告诉它覆盖了多少,由着色器决定是否写入颜色(订正:D3D 并不把"覆盖了多少"传给像素着色器;保守光栅化 Tier 3 只提供布尔型的 SV_InnerCoverage,表示该像素是否被三角形完全覆盖,其余像素只知道"可能部分覆盖";需要精确覆盖率时要自己在着色器里算。另外 GEA 说"从 DirectX 12 开始",其实 D3D11.3 也提供保守光栅化。见 MS Learn "Conservative rasterization"。见 硬件光栅化与透视校正插值)。
  1. realtime_rendering_4ed.pdf 第 3 章 The Graphics Processing Unit
  2. Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
  3. Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 11 章 11.4.6.1 节

相关页面

图形学可编程着色器与 GPU 管线Programmable Shaders & GPU PipelineGPU 把第 2 章的概念管线实现为一组硬件阶段,分为完全可编程、可配置但不可编程、完全固定三类。现代 GPU 采用统一着色器架构:各类着色器共用同一套编程模型和核心,由 GPU 自行分配负载。着色器用 HLSL、GLSL 等类 C 语言编写,输入分为 uniform 和 varying 两类。图形学光栅化Rasterization光栅化(也叫扫描转换)把屏幕空间里带深度和着色数据的顶点,转换成三角形覆盖的像素(片元),交给像素处理阶段。它是几何处理和像素处理之间的同步点:三个顶点在这里组成三角形。图形学合并阶段与帧缓冲Merging & Framebuffer合并阶段把像素着色算出的片元颜色和缓冲区里已有的颜色结合起来,同时负责深度测试、模板测试和混合。它通常不可编程但高度可配置,也叫 ROP(光栅操作或渲染输出单元)。帧缓冲是系统中所有缓冲区的总称。图形学GPU 并行架构GPU Parallel ArchitectureGPU 为吞吐量而设计:芯片大部分面积是成千上万个着色器核心,而不是缓存和控制逻辑。它靠在大量线程组 (warp/wavefront) 之间快速切换来隐藏访存延迟,用 SIMD 让一组线程锁步执行同一条指令。理解占用率和线程分歧,是写出高效着色器的关键。图形学纹理过滤与 MipmapTexture Filtering & Mipmap纹理在屏幕上被放大时需要插值,被缩小时一个像素覆盖许多纹素,必须预先滤波才能避免混叠。放大用最近邻、双线性或双三次;缩小的主流方案是 mipmap + 三线性插值,斜视角下用各向异性过滤减少过度模糊。注意:过滤纹理值只在输入与着色结果线性相关时等价于过滤最终颜色。图形学延迟着色Deferred Shading延迟着色先用一遍几何 pass 把所有可见表面的材质参数写入多张渲染目标(G-buffer),再以后处理方式对这些存储值逐光源计算光照,从而把"几何可见性"与"光照"彻底解耦,并把材质与光源分离。它避免了着色的过度绘制、简化了着色器管理、天然适合贴花,但 G-buffer 占用显存和带宽,且难以直接支持透明和 MSAA。本页也覆盖前向渲染下限制光源影响范围的基础手段与"光照预通道 (light prepass)"类混合方案。图形学分块着色Tiled Shading分块着色把屏幕划分为瓦片(如 32×32 像素),先为每个瓦片建立可能影响它的光源列表,着色时每个像素在一次着色器调用中只遍历所在瓦片的列表。它可用于延迟着色(tiled deferred),也可用于前向着色(tiled forward,又称 Forward+)。配合 z-prepass 得到的瓦片深度范围可进一步剔除光源,但深度不连续会削弱效果,于是出现 HalfZ、2.5D 剔除等改进。图形学分簇着色Clustered Shading分簇着色把整个视锥(而不只是屏幕平面)划分为三维单元——簇 (cluster),按簇建立光源列表。与依赖场景几何深度的分块着色不同,簇的划分与几何无关,可只由视图和光源计算,因此对相机位置的性能波动更小、对深度不连续更稳健,并为不透明、透明、体积物体提供统一的光照方案。它同样可配合前向或延迟着色,并衍生出 z-binning、光源树等更省内存的变体。本页还汇总了各类多光源方案的对比。图形学延迟纹理与可见性缓冲Deferred Texturing & Visibility Buffer延迟着色避免了着色的过度绘制,但生成 G-buffer 时仍有过度绘制:先画的物体读了大量纹理,被后画的物体挡住后这些带宽全部浪费。延迟纹理只用一遍几何 pass、存极少的数据(纹理坐标与切线基,或仅三角形与实例 ID),直到着色时才读纹理。由于 GPU 算力增长快于带宽,让瓶颈落在计算而非缓冲访问被视为面向未来的做法。图形学着色模型与光源Shading Models & Light Sources着色模型描述物体颜色如何随表面朝向、观察方向和光照变化。光源通过两个参数与着色模型交互:指向光源的方向 \mathbf{l} 和光的颜色 \mathbf{c}{\text{light}}。方向光、点光源、聚光灯的区别就在于这两个量如何随场景变化;距离衰减和方向衰减函数的选择同时影响画面与性能。图形学着色频率与材质系统Shading Frequency & Material Systems实现着色时要按"计算频率"划分工作:整个 draw call 不变的放到应用端作为 uniform,其余放在相应的可编程阶段。实践中大多数着色在像素级完成,顶点着色器负责变换和变形。材质系统把面向美术的材质与底层着色器组织起来,处理海量着色器变体的组合问题。图形学贴花渲染Decal Rendering贴花 (decal) 是叠加在表面上的图案或纹理,如轮胎印、弹孔、玩家喷漆、标志。贴花可只改颜色(纹身)、只改凹凸(压印标志)或换成完全不同的材质(车窗贴纸),可多个叠在同一几何上,也可横跨多个模型。主流做法是用有向盒做正交投影的投影贴花;延迟着色把贴花效果直接写进 G-buffer,分簇前向着色则把贴花像光源一样放进簇列表。图形学抗锯齿Antialiasing渲染本质上是采样,采样频率不足就会混叠(锯齿、闪烁的"萤火虫"、车轮倒转)。屏幕空间抗锯齿在每个像素取多个样本并加权求和:超采样最直接但最贵,MSAA 每像素只着色一次,时间抗锯齿 (TAA) 把样本分散到多帧,形态学方法 (FXAA/SMAA) 作为后处理重建边缘。没有一种方法在所有方面最好。图形学图像处理与后处理Image Processing & Post-Processing后处理是在渲染出图像后再用 GPU 对其做图像处理:把离屏缓冲当纹理,用全屏 pass 让像素着色器(或计算着色器)对每个像素采样邻域并加权求和。核心技巧包括可分离滤波、利用双线性插值减少采样、降采样、以及借助深度/法线保持边缘的(联合)双边滤波(RTR4 12.1 节)。图形学纹理映射管线Texturing Pipeline纹理通过修改着色方程中使用的值(颜色、粗糙度、法线等)来高效表现表面材质的变化。通用纹理管线是:空间位置 → 投影函数得到纹理坐标 → 对应函数变换到纹理空间 → 取值 → 值变换 → 用于着色。本页还整理纹理坐标约定、纹理表示方式、程序纹理、纹理动画、材质贴图、alpha 贴图和纹理光源。图形学线框与线条渲染Wireframe & Line Rendering实心"硬"线条看似简单,却在 CAD、选中高亮、技术插图中很重要。难点在于:线与三角形位于同一位置时如何保证线在前面;如何隐藏或淡化被遮挡的线;线交叉时如何用光晕表现前后关系。图形学硬件光栅化与透视校正插值Hardware Rasterization & Perspective-Correct InterpolationGPU 光栅化由三角形设置与三角形遍历组成。硬件用每条边一个边函数判断采样点是否在三角形内,利用其增量性质按瓦片分层遍历,用左上规则决定恰好落在边上的像素归属,用保护带避免大多数裁剪。边函数的值恰好是子三角形面积的两倍,因此遍历顺带得到重心坐标;透视投影下需要线性插值 a/w 与 1/w 再逐像素相除,得到透视校正插值。本页还介绍 2×2 quad 与辅助像素、保守光栅化。图形学图像空间边缘检测Image-Space Edge Detection不碰场景几何,只对渲染出的深度、法线、物体 ID 等缓冲(G-buffer)做图像处理来找出描边位置,再用形态学算子加粗、与着色结果合成。它对任何表面都适用、不要求网格连通,是游戏中最常见的描边方式之一;缺点是边的判定需要按内容调参,可能漏检或误检。图形学透明渲染与 Alpha 混合Transparency & Alpha Blending本页讲最简单的"视角相关"透明:半透明物体只衰减其后方物体的颜色。z-buffer 每像素只存一个表面,所以透明物体通常要从后往前用 over 运算符混合;顺序无关透明 (OIT) 方法用多遍渲染、逐像素链表或近似公式避免排序,各有内存、性能或准确性代价。预乘 alpha 让过滤、混合和合成更正确高效。图形学图形 API 设备、管线状态与资源Graphics API Devices, Pipeline State & Resources所有图形 API(OpenGL、DirectX、Vulkan、Metal)都围绕同一组概念:代表 GPU 的设备、描述各阶段配置的管线状态、承载数据的资源及其视图。GEA 把 API 分成高层(OpenGL、D3D11 及更早)和底层(D3D12、Vulkan、Metal、PlayStation 的 Gnm),并介绍 D3D11 的特性级别、设备上下文与延迟上下文、D3D12 的命令列表、PSO 和根签名。本页按 GEA 的整理讲这些跨 API 的共同概念,不是任何一个 API 的教程。图形学渲染管线Rendering Pipeline渲染管线是实时图形的核心:给定虚拟相机、三维物体、光源等,把场景转换成一张二维图像。RTR4 把它粗分为四个功能阶段:应用、几何处理、光栅化、像素处理,各阶段并行执行,整体速度由最慢的阶段决定。图形学着色方程与经典光照模型Shading Equation & Classic Lighting ModelsGEA 把渲染方程在两条简化假设下化成的求和式称为着色方程:只有理想化的点光源和方向光,间接光只用一个环境光项粗略近似。配上 Lambert、Phong、Blinn-Phong 这类简单 BRDF,它给不出照片级真实感,但能产生可信的画面,是早期游戏的全部光照,也是理解 BRDF 用法的最好入门(GEA4 卷 II 12.4、12.5.1)。图形学片元着色器与 GLSL 基础Fragment Shaders & GLSL Basics片元着色器(fragment shader)是在每个像素(片元)上并行执行的小程序:输入位置等信息,输出颜色。GLSL 是 OpenGL / OpenGL ES / WebGL 共用的着色语言;本书用全屏 billboard 上的像素着色练习,建立程序化图形的基础心智模型。游戏开发 · UE5Custom 材质表达式Custom Material ExpressionCustom 节点允许在材质图里直接写 HLSL:任意数量输入、指定输出类型,可选额外输出、define 与外部 .ush/.usf 包含路径。它是「内置节点做不到」时的逃生口;代价是无法常量折叠、输入仅函数局部可见、且必须是合法 HLSL。优先用内置节点与材质函数,只在必要时上 Custom。游戏开发 · UE5材质图到 HLSL 的翻译Material Graph to HLSL Translation材质编辑器点 Apply 之后,引擎先把节点图翻译成 HLSL,再把 HLSL 编译成 GPU 字节码。翻译以 MaterialTemplate.ush 为模板、由 FHLSLMaterialTranslator 做文本替换与常量折叠;内置节点能被折叠,Custom 材质表达式 则不能。每个材质还会按特性/平台/用途生成多份着色器排列。

被这些页面引用1 篇

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原