Unity引擎材质系统解析原理
一、核心架构
Unity材质系统围绕**PBR(物理基础渲染)**设计,主要分为三大层级:
-
材质资产层(Material Asset)
- 存储Shader引用、参数(颜色、数值、贴图)、渲染队列、渲染状态(如混合、剔除、ZTest等)。
- 以
.mat
文件形式存在,序列化在项目资源中。 - 关联Shader文件(如Standard、URP/Lit等)。
-
材质实例层(Material Instance)
- 运行时可通过
Material
对象动态修改参数(如SetFloat
、SetTexture
)。 - 支持
MaterialPropertyBlock
,实现同一材质资产下的批量实例化和参数差异化,避免材质资产冗余。 - 支持运行时克隆(
new Material(mat)
),便于动态效果。
- 运行时可通过
-
着色器编译层(Shader Compilation)
- 材质参数和功能开关(如是否有法线贴图、金属贴图)决定Shader变体。
- Unity自动管理Shader变体编译、缓存和运行时切换。
- 支持多平台后端(GLSL/HLSL/Metal等)。
二、核心参数体系
参数类型 | 作用原理 | 数据范围/格式 |
---|---|---|
Albedo | 表面基础色,决定物体主色调 | RGB(0-1) |
Metallic | 金属度,0为绝缘体,1为金属 | 0.0-1.0 |
Smoothness | 光滑度,控制高光锐利度(与Roughness反向) | 0.0-1.0 |
Occlusion | 环境光遮蔽,控制AO强度 | 单通道(0-1) |
Normal Map | 法线贴图,存储切线空间法线 | RGB,标准化向量 |
Emission | 自发光,决定物体自发光颜色 | RGB(0-1) |
Specular | 高光颜色(镜面工作流专用) | RGB(0-1) |
- 金属工作流:Albedo+Metallic+Smoothness为主,贴图通常合并(如Metallic®+Smoothness(A))。
- 镜面工作流:Albedo+Specular+Smoothness,Specular为高光颜色贴图。
三、工作流实现
1. 金属工作流(Metallic Workflow)
-
核心特征:Albedo为基础色,金属度决定能否反射环境,Smoothness控制高光锐度。
-
贴图合并:通常Metallic和Smoothness合并为一张贴图(R通道金属度,A通道光滑度)。
-
Shader片段示例(Unity Standard):
half metallic = tex2D(_MetallicGlossMap, uv).r * _Metallic; half smoothness = _GlossMapScale * tex2D(_MetallicGlossMap, uv).a; half3 diffuse = lerp(albedo, 0, metallic); half3 specular = lerp(0.04, albedo, metallic); // 光照计算
2. 镜面工作流(Specular Workflow)
-
核心特征:Specular贴图直接定义高光颜色,适合非金属有色高光材质。
-
Shader片段示例:
half4 specGloss = tex2D(_SpecGlossMap, uv); half3 specular = specGloss.rgb * _SpecColor.rgb; half smoothness = _GlossMapScale * specGloss.a; // 能量守恒计算
四、渲染流程实现
1. 材质实例化与参数修改
- 运行时修改:
Material.SetFloat
、SetTexture
、SetColor
等。 - 批量实例化:
MaterialPropertyBlock
,用于同一材质资产下的参数差异化,提升批量渲染效率。 - 动态克隆:
new Material(originalMat)
,避免影响原始资产。
2. Shader变体管理
- 功能开关:
#pragma shader_feature
、#pragma multi_compile
,如_NORMALMAP
、_METALLICGLOSSMAP
。 - 变体编译:Unity自动根据材质参数和功能开关生成、缓存、切换Shader变体。
- 变体剔除:通过
ShaderVariantCollection
和GraphicsSettings
优化构建体积。
3. 渲染路径适配
- 前向渲染:每像素最多支持4个实时光源,超出部分以逐顶点或球谐光处理。
- 延迟渲染:所有光源信息写入GBuffer,Lighting Pass统一处理,理论支持无限光源。
五、核心优化机制
1. 能量守恒
- 原理:漫反射+镜面反射能量不超过入射光能量,避免材质过曝。
- 实现:
diffuse + specular <= 1.0
,Shader中通过lerp
和归一化实现。
2. 菲涅尔效应
-
原理:视角越斜,反射越强,提升真实感。
-
实现(Unity内置近似):
half fresnel = pow(1.0 - saturate(dot(normal, viewDir)), 5.0); specular *= 1.0 + fresnel * (1.0 / max(specular, 0.001) - 1.0);
3. 多光源支持
- 前向渲染:主光源+最多3个附加光源逐像素,超出部分逐顶点或球谐光。
- 延迟渲染:所有光源信息写入GBuffer,Lighting Pass统一处理,理论支持无限光源。
六、底层实现与源码参考
- 材质与Shader绑定:
Material
对象持有Shader
引用,参数通过PropertyID
映射到GPU常量缓冲区。 - 参数传递:C#层
Material.SetXXX
→ C++层GfxDevice::SetMaterialProperty
→ GPU Uniform/CBV/UBO。 - 变体切换:C#层
Material.EnableKeyword
/DisableKeyword
,底层自动切换对应变体。 - SRP适配:URP/HDRP通过
MaterialPropertyBlock
和ShaderTagId
实现灵活调度。
七、参考资料
- Unity官方文档-材质系统
- Unity官方文档-标准着色器
- Unity官方文档-Shader变体
- Unity C#源码(Material/Shader)
- Unity SRP源码(URP/HDRP)
- PBR原理与能量守恒
- Unity Standard Shader源码
八、总结
- Unity材质系统以PBR为核心,分为资产、实例、编译三层,支持灵活参数管理和高效批量渲染。
- 支持金属/镜面两种主流工作流,参数体系与物理模型紧密结合。
- 通过Shader变体、能量守恒、菲涅尔效应等机制,兼顾真实感与性能。
- 适配多种渲染路径和平台,底层实现高度自动化,便于开发者高效使用。
下面将结合Unity C#源码、Shader源码和底层流程,分层次给出Unity引擎材质系统的核心源码解析。内容涵盖:
- C#层材质与Shader绑定、参数管理源码
- ShaderLab层参数声明与映射
- 底层参数传递与变体切换流程
- SRP材质系统关键源码(以URP为例)
- 参考与说明
1. C#层材质与Shader绑定、参数管理源码
1.1 材质与Shader绑定
Material.cs(UnityCsReference)
Material.cs 源码
public class Material : Object
{
public Material(Shader shader) { /* 绑定Shader */ }
public Material(Material source) { /* 克隆材质 */ }
public Shader shader { get; set; }
// ...
}
用法示例:
Shader shader = Shader.Find("Standard");
Material mat = new Material(shader);
1.2 材质参数管理
public void SetFloat(string name, float value);
public void SetColor(string name, Color value);
public void SetTexture(string name, Texture value);
public void SetVector(string name, Vector4 value);
public void SetMatrix(string name, Matrix4x4 value);
底层实现:
参数通过Shader.PropertyToID(name)
转为int,映射到GPU常量缓冲区。
示例:
mat.SetFloat("_Metallic", 0.8f);
mat.SetTexture("_MainTex", tex);
1.3 MaterialPropertyBlock支持
MaterialPropertyBlock.cs
MaterialPropertyBlock.cs 源码
MaterialPropertyBlock block = new MaterialPropertyBlock();
block.SetFloat("_Metallic", 0.5f);
renderer.SetPropertyBlock(block);
- 用于批量渲染时的参数差异化,避免实例化新材质。
2. ShaderLab层参数声明与映射
标准Shader参数声明:
Shader "Standard"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo", 2D) = "white" {}
_Metallic ("Metallic", Range(0,1)) = 0.0
_Glossiness ("Smoothness", Range(0,1)) = 0.5
}
SubShader
{
CGPROGRAM
// 声明与Properties一致
float4 _Color;
sampler2D _MainTex;
float _Metallic;
float _Glossiness;
// ...
ENDCG
}
}
- C#层
SetFloat("_Metallic", ...)
会自动映射到Shader中的_Metallic
变量。
3. 底层参数传递与变体切换流程
3.1 参数传递流程
- C#层调用
SetFloat
等,参数存入Material的C++对象。 - 渲染时,Unity将参数打包为Uniform Buffer(CBV/UBO),通过底层API(如
glUniform
、vkUpdateDescriptorSets
)上传到GPU。 - Shader中通过变量名直接访问。
3.2 Shader变体切换
声明:
#pragma shader_feature _NORMALMAP
#pragma shader_feature _METALLICGLOSSMAP
- 材质启用/禁用功能时,C#层调用
EnableKeyword("_NORMALMAP")
,Unity自动切换对应变体。
C#源码片段:
public void EnableKeyword(string keyword);
public void DisableKeyword(string keyword);
底层流程:
- Unity内部维护变体哈希表,按当前启用的Keyword查找/切换Shader变体。
4. SRP材质系统关键源码(以URP为例)
4.1 材质参数传递
URP/Lit.shader(部分)
CBUFFER_START(UnityPerMaterial)
float4 _BaseColor;
float4 _BaseMap_ST;
float _Cutoff;
float _Smoothness;
float _Metallic;
// ...
CBUFFER_END
- SRP下参数通过CBUFFER(常量缓冲区)传递,支持高效批量渲染。
4.2 渲染流程(URP)
UniversalRenderPipeline.cs(核心片段)
foreach (var renderer in renderers)
{
// 绑定材质参数
context.SetupCameraProperties(camera);
// 绘制物体
context.DrawRenderers(cullResults, ref drawingSettings, ref filteringSettings);
}
drawingSettings
中包含材质、ShaderTagId、Keyword等信息。
5. 参考与说明
- UnityCsReference/Material.cs
- UnityCsReference/MaterialPropertyBlock.cs
- Unity Built-in Shaders/Standard.shader
- URP/Lit.shader
- URP/UniversalRenderPipeline.cs
总结
- C#层:Material对象绑定Shader,参数通过SetXXX接口传递,支持PropertyBlock批量优化。
- ShaderLab层:Properties声明参数,自动与C#层映射,支持变体声明。
- 底层:参数打包为Uniform Buffer上传,变体通过Keyword切换。
- SRP:参数通过CBUFFER高效传递,渲染流程高度模块化。