【Unity Shader学习笔记】(四)Shader编程
一、OpenGL与DirectX
这是计算机图形学中两个最核心的应用程序接口(API),它们充当了应用程序与显卡硬件之间的桥梁,让开发者能够调用GPU进行图形渲染和通用计算。
特性维度 | OpenGL | DirectX |
主导公司 | Khronos Group (原SGI) | Microsoft |
平台支持 | 跨平台。Windows, Linux, macOS, Android, iOS, 各种嵌入式系统等。 | 微软生态系统。主要限于Windows、Xbox系列主机。部分支持Win10/11上的Android子系统。 |
设计哲学 | 开放标准。由行业联盟共同维护,规范公开,各家硬件厂商提供自己的实现。更偏向底层和灵活。 | 一体化解决方案。作为Windows系统的一部分,与系统深度集成,提供从图形、声音、输入到网络的一整套多媒体API。 |
发展状态 | 现代版:Vulkan。OpenGL本身已停止重大更新(最新为4.6),其下一代标准是Vulkan API,它是一个更低开销、更显式控制的跨平台API。 | 持续活跃更新。DirectX 12是当前最新版本,同样致力于降低CPU开销和提高硬件控制力,与Vulkan对标。 |
驱动模型 | 传统状态机模式,驱动层较厚,CPU开销相对较高。 | 历史上开销也较大,但到DirectX 12变为底层、显式控制模式,极大降低了CPU开销,将性能优化责任更多地交给了开发者。 |
行业应用 | 专业领域、移动平台、跨平台应用。CAD/CAE软件(如SolidWorks)、科学可视化、Mac/Linux应用、Android/iOS的图形基础(OpenGL ES)。 | 游戏产业(尤其是PC和Xbox)。绝大多数Windows平台的游戏都基于DirectX开发,因为它能更好地发挥Windows系统性能并与Xbox共享代码。 |
易用性 | 学习资料分散,由于是跨平台标准,在不同驱动和平台上可能遇到兼容性问题,需要处理各种扩展(Extension)。 | 开发工具链(如Visual Studio)和文档(MSDN)非常完善,在Windows平台上兼容性和一致性极好,对开发者更友好。 |
二、GLSL/HLSL/Cg
这些是专门用于在GPU上运行的编程语言,称为着色器语言(Shader Language)。程序员使用它们编写顶点着色器(Vertex Shader)、片元着色器(Fragment/Pixel Shader) 等程序,来控制渲染管线的各个阶段。
特性维度 | GLSL (OpenGL Shading Language) | HLSL (High-Level Shading Language) | Cg (C for Graphics) |
关联API | OpenGL / Vulkan (Vulkan使用SPIR-V字节码,通常由GLSL编译而来) | DirectX (D3D9, D3D10, D3D11, D3D12) | 设计为跨API (主要支持OpenGL和DirectX) |
主导公司 | Khronos Group | Microsoft | NVIDIA |
语法风格 | 与C语言类似,但具有大量内置的数学函数和数据类型(如vec3 , mat4 )。 | 与C语言类似,语法和HLSL与GLSL非常接近,但内置函数和语义(Semantics)的命名有所不同。 | 语法几乎是HLSL的超集。与HLSL极度相似,设计目标是一次编写,可编译到多个API目标。 |
平台特性 | 是OpenGL标准的一部分,与OpenGL一样是跨平台的。 | 深度集成于Microsoft平台,是DirectX的官方着色器语言。 | 已被官方弃用 (2012年宣布)。其理念和精神由HLSL和GLSL继承。 |
现状与用途 | 活跃发展。是现代OpenGL和Vulkan开发的首选着色器语言。在Web端也有WebGL使用的GLSL ES。 | 极度活跃。是Windows游戏开发、Unity URP/HDRP、Unreal Engine等主流引擎的标准着色器语言。 | 历史意义重大。在早期简化了着色器开发,但其跨API的使命现在已由离线工具链(如Unity/UE的着色器编译器)更好地实现。开发者现在直接使用HLSL或GLSL。 |
核心总结与关系:
HLSL和GLSL是当前并行的两大主流着色器语言,分别牢牢绑定在DirectX和OpenGL/Vulkan生态中。
Cg是NVIDIA推出的跨API着色器语言,旨在解决HLSL和GLSL的分裂问题。虽然它已被弃用,但其语法深刻影响了HLSL和GLSL的发展。今天你在很多地方看到的类似HLSL的语法,其根源可能就是Cg。
现代游戏引擎(如Unity)如何处理? 引擎通常会选择一个“主”着色器语言(Unity选择HLSL),然后通过其内部的着色器编译器,在打包时根据目标平台(Windows, macOS, Android等)将HLSL代码交叉编译成对应平台所需的着色器形式(如GLSL、GLSL ES、Metal SL等)。这使得开发者只需编写一种代码即可部署到多个平台。
三、ShaderLab
ShaderLab是Unity游戏引擎自定义的一种着色器描述语言。它本身不是一种像HLSL/GLSL那样的可编程着色器语言,而是一个包装和组织框架。
1、定位与作用
Unity需要支持从高端PC到移动WebGL的数十个图形API平台(OpenGL, Direct3D, Metal, Vulkan, GLES等)。如果让开发者直接为每个平台编写GLSL或HLSL代码,将是灾难性的。ShaderLab应运而生,它的核心作用是:
提供统一的抽象层:开发者使用一套ShaderLab语法来定义着色器,由Unity负责将其编译到所有目标平台。
描述着色器的结构:定义属性(Properties)、子着色器(SubShader)、通道(Pass)、渲染状态(Render State)等,而不仅仅是可以编程的代码。
集成可编程代码段:在ShaderLab的
CGPROGRAM
/HLSLPROGRAM
块内部,开发者才真正地编写HLSL代码。
2、核心结构
一个典型的Unity ShaderLab文件(.shader
)结构如下:
Shader "MyShader/Example" { // 着色器在材质下拉菜单中的路径Properties { // 定义在材质面板上可见的可调参数_MainTex ("Texture", 2D) = "white" {}_Color ("Color", Color) = (1,1,1,1)_Glossiness ("Smoothness", Range(0,1)) = 0.5}SubShader { // 为一个渲染平台或一组平台定义渲染方案// 标签:告诉Unity如何以及何时渲染该SubShaderTags { "RenderType"="Opaque" "Queue"="Geometry" }// 渲染状态:如深度测试、混合模式、剔除模式等Cull BackZWrite OnPass { // 一个Pass代表一次渲染调用// 在CGPROGRAM或HLSLPROGRAM块中编写实际的HLSL代码CGPROGRAM#pragma vertex vert // 编译指令:声明顶点着色器函数名#pragma fragment frag // 编译指令:声明片元着色器函数名#include "UnityCG.cginc" // 包含Unity的内置辅助函数和变量// 声明在Properties中定义的属性对应的变量sampler2D _MainTex;fixed4 _Color;// 定义数据结构(从CPU应用到顶点着色器,从顶点着色器到片元着色器)struct appdata { ... };struct v2f { ... };// 下面是HLSL代码!!!v2f vert (appdata v) { ... } // 顶点着色器fixed4 frag (v2f i) : SV_Target { ... } // 片元着色器ENDCG // 结束HLSL代码块}}// 可选的Fallback,如果当前SubShader都不支持,则使用后备着色器Fallback "Diffuse"
}
3、与HLSL/GLSL的关系
ShaderLab是容器,HLSL是内容:ShaderLab文件像一个盒子,盒子里装的“货物”是写在
CGPROGRAM
/HLSLPROGRAM
块中的HLSL代码。Unity负责编译:当你点击打包时,Unity的着色器编译器会读取你的ShaderLab文件,提取出其中的HLSL代码,然后根据你要发布的平台(如Windows -> D3D11, macOS -> Metal)将其交叉编译成对应的底层着色器字节码(如DXBC, SPIR-V, MSL)。开发者无需关心平台差异。
4、总结
ShaderLab是Unity引擎为了实现“一次编写,多处部署”的跨平台着色器解决方案而创造的一种领域特定语言(DSL)。它通过封装渲染设置、属性并集成HLSL代码段,极大简化了现代游戏开发中复杂且碎片化的着色器编写工作。
四、提出问题
计算机图形学、GPU、OpenGL/DirectX、Unity、Shaderlab之间是什么关系?
1、理论基础 - 计算机图形学 (Computer Graphics)
角色:理论、算法和数学基础。
关系:它是所有其他技术的根基。它研究如何用计算机生成、处理和显示图像。它定义了:
渲染管线:顶点处理、光栅化、着色、混合等一系列步骤。
核心算法:如变换(矩阵运算)、光照模型(Phong, Blinn-Phong)、纹理映射、阴影生成、几何处理、光线追踪等。
数学模型:向量、矩阵、四元数、坐标系、投影等。
简单来说:OpenGL/DirectX是计算机图形学理论的具体实现。Shader中写的代码就是在实现图形学中的各种算法和模型。
2、硬件核心 - GPU (Graphics Processing Unit)
角色:专门处理图形和并行计算的硬件。
关系:GPU是所有这些软件技术最终要控制和驱动的物理设备。它的设计特点(高度并行、流水线化)决定了图形API和着色器语言的工作方式。
顶点着色器 在GPU的一组核心上并行执行。
片元着色器 在另一组更庞大的核心上并行执行。
简单来说:GPU是“工厂”,它被设计成能高效地完成图形学计算任务。
3、标准与桥梁 - 图形API (OpenGL / DirectX)
角色:操作系统/应用程序与GPU驱动之间的标准化桥梁。
关系:直接操作GPU硬件是非常复杂且与厂商强相关的。图形API的出现,提供了一套统一的、标准的函数库。
对下:各家GPU厂商(NVIDIA, AMD, Intel)会为自己的显卡提供实现该API标准的驱动程序。这意味着开发者调用
OpenGL.glDrawArrays()
时,NVIDIA和AMD的驱动会分别将它翻译成自己GPU能听懂的指令。对上:应用程序(如游戏、Unity引擎)只需要调用这些标准的API函数(如
glDrawArrays
,DrawIndexed
)就可以命令GPU进行渲染,而无需关心底层的硬件差异。简单来说:OpenGL/DirectX是“标准化的工作指令”,它告诉GPU“工厂”要生产什么,但具体每个车间(不同品牌的GPU)怎么生产,由车间主任(驱动)自己决定。
4、游戏引擎 - Unity
角色:集大成的应用框架和生产力工具。
关系:游戏引擎建立在图形API之上,提供了更高层次的抽象。
它封装了图形API:Unity的渲染代码底层实际上调用了OpenGL、DirectX、Metal或Vulkan。但开发者不需要直接处理这些API,Unity帮你处理了跨平台的问题。
它提供了完整的工作流:不仅渲染,还包括物理系统、动画系统、音频系统、资源管理等,让开发者能更专注于游戏逻辑本身。
简单来说:Unity是“自动化的大型生产线”,它内部包含了调用“标准化工作指令”(图形API)的模块,并为你提供了编辑器、按钮和仪表盘来更方便地管理整个“工厂”(GPU)的生产流程。
5、引擎中的着色器框架 - ShaderLab
角色:Unity引擎内部用于定义和编写着色器的特定领域语言(DSL)和框架。
关系:这是最具体的一层。
ShaderLab是Unity的一部分:
.shader
文件是Unity的资产。它是对图形API的进一步抽象:它的主要目的是解决不同平台(对应不同图形API)的着色器语言差异。你在ShaderLab中写入
CGPROGRAM/HLSLPROGRAM
代码块(通常写HLSL)。Unity的编译器在打包游戏时,会将你的HLSL代码交叉编译到目标平台所需的着色器语言(如Windows平台编译为HLSL/GLSL,macOS平台编译为MSL,Android平台编译为GLSL ES)。
简单来说:ShaderLab是Unity这条“生产线”上的“标准化工艺单模板”。你在这个模板上用一种格式(HLSL)写下工艺要求,Unity的翻译部门(着色器编译器)会把它转换成不同“工厂”(不同平台的GPU)能看懂的“具体指令格式”。
五、总结与类比
层级 | 技术 | 类比 |
---|---|---|
理论层 | 计算机图形学 | 物理学定律 (如牛顿力学) |
硬件层 | GPU | 工厂的机器和工人 |
接口层 | OpenGL / DirectX | 标准的机器操作指令手册 |
框架层 | Unity引擎 | 一整套自动化生产线和管理系统 |
工具层 | ShaderLab | 生产线上使用的、统一的工艺单模板 |
整个工作流程就像是:
一位工程师(你,开发者)利用物理学定律(图形学) 知识,在生产线管理系统(Unity) 中,填写了一张工艺单(ShaderLab文件),描述了如何制造一个产品。管理系统收到指令后,根据目标工厂的类型(Windows/Android等),将工艺单翻译成该工厂标准指令手册(OpenGL/DirectX) 上的具体命令。工厂的车间主任(GPU驱动)收到这些标准命令后,再指挥自己家的工人和机器(GPU) 开始并行地、高效地生产出最终产品(屏幕上的画面)。