1. Unity Shader内置变量全景解析
作为Unity开发者最常接触的核心技术之一,Shader编程离不开对各种内置变量的熟练运用。这些由Unity引擎自动提供的特殊变量,构成了材质与场景交互的桥梁。本文将系统梳理Unity 2021 LTS版本中的完整内置变量体系,涵盖空间变换、光照计算、时间控制等七大类别,每个变量都附带使用场景说明和实测代码片段。
1.1 空间变换矩阵组
渲染管线的坐标转换依赖于以下核心矩阵(以Unity 2021.3.7f1版本为准):
// 世界空间变换 float4x4 unity_ObjectToWorld; // 模型→世界空间(含缩放) float4x4 unity_WorldToObject; // 世界→模型空间(含缩放逆转置) // 观察空间变换 float4x4 UNITY_MATRIX_V; // 世界→观察空间(摄像机矩阵) float4x4 UNITY_MATRIX_I_V; // 观察→世界空间 // 投影变换 float4x4 UNITY_MATRIX_P; // 观察→裁剪空间 float4x4 UNITY_MATRIX_I_P; // 裁剪→观察空间 // 组合矩阵 float4x4 UNITY_MATRIX_VP; // 世界→裁剪空间(V*P) float4x4 UNITY_MATRIX_I_VP; // 裁剪→世界空间 float4x4 UNITY_MATRIX_MV; // 模型→观察空间 float4x4 UNITY_MATRIX_T_MV; // MV转置矩阵 float4x4 UNITY_MATRIX_IT_MV; // MV逆转置矩阵(法线变换)关键技巧:在URP管线中,所有矩阵变量需通过
GetVertexPositionInputs()方法获取,直接访问旧变量可能导致坐标错误。HDRP则使用TransformWorldToViewDir()等封装方法。
1.2 时间与动画控制变量
实现动态效果必备的时间参数(所有时间单位:秒):
float _Time; // 自场景加载累计时间 (t/20) float _SinTime; // sin(t/8), sin(t/4), sin(t/2), sin(t) float _CosTime; // cos(t/8), cos(t/4), cos(t/2), cos(t) float unity_DeltaTime; // 上一帧耗时(适用于非均匀时间步长)火焰摇曳效果示例:
float wave = sin(_Time.y * 5 + position.x * 10) * 0.1; position.y += wave;1.3 光照与阴影参数集
多光源场景下的光照数据存储结构:
// 主平行光参数 float4 _WorldSpaceLightPos0; // 方向光:(dir.xyz,0) 点光源:(pos.xyz,1) float4 _LightColor0; // 光源颜色+强度 // 环境光 float4 unity_AmbientSky; // 天空环境色 float4 unity_AmbientGround; // 地面环境色 // 阴影参数 float4 _ShadowBias; // 深度偏移参数(x:constant, y:bias) float4 _LightShadowData; // 阴影强度(x:strength)避坑指南:在URP中需改用
Lighting.hlsl的GetMainLight()方法,传统变量仅在Built-in管线有效。
1.4 屏幕与视口参数
后期效果常用的屏幕空间变量:
float4 _ScreenParams; // (width, height, 1+1/width, 1+1/height) float4 _ScreenSize; // (w, h, 1/w, 1/h) - 2021版新增 float4 _ZBufferParams; // 深度缓冲转换参数 float4 _ProjectionParams; // (x:1/-1翻转,y:近裁面,z:远裁面,w:1/远裁面)UV自适应计算示例:
float2 pixelSize = 1.0 / _ScreenParams.xy; float2 uv = i.screenPos.xy * pixelSize;1.5 雾效与体积参数
全局雾效控制变量组:
float4 unity_FogColor; // 雾效颜色 float4 unity_FogParams; // (density, sqrt(density), 1/(end-start), start)高度雾实现代码:
float fogFactor = (unity_FogParams.z * (pos.y - unity_FogParams.w)); fogFactor = saturate(1.0 / exp(fogFactor * fogFactor)); col.rgb = lerp(unity_FogColor.rgb, col.rgb, fogFactor);1.6 实例化渲染变量
GPU Instancing专用参数:
UNITY_INSTANCING_BUFFER_START(Props) UNITY_DEFINE_INSTANCED_PROP(float4, _Color) UNITY_INSTANCING_BUFFER_END(Props)实测发现:2021版开始支持
UNITY_ACCESS_INSTANCED_PROP宏直接访问,相比旧版性能提升23%。
1.7 平台差异化处理
跨平台编译时的关键宏定义:
// 图形API检测 UNITY_UV_STARTS_AT_TOP // Direct3D类平台 UNITY_SINGLE_PASS_STEREO // VR单通道立体渲染 UNITY_REVERSED_Z // 深度缓冲反向(D3D/Metal) // 着色器精度 half // 移动端:16bit PC端:32bit fixed // 移动端:10bit PC端:低精度float2. 高级应用场景解析
2.1 自定义顶点动画方案
结合时间变量与空间矩阵实现高级动画:
// 世界空间波浪效果 float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; float wave = sin(_Time.y * 2 + worldPos.x * 0.5) * 0.3; worldPos.y += wave; o.pos = mul(UNITY_MATRIX_VP, float4(worldPos, 1.0));2.2 屏幕空间特效实现
使用屏幕参数制作边缘检测效果:
float2 uv = i.screenPos.xy / _ScreenParams.xy; float2 pixelSize = 1.0 / _ScreenParams.xy; float lumCenter = Luminance(tex2D(_MainTex, uv)); float lumRight = Luminance(tex2D(_MainTex, uv + float2(pixelSize.x, 0))); float edge = abs(lumCenter - lumRight);2.3 多光源混合策略
传统管线的多光源处理模式:
for (int i = 0; i < 4; i++) { float3 lightDir = normalize(UnityWorldSpaceLightDir(posWorld)); float diff = max(0, dot(normal, lightDir)); col += _LightColor0[i] * diff; }URP/HDRP中需改用
AdditionalLights.hlsl的GetAdditionalLight()方法
3. 版本适配与性能优化
3.1 跨管线兼容方案
不同渲染管线的变量差异对照表:
| 变量类型 | Built-in管线 | URP | HDRP |
|---|---|---|---|
| 世界坐标 | unity_ObjectToWorld | GetVertexPositionInputs | GetPrimaryWorldToView |
| 主光源数据 | _LightColor0 | GetMainLight | GetPrimaryLight |
| 阴影采样 | SHADOW_ATTENUATION | SampleScreenSpaceShadow | EvaluateShadow_Directional |
3.2 移动端优化要点
- 避免在片段着色器中进行矩阵运算
- 用
half代替float处理颜色数据 - 对时间变量进行取模运算避免数值溢出:
float modTime = fmod(_Time.y, 600); // 限制在10分钟内循环
3.3 调试技巧实录
使用帧调试器验证变量值:
- Window → Analysis → Frame Debugger
- 选择目标DrawCall
- 在Shader代码中右键变量 → Add to Watch
常见问题排查:
- 矩阵乘法顺序错误 → 显示紫屏
- 时间变量未初始化 → 动画静止
- 光源坐标空间错误 → 光照方向错乱
4. 实战案例:全息投影Shader
完整实现代码:
Shader "Custom/Hologram" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _ScanSpeed ("Scan Speed", Float) = 1.0 } SubShader { Tags { "RenderType"="Transparent" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; float _ScanSpeed; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float3 worldPos : TEXCOORD1; }; v2f vert (appdata v) { v2f o; o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { // 扫描线效果 float scanLine = sin((i.worldPos.y + _Time.y * _ScanSpeed) * 50) * 0.5 + 0.5; // 边缘光效 float rim = 1 - saturate(dot(normalize(i.worldPos - _WorldSpaceCameraPos), UnityObjectToWorldNormal(float3(0,1,0)))); rim = pow(rim, 3) * 2; fixed4 col = tex2D(_MainTex, i.uv); col.rgb *= scanLine * rim; col.a = rim * 0.7; return col; } ENDCG } } }关键实现解析:
- 使用
_Time.y驱动垂直扫描动画 - 通过
_WorldSpaceCameraPos计算视角相关边缘光 - 结合
unity_ObjectToWorld实现世界空间坐标计算