Fragment Shader — 是什么、任务及工作原理

作者: IT Sectr 发布日期: 2026-06-11 阅读时间: 10 分钟

Fragment Shader — 是图形管线中可编程的阶段,用于确定图像每个片段(像素)的最终颜色。根据Khronos Group,2025的数据,片段着色器接收来自顶点着色器的插值数据,并执行光照、纹理和颜色效果的计算。Fragment Shader为每个像素调用,这使其成为图形管线中最消耗资源的阶段。

要点

  • Fragment Shader — GPU的可编程阶段,决定屏幕上每个像素的颜色。
  • 调用次数等于屏幕分辨率,使其成为渲染的主要计算负载。
  • 纹理映射和冯氏光照 — 在片段着色器中实现的典型任务。
  • GLSL和Metal Shading Language为编写Fragment Shader提供标准工具。
  • Fragment Shader的性能对于在移动应用中达到60 FPS至关重要。

什么是Fragment Shader?

Fragment Shader(片段或像素着色器)— 是图形管线中可编程的阶段,计算图像每个片段的最终颜色。片段是一个潜在的像素,在通过深度和模板测试后,成为屏幕上可见的像素。

Fragment Shader在光栅化之后执行,此时三维图元(三角形)已被转换为片段集合。着色器在输入时接收插值属性 — 纹理坐标、法线、颜色,这些在顶点着色器中为三角形的顶点计算,并为其内部的每个像素进行线性插值。

Fragment Shader的演变历史

在可编程着色器出现之前(DirectX 8 / OpenGL 2.0之前),像素颜色的计算由固定管线以有限的操作集执行。可编程Fragment Shader于2001年出现,从根本上扩展了图形能力:开发人员获得了对每个像素颜色的完全控制,这使得实现照片级逼真的光照、复杂材质和后处理效果成为可能。

Fragment Shader如何工作?

Fragment Shader由GPU为光栅化器生成的每个片段启动。每次着色器调用处理一个片段,读取其输入数据,执行用户代码,并写入输出值 — 片段的颜色(RGBA格式)。

输入和输出数据

Fragment Shader的输入数据包括来自顶点着色器的插值属性(纹理坐标、法线、颜色)、uniform变量(光源、矩阵)和纹理采样器。输出数据 — 颜色向量(OpenGL ES 2.0中的gl_FragColor或ES 3.0中的用户变量),写入颜色缓冲区。

glsl
#version 300 es
precision mediump float;

in vec2 vTexCoord;
uniform sampler2D uTexture;
out vec4 fragColor;

void main() {
    fragColor = texture(uTexture, vTexCoord);
}

Fragment Shader的主要任务

Fragment Shader负责图像的视觉质量。用户在屏幕上看到的所有效果 — 颜色、阴影、反射、透明度 — 都在图形管线的这个阶段计算。

纹理映射

最常见的操作 — 根据插值的UV坐标从纹理中获取颜色。Sampler2D和texture()函数允许从纹理图集中获取像素颜色。过滤(双线性、三线性、各向异性)通过采样器参数控制,并影响不同视角下的图像清晰度。

冯氏光照

冯氏光照模型(Phong shading)在Fragment Shader中为每个像素计算,比顶点光照(Gouraud shading)提供更平滑的高光。Phong lighting包括三个分量:环境光(ambient)、漫反射光(diffuse)和镜面反射光(specular)。

glsl
// 带有冯氏光照的片段着色器
in vec3 vNormal;
in vec3 vPosition;
uniform vec3 uLightDir;
uniform vec3 uLightColor;

void main() {
    vec3 norm = normalize(vNormal);
    float diff = max(dot(norm, uLightDir), 0.0);
    fragColor = vec4(uLightColor * diff, 1.0);
}

后处理效果和混合

Fragment Shader允许应用后处理:通过LUT表进行颜色校正、模糊(blur)、发光效果(bloom)以及HDR渲染的色调映射(tonemapping)。所有这些效果在像素级别工作,并实时执行。

Fragment Shader的性能与优化

Fragment Shader的性能 — 是现代移动游戏中限制FPS的主要因素。由于着色器为每个像素调用,即使代码复杂度的微小增加也可能导致帧率的显著下降。

主要性能问题

  • 过度绘制(Overdraw) — 由于对象重叠导致同一像素的重复绘制。对于半透明对象尤其关键。
  • 条件分支 — Fragment Shader中的分支降低了SIMD执行效率,因为GPU必须执行两条路径。
  • 纹理采样 — 每次纹理采样(texture lookup)都有数十个GPU周期的延迟。最小化采样器数量可加速着色器。

Fragment Shader的优化方法

对于移动GPU(Qualcomm Adreno、ARM Mali),建议:使用mediump precision处理float变量,合并纹理采样,应用early-z测试以在着色器执行前丢弃不可见的片段。

方法描述收益
Mediump precision16位精度替代32位最高2倍速度
Early-Z着色器前的深度测试减少调用
纹理图集单一纹理替代多个更少切换

Fragment Shader vs Vertex Shader:对比

两种着色器都由开发人员编程,但它们在管线的不同阶段工作并解决不同的任务。Fragment Shader由于调用次数多而显著更消耗资源。

参数Fragment ShaderVertex Shader
调用次数数百万(屏幕分辨率)数千(顶点数量)
输入数据插值属性顶点属性
主要功能计算片段颜色几何变换
纹理访问完全(多个采样器)受限
性能影响高(取决于分辨率)中等(取决于几何形状)

Fragment Shader代码示例

Fragment Shader可以简单(纹理采样)或复杂(多层光照)。让我们来看两个实际示例:基础纹理映射和使用时间的渐变效果。

GLSL中的基础纹理映射

glsl
#version 300 es
precision mediump float;
in vec2 vTexCoord;
in vec3 vColor;
uniform sampler2D uDiffuseMap;
out vec4 fragColor;

void main() {
    vec4 texColor = texture(uDiffuseMap, vTexCoord);
    fragColor = texColor * vec4(vColor, 1.0);
}

Metal Shading Language示例

cpp
#include <metal_stdlib>
using namespace metal;

struct FragmentIn {
    float2 texCoord;
    float3 normal;
};

fragment float4
fragmentMain(FragmentIn in [[stage_in]],
              texture2d<float> tex [[texture(0)]]) {
    constexpr sampler s(filter = linear);
    float4 color = tex.sample(s, in.texCoord);
    return color;
}

常见问题

用简单的话说,什么是Fragment Shader?

Fragment Shader — 是GPU上的一个小程序,决定屏幕上每个像素的颜色。如果Vertex Shader确定对象的形状,那么Fragment Shader确定该对象的外观 — 纹理、光照、颜色。

为什么Fragment Shader如此消耗资源?

Fragment Shader为屏幕上的每个像素调用。在分辨率为2532x1170的设备(iPhone)上,每帧几乎300万次调用。在60 FPS时,着色器每秒执行1.8亿次,这需要巨大的计算能力。

在Fragment Shader中可以制作哪些效果?

在Fragment Shader中实现:纹理映射、光照(Phong、PBR)、阴影、反射、折射、bloom、景深、颜色校正、LUT滤镜和程序化纹理生成。

如何在移动设备上优化Fragment Shader?

使用mediump precision处理float,减少纹理采样次数,应用early-z剔除,合并着色器操作,避免着色器内部的条件分支。

Fragment Shader和Pixel Shader有什么区别?

它们是同一个概念。Fragment Shader术语用于OpenGL / Vulkan,而Pixel Shader用于Direct3D(Microsoft)的术语。功能上相同:两者都计算每个片段/像素的颜色。

总结

  • Fragment Shader — GPU的可编程阶段,计算图像每个像素的颜色。
  • 调用次数等于屏幕分辨率,使其成为最消耗资源的渲染阶段。
  • 纹理映射、冯氏光照和后处理效果 — 片段着色器的主要任务。
  • 性能直接取决于纹理采样次数和使用highp精度。
  • Mediump precision和early-z测试 — 移动GPU的关键优化技术。
  • OpenGL ES使用Fragment Shader术语,Direct3D使用Pixel Shader,本质相同。
  • GLSL和Metal Shading Language — 移动开发中编写Fragment Shader的主要语言。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读