一、着色器开发使用的语言
在 Unity 中编写着色器主要涉及两种语言,分别用于不同层面的逻辑实现:
-
HLSL(High-Level Shading Language)
- 用途:编写着色器的核心程序(顶点着色器、片元着色器、计算着色器等)。
- 优势:
- 跨平台兼容性:Unity 自动将 HLSL 编译为不同图形 API(如 DirectX 的 HLSL、OpenGL 的 GLSL、Metal 的 SLang)的代码。
- 官方支持:URP、HDRP 及内置管线均优先支持 HLSL,且提供丰富的内置函数库(如
UnityCG.cginc
中的坐标转换、光照计算)。
- 示例:
hlsl
// 片元着色器中计算漫反射光照 fixed4 frag(v2f i) : SV_TARGET { fixed3 lightDir = _WorldSpaceLightPos0.xyz; fixed3 normal = normalize(i.normal); fixed diff = saturate(dot(lightDir, normal)); return fixed4(diff, diff, diff, 1); }
-
ShaderLab
- 用途:定义着色器的结构和属性,作为 HLSL 程序的容器。
- 核心功能:
- 声明材质面板参数(如纹理、颜色、数值滑块)。
- 定义子着色器(SubShader)、渲染路径(Render Path)、LOD 等级等。
- 示例:
hlsl
Shader "Custom/DiffuseShader" { Properties { _MainTex ("Texture", 2D) = "white" {} _TintColor ("Tint Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" // 引用HLSL变量 sampler2D _MainTex; float4 _MainTex_ST; fixed4 _TintColor; // ... 顶点和片元着色器代码 ... ENDCG } } }
-
其他语言(非推荐)
- GLSL:直接为 OpenGL 平台编写着色器,但 Unity 仅部分支持,且需手动处理跨平台差异。
- Metal SLang:仅用于 iOS/macOS 平台,Unity 会自动将 HLSL 编译为 Metal 代码,无需直接编写。
二、着色器编写的不同方式
Unity 提供三种主要的着色器编写方式,适用于不同场景和开发需求:
-
顶点与片元着色器(Vertex & Fragment Shaders)
- 特点:
- 直接控制渲染管线的顶点处理和像素计算阶段,灵活性最高。
- 需手动实现光照、阴影等逻辑,适合高性能需求或自定义渲染效果(如体积雾、卡通渲染)。
- 适用场景:
- 优化关键材质(如角色皮肤、武器特效)。
- 实现传统管线不支持的效果(如动态网格变形、自定义光照模型)。
- 代码结构:
hlsl
struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; }; v2f vert(appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag(v2f i) : SV_TARGET { return fixed4(1, 0, 0, 1); // 红色输出 }
- 特点:
-
表面着色器(Surface Shaders,内置管线专用)
- 特点:
- 基于高级抽象层,自动处理光照、阴影、法线贴图等复杂逻辑。
- 语法简化,只需定义材质的表面属性(如漫反射、高光、透明度)。
- 局限性:
- 仅适用于内置渲染管线,URP/HDRP 不支持。
- 生成的代码较多,性能略低于顶点 / 片元着色器。
- 示例:
hlsl
struct SurfaceOutput { fixed3 albedo; fixed3 normal; fixed3 emission; float alpha; }; void surf(Input IN, inout SurfaceOutput o) { o.albedo = tex2D(_MainTex, IN.uv).rgb; o.normal = UnpackNormal(tex2D(_NormalMap, IN.uv)); }
- 特点:
-
固定函数着色器(Fixed Function Shaders,旧版兼容)
- 特点:
- 使用 ShaderLab 的旧版指令(如
Color
、Material
、Lighting
),无需 HLSL 代码。 - 功能受限,仅支持基础渲染(如漫反射、顶点光照),不推荐用于新项目。
- 使用 ShaderLab 的旧版指令(如
- 示例:
hlsl
Shader "Legacy/Diffuse" { SubShader { Pass { Material { Diffuse [_MainColor] } Lighting On SetTexture [_MainTex] { Combine Primary * Texture } } } }
- 特点:
三、跨图形 API 的着色器编写
Unity 会自动将 HLSL/ShaderLab 代码编译为不同平台的图形 API 代码,但部分场景需手动处理差异:
-
图形 API 与编译目标
平台 图形 API HLSL 编译目标 Windows/macOS DirectX 11/12 HLSL Linux OpenGL/OpenCL GLSL(通过 glslang 转换) Android Vulkan/OpenGL ES GLSL iOS/macOS Metal Metal SLang(自动转换) WebGL WebGL 1.0/2.0 GLSL ES -
跨平台注意事项
- 纹理采样差异:
- 使用 Unity 内置宏
UNITY_SAMPLE_TEX2D
替代原生tex2D
,自动适配不同 API 的采样方式。
hlsl
// 正确方式 fixed4 tex = UNITY_SAMPLE_TEX2D(_MainTex, i.uv); // 避免直接使用 fixed4 tex = tex2D(_MainTex, i.uv);
- 使用 Unity 内置宏
- 精度声明:
- 移动端需显式声明变量精度(如
half
或fixed
),减少 GPU 指令数。
hlsl
// 声明half精度(适用于URP移动端) half4 frag(v2f i) : SV_TARGET { ... }
- 移动端需显式声明变量精度(如
- 剔除模式与深度测试:
- 部分 API(如 Metal)默认开启背面剔除,需通过
Tags { "Cull"="Off" }
显式关闭。
- 部分 API(如 Metal)默认开启背面剔除,需通过
- 纹理采样差异:
-
条件编译与平台判断
- 使用
#pragma target
指定 Shader Model 版本(如#pragma target 3.0
)。 - 通过
UNITY_API_FAMILY
宏判断当前 API 家族(如UNITY_API_FAMILY_OpenGL
)。
hlsl
#if UNITY_API_FAMILY_URP // URP专用代码 #endif
- 使用
四、推荐工作流与学习路径
-
新项目最佳实践
- 渲染管线选择:
- 移动端 / 中低端设备:使用 URP + Shader Graph 或手写 HLSL 顶点 / 片元着色器。
- 高端平台 / 电影级效果:使用 HDRP + Shader Graph,结合 HLSL 实现复杂逻辑。
- 避免使用:表面着色器(Surface Shaders)和固定函数着色器,优先采用可视化工具或底层 HLSL。
- 渲染管线选择:
五、总结:选择合适的着色器开发方式
需求 | 推荐方式 | 工具 / 语言 |
---|---|---|
快速实现材质效果 | Shader Graph | 可视化节点(HLSL) |
高性能渲染(如移动端) | 顶点 / 片元着色器 | HLSL |
内置管线传统项目维护 | 表面着色器(Surface) | HLSL + ShaderLab |
跨平台兼容 | 手写 HLSL + 条件编译 | HLSL + ShaderLab |
实验性特效或底层控制 | 计算着色器(Compute) | HLSL |
通过理解不同语言和编写方式的适用场景,结合 Unity 的自动编译机制,开发者可高效实现跨平台的高性能着色器,同时利用可视化工具(如 Shader Graph)降低入门门槛。建议从顶点 / 片元着色器的基础语法入手,逐步深入 HLSL 和渲染管线原理,以应对复杂的渲染需求。