Blog

着色器语言综述及在引擎中的应用

明白了,我将撰写一篇正式且系统化的博客文章,列举并介绍主流着色器语言(如HLSL、GLSL、Cg、MSL、SPIR-V等),并解析它们如何在Unity3D(U3D)和Unreal Engine(UE)中构建Shader体系,帮助大家建立清晰的认知体系。请稍等,我正在为你准备内容。

主流着色器语言概述

不同渲染API和平台采用了各自的着色语言。官方上,OpenGL/OpenGL ES 使用的是 GLSL(OpenGL Shading Language),Direct3D 使用的是 HLSL(High-Level Shading Language) (着色器 - 维基百科,自由的百科全书)。NVIDIA 曾推出通用跨API的 Cg 语言,支持生成 OpenGL 和 DirectX 着色器,但自 2012 年起已被弃用 (着色器 - 维基百科,自由的百科全书)。Apple 在 Metal 框架中定义了自己的 Metal Shading Language(MSL) (着色器 - 维基百科,自由的百科全书)。此外,Vulkan(以及 OpenCL)采用了 SPIR-V——一种二进制中间表示(Intermediate Representation),用于图形和计算着色器的统一编译 (What is SPIR-V :: Vulkan Documentation Project)。下面分别介绍这些语言的特点、应用平台和历史背景:

  • HLSL (High-Level Shading Language):由微软为 DirectX 开发(约 2002 年),语法类似 C/C++,使用“语义”(semantics) 标注顶点属性和渲染输出。现代 HLSL 支持各种着色器阶段(顶点、像素/片元、几何、细分、计算、光线追踪等)和 Shader Model 6.x 的新特性 (Cg (programming language) - Wikipedia) (Cg (programming language) - Wikipedia)。HLSL 是 Windows、Xbox 平台的官方着色语言,常用于 Unity 和 Unreal 等引擎。HLSL 与 NVIDIA 的 Cg 语言核心语法相似,后者可生成 HLSL 或 GLSL 着色器代码 (Cg (programming language) - Wikipedia)。示例:一个简单的 HLSL 顶点着色器可能写成

    float4 vert(float4 pos : POSITION) : SV_POSITION {
        return mul(UnityObjectToClipPos, pos);
    }
    

    这里使用了 HLSL 的输入语义 POSITION 和输出语义 SV_POSITION,并调用 Unity 内置的变换矩阵。

  • GLSL (OpenGL Shading Language):由 Khronos 组织为 OpenGL/OpenGL ES 定义(从 OpenGL 2.0 时代引入),也是 WebGL 的标准着色语言。GLSL 语法同样基于 C 语言,使用 layout(location = …) 等布局修饰符指定顶点属性或片元输出。它与 HLSL 的主要差异在于资源绑定(GLSL 将纹理与采样器合并)和内置函数等。由于 Vulkan 的出现,GLSL 可经由 glslang 编译为 SPIR-V 在 Vulkan 中使用;同时 Vulkan 还定义了 GLSL 的“Vulkan 兼容”方言(GLSL for Vulkan)。示例:一个简单的 GLSL 顶点着色器示例如下:

    #version 330 core
    layout(location = 0) in vec3 aPos;
    uniform mat4 u_MVP;
    void main() {
        gl_Position = u_MVP * vec4(aPos, 1.0);
    }
    

    该代码使用了 GLSL 的 layout 来指定顶点属性位置,并通过 uniform 传入变换矩阵,最后将结果写入内建变量 gl_Position

  • Cg (C for Graphics):NVIDIA 与微软合作开发的跨平台着色语言,设计之初用于同时输出 DirectX 和 OpenGL 着色器。语法与 HLSL 基本相同,包含大量图形特定扩展。尽管早期被广泛用于显卡编程(特别是在早期的 Unity 版本中),但自 2012 年起 NVIDIA 停止了对 Cg 的更新 (着色器 - 维基百科,自由的百科全书)。如今 Cg 更多作为历史名词,开发者通常直接使用等价的 HLSL。

  • Metal Shading Language (MSL):Apple 在 2014 年为 iOS/MacOS 的 Metal API 引入的着色语言。MSL 基于 C++14,采用类似 HLSL 的语法,但引入了新的属性语法(如 [[attribute(n)]][[buffer(n)]][[threadgroup]] 等)来指定输入/输出绑定和并行计算。MSL 专用于 Apple 硬件,且与 GPU 关系紧密(如使用 float3threadgroup memory 等)。示例(一个 Metal 顶点着色器):

    #include <metal_stdlib>
    using namespace metal;
    struct VertexIn { float3 position [[attribute(0)]]; };
    vertex float4 vert(VertexIn in [[stage_in]], constant float4x4 &MVP [[buffer(0)]]) {
        return MVP * float4(in.position, 1.0);
    }
    

    其中使用了 [[stage_in]][[buffer]] 等 MSL 特性。

  • SPIR-V (Standard Portable Intermediate Representation V):Khronos 定义的一种二进制中间表示,专为 Vulkan 和 OpenCL 而设计 (What is SPIR-V :: Vulkan Documentation Project)。SPIR-V 可通过编译工具(如 glslangdxc 或 Google 的 Shaderc)从 GLSL/HLSL/ESSL 生成,作为 Vulkan 管线创建时所需的着色器二进制输入。其优势在于统一了不同高级语言编译器前端,并且提高了驱动效率和跨平台兼容性 ([ SPIR-V Overview

  • The Khronos Group Inc](https://www.khronos.org/spirv/#:~:text=SPIR,porting%2C%20debug%20and%20optimization%20tools)) (What is SPIR-V :: Vulkan Documentation Project)。例如,Microsoft 的 DXC 编译器可以直接将 HLSL 编译为 SPIR-V,从而在 Vulkan 中使用 ([ SPIR-V Overview

  • The Khronos Group Inc](https://www.khronos.org/spirv/#:~:text=SPIR,porting%2C%20debug%20and%20optimization%20tools))。SPIR-V 本身不面向开发者编写(除非使用较高级的工具链),而是图形引擎与驱动间的底层接口。

Unity3D 中的着色语言与 Shader 系统

在 Unity3D(U3D)中,所有用户编写的着色器程序都采用 HLSL 语法(历史上称为 Cg) (Unity - Manual: Shading language used in Unity)。虽然 Unity Shader 源文件通常以 ShaderLab 格式组织(顶级的 Shader 块、SubShaderPass 等),但实际的计算逻辑由 HLSL 代码块实现。Unity 早期使用 Cg 语言,其关键字 CGPROGRAM.cginc 扩展名沿用至今,但引擎已经转向直接使用 HLSL 编译。因此,所有在 Unity 中编写的着色器代码最终都必须是有效的 HLSL (Unity - Manual: Shading language used in Unity)。

ShaderLab 与 HLSL 的结合

Unity 的 ShaderLab 框架为跨平台渲染提供元语言结构,开发者在其中声明材质属性、渲染队列和多个渲染通道(Pass)。真正的着色算法写在特定的代码块中,以 CGPROGRAM/ENDCGHLSLPROGRAM/ENDHLSL 包裹 (Unity - Manual: Shader code blocks in ShaderLab reference)。其中,CGPROGRAM(旧式前缀)默认自动包含 Unity 的若干内置库(如 UnityCG.cginc),可直接使用常用宏和内置函数 (Unity - Manual: Shader code blocks in ShaderLab reference);而 HLSLPROGRAM(新式前缀)不会隐式包含这些库,需要手动 #include 所需头文件,适用于内置管线和可编程渲染管线(URP/HDRP) (Unity - Manual: Shader code blocks in ShaderLab reference)。举例如下,一个简单的 Unity 顶点/片元着色器框架:

Shader "Custom/SimpleShader" {
  Properties { _Color ("Color", Color) = (1,1,1,1) }
  SubShader {
    Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #include "UnityCG.cginc"
      struct appdata { float4 vertex : POSITION; };
      struct v2f { float4 pos : SV_POSITION; };
      v2f vert(appdata v) {
          v2f o;
          o.pos = UnityObjectToClipPos(v.vertex);
          return o;
      }
      fixed4 frag(v2f i) : SV_Target {
          return _Color;
      }
      ENDCG
    }
  }
}

在上例中,CGPROGRAM 块内的语法即 HLSL(Cg),vert/frag 函数描述了顶点变换和像素颜色计算。Unity 会根据目标平台使用相应的编译器(Windows 平台用微软 HLSL 编译器 DXC/FXC,OpenGL 平台则将 HLSL 转换为 GLSL 等)完成最终编译 (Unity - Manual: Shading language used in Unity)。通过 ShaderLab 的多编译宏,Unity 还可为不同渲染路径(如前向渲染 vs 延迟渲染)或平台(PC、移动)生成不同变体。

Shader Graph 的语言依赖

Unity 的 Shader Graph 是一种基于节点的着色器编辑器,它本质上通过图形化界面构建节点网络,再将其转换为底层 HLSL 代码。Shader Graph 允许用户拖放节点(数学运算、纹理采样、插值等)来定义材质逻辑,而最终所有节点共同生成的着色器函数实际上用 HLSL 来实现。Custom Function 节点就是一个显例:它允许开发者在节点网络中注入自定义的 HLSL 代码片段,可直接书写或引用外部 .hlsl 文件 (Custom Function Node | Shader Graph | 6.9.2 )。正如 Unity 官方文档所述:“Shader Graph 的 Custom Function 节点让你可以在图中注入自定义的 HLSL 代码” (Custom Function Node | Shader Graph | 6.9.2 )。换言之,Shader Graph 并没有引入新的底层语言,最终输出仍为标准 HLSL 着色器,并通过 ShaderLab 管道提交编译。在 Shader Graph 编辑器中,用户可以在最终节点上右键选择“显示生成的代码”来查看完整的 HLSL 源码,甚至将其导出为常规 Shader 文件进行调试或扩展(例如将生成的 HLSL 与 Unity 的光照模型结合)。

Unreal Engine 中的着色语言与材质系统

Unreal Engine (UE) 的材质系统也使用 HLSL 作为底层着色语言。UE 中的 Material Editor 是基于节点的可视化着色器编辑器,通过连接表达式节点构建材质网络。与 Unity 类似,这些节点最终会被编译成 HLSL 代码。Unreal 提供了一个 “HLSL 代码” 面板(可在窗口菜单开启)用于显示当前材质网络生成的 HLSL 代码 (虚幻引擎材质编辑器UI | 虚幻引擎 5.5 文档 | Epic Developer Community)。正如官方文档所述:“此面板显示了由当前材质生成的高级着色器语言(HLSL)代码。注意这并非编辑器;用户无法修改 HLSL 代码。这只是一个查看器,允许用户显示由材质网络定义的代码” (虚幻引擎材质编辑器UI | 虚幻引擎 5.5 文档 | Epic Developer Community)。通过这个 HLSL 代码视图,开发者可以直观地看到每个节点所对应的 HLSL 计算和函数,了解材质如何被转换为低层次实现。无论目标平台是 Windows(DirectX)还是移动/主机(Metal/Vulkan),UE 的材质都会先生成对应平台的 HLSL,再由引擎转换到最终硬件支持的语言(如在 macOS 上使用 Metal Shader Converter 将 HLSL 中间表示转换为 MSL) (Bringing Unreal Engine on macOS up to feature parity with Windows—progress report - Unreal Engine)。例如在苹果平台上,Unreal 5.4+ 开始使用 Metal Shader Converter,直接将 DirectX 的 HLSL 中间语言(DXIL)转换为 Metal 着色中间表示,以尽量减少转换次数 (Bringing Unreal Engine on macOS up to feature parity with Windows—progress report - Unreal Engine)。

Material Editor 节点系统与 HLSL 生成机制

材质图表中的每一个节点本质上对应一段 HLSL 代码(或一个函数调用)。当保存或编译材质时,Unreal 的图形管线将这些节点级联生成一个完整的 HLSL 着色器程序,然后交给编译器(基于目标渲染接口)生成 GPU 可执行代码。通常情况下,用户无需直接编写或修改 HLSL,整个过程对用户透明。Unreal 的文档指出:材质编辑器的 HLSL 代码面板仅供查看使用,用户无法直接编辑 (虚幻引擎材质编辑器UI | 虚幻引擎 5.5 文档 | Epic Developer Community),所有更改都应在节点图层面完成。这种节点到代码的自动生成机制既方便了非编程美术人员创作复杂材质,也确保了跨平台兼容性。

Custom 节点与手写 Shader

尽管 Material Editor 已非常强大,但有时需要在材质中插入一些手写逻辑。Unreal 提供了 Custom Expression(自定义表达式)节点,允许开发者编写原生的 HLSL 代码片段并作为一个节点使用。官方文档说明:“Custom expression(自定义表达式)允许你编写任意的 HLSL 着色器代码,对任意数量的输入进行操作并输出结果” (Custom Expressions | Unreal Engine 4.27 Documentation | Epic Developer Community)。使用时,可以在节点的文本框中写入一个函数体或表达式,比如对法线进行自定义计算或实现特殊效果。这段 HLSL 代码会被插入到最终生成的着色器函数中,与其他节点代码一起编译。需要注意的是,Custom Node 中的代码也是 HLSL 片段,不能直接访问图形 API 的底层资源,只能通过节点输入来获取纹理、常量等。 (Custom Expressions | Unreal Engine 4.27 Documentation | Epic Developer Community)的说明强调了这一点。

在更高级的使用场景下,Unreal 允许开发者编写全局着色器(Global Shader)或使用材质插件来直接写完整的 HLSL 文件。这些方法通常结合 C++ 代码,将自定义 HLSL 文件编译进引擎,用于后处理、计算着色器等特殊用途。但对于大多数材质工作流程而言,Material Editor 配合 Custom Expression 已足够实现复杂的视觉效果。

总结

总之,各大图形框架和引擎最终都离不开着色器语言:它们定义了从模型顶点到最终像素的光照、材质与效果计算。掌握主流的着色语言(HLSL、GLSL、MSL 等)和理解它们在引擎中的应用方式,对于现代图形开发至关重要。无论是 Unity 的 ShaderLab 与 Shader Graph,还是 Unreal 的材质编辑器与自定义节点,它们都依赖于底层的 HLSL/GLSL 代码来驱动渲染流水线。随着实时渲染、虚拟制作、元宇宙等领域的蓬勃发展,熟悉着色器编程能够让开发者更好地实现炫目的视觉效果,优化性能并拓展创意边界。学习并精通着色语言将为未来的图形编程、游戏开发和虚拟现实带来重要优势,推动技术创新与创作实践不断前行。

参考文献: 以上内容参考了 Unity 和 Unreal 官方文档,以及相关技术资料 (Unity - Manual: Shading language used in Unity) (Unity - Manual: Shader code blocks in ShaderLab reference) (Custom Function Node | Shader Graph | 6.9.2 ) (虚幻引擎材质编辑器UI | 虚幻引擎 5.5 文档 | Epic Developer Community) (Custom Expressions | Unreal Engine 4.27 Documentation | Epic Developer Community) (着色器 - 维基百科,自由的百科全书) (What is SPIR-V :: Vulkan Documentation Project) (Bringing Unreal Engine on macOS up to feature parity with Windows—progress report - Unreal Engine)等。