3.5 材质 (Material) 与着色器 (Shader) 3.5 材质 (Material) 与着色器 (Shader) 在 Unity3D 的世界中,材质 (Material) 与着色器 (Shader) 是构建视觉效果的核心基石。它们共同决定了游戏中物体的外观,从简单的颜色到复杂的光影效果,都离不开材质和着色器的精妙配合。理解材质和着色器的工作原理,是成为一名优秀的 Unity 开发者不可或缺的一步。 3.5.1 材质 (Material) 的概念与作用 什么是材质? 在现实世界中,材质描述了物体表面的物理属性,例如颜色、纹理、粗糙度、反射率等。这些属性决定了物体如何与光线相互作用,从而呈现出不同的外观。
在 Unity3D 的世界中,材质 (Material) 与着色器 (Shader) 是构建视觉效果的核心基石。它们共同决定了游戏中物体的外观,从简单的颜色到复杂的光影效果,都离不开材质和着色器的精妙配合。理解材质和着色器的工作原理,是成为一名优秀的 Unity 开发者不可或缺的一步。
什么是材质?
在现实世界中,材质描述了物体表面的物理属性,例如颜色、纹理、粗糙度、反射率等。这些属性决定了物体如何与光线相互作用,从而呈现出不同的外观。
在 Unity3D 中,材质 (Material) 是资源的实例,它定义了如何渲染一个对象的外观。可以将材质视为一个“皮肤”,它被应用到 3D 模型上,赋予模型特定的视觉特征。材质包含了控制渲染过程的各种参数,例如颜色、纹理、光照属性等。
材质的作用:
定义物体外观: 材质是控制物体最终视觉呈现的关键。通过调整材质的属性,可以改变物体的颜色、纹理、光泽度、透明度等,使其呈现出不同的质感和风格。
控制渲染流程: 材质内部引用了着色器 (Shader),着色器是实际执行渲染计算的程序。材质通过向着色器传递参数,来控制着色器的行为,从而影响最终的渲染结果。
资源复用与管理: 材质作为资源,可以在多个对象之间复用,节省资源并方便管理。例如,多个相同的木箱可以使用同一个木质材质,只需调整少量参数即可实现差异化。
性能优化: 合理使用材质可以优化渲染性能。材质的属性设置和着色器的复杂度直接影响渲染的计算量。通过选择合适的着色器和优化材质参数,可以提高游戏的运行效率。
材质的组成部分:
一个材质主要由以下几个部分组成:
着色器 (Shader): 材质的核心,决定了渲染算法和可调节的属性。每个材质都必须关联一个着色器。
属性 (Properties): 着色器定义的、可以由材质实例控制的参数。例如颜色、纹理、数值等。材质通过调整这些属性的值来定制外观。
纹理 (Textures): 图像数据,用于赋予材质更丰富的细节和视觉效果。例如颜色纹理、法线纹理、高光纹理等。
颜色 (Colors): 用于定义材质的颜色属性,例如漫反射颜色、高光颜色、环境光颜色等。
数值 (Numbers): 用于控制材质的数值属性,例如光滑度、金属度、透明度、反射强度等。
什么是着色器?
着色器 (Shader) 是一种运行在 GPU (图形处理器) 上的程序,它负责计算如何渲染 3D 物体。更具体地说,着色器定义了渲染管线中特定阶段的处理逻辑,例如顶点着色器负责处理顶点数据,片元着色器(也称为像素着色器)负责处理像素颜色。
可以将着色器视为渲染的“算法”或“配方”,它告诉 GPU 如何将 3D 模型、材质属性、光照信息等转化为屏幕上的像素颜色。
着色器的作用:
控制渲染管线: 着色器是渲染管线中不可或缺的组成部分。不同的着色器程序负责不同的渲染阶段,共同完成最终的渲染任务。
实现视觉效果: 着色器是实现各种视觉效果的核心工具。通过编写不同的着色器代码,可以实现各种各样的渲染效果,例如卡通渲染、水面效果、火焰效果、体积云效果等。
定义材质属性: 着色器定义了材质可以调节的属性。材质的属性面板实际上是由着色器定义的。
性能优化: 着色器的性能直接影响渲染效率。编写高效的着色器代码,可以提高游戏的帧率,优化性能。
着色器的类型:
Unity 中常用的着色器类型包括:
表面着色器 (Surface Shader): Unity 提供的简化着色器编写方式,隐藏了复杂的渲染细节,适合快速实现常见的光照模型。
顶点片元着色器 (Vertex/Fragment Shader): 也称为 ShaderLab 程序,是最灵活、功能最强大的着色器类型,可以完全自定义渲染管线的各个阶段。
固定功能管线着色器 (Fixed Function Shader): 较旧的着色器类型,功能有限,现在已经较少使用。
计算着色器 (Compute Shader): 用于通用计算的着色器,不直接参与渲染,但可以用于 GPU 加速的计算任务。
本章节主要关注 顶点片元着色器 (Vertex/Fragment Shader),因为它提供了最大的灵活性和控制力,是理解着色器原理的基础。
材质和着色器是紧密相关的,它们之间的关系可以理解为:
着色器是蓝图,材质是实例: 着色器定义了渲染逻辑和可调节的属性,而材质是基于某个着色器创建的实例。一个着色器可以被多个材质复用。
材质驱动着色器: 材质通过设置属性值,来控制着色器的行为。材质的属性值会传递给着色器,作为着色器计算的输入参数。
着色器决定材质的功能: 材质的功能和可调节的属性完全由其关联的着色器决定。更换材质的着色器,材质的功能和外观也会随之改变。
可以用一个比喻来形象地理解它们的关系:
关系图解释:
Shader (着色器) 是核心,它定义了渲染的规则和可以调节的属性。
Material (材质) 基于 Shader 创建,是 Shader 的一个具体实例。
Shader 定义了材质可以有哪些属性,例如颜色、纹理等。
Material 负责设置这些属性的具体数值,例如颜色设置为红色,纹理设置为木纹图片。
Material 被应用到 3D 对象上,最终决定了对象的渲染外观。
当渲染时,Material 将其属性值传递给 Shader,Shader 根据这些属性值和自身的渲染逻辑进行计算,最终输出对象的像素颜色。
1. 创建材质 (Material)
在 Unity 编辑器中,创建材质非常简单:
在 Project 窗口 中,右键单击,选择 Create > Material。
输入材质的名称,例如 "MyMaterial"。
在 Inspector 窗口 中,可以看到材质的属性面板。
2. 选择着色器 (Shader)
在材质的 Inspector 窗口 中,点击 Shader 下拉菜单。
可以看到 Unity 内置的各种着色器,以及项目中自定义的着色器。
选择合适的着色器,例如 "Standard" (标准着色器) 或 "Unlit/Color" (无光照颜色着色器)。
3. 调整材质属性
选择了着色器后,材质的 Inspector 窗口 会显示该着色器定义的属性。
例如,如果选择了 "Standard" 着色器,可以看到 "Albedo" (反照率颜色)、 "Metallic" (金属度)、 "Smoothness" (光滑度) 等属性。
可以通过颜色拾取器、纹理选择器、数值输入框等方式,调整这些属性的值,实时预览材质的外观变化。
代码示例 1:创建并修改材质 (C# Script)
using UnityEngine; public class MaterialExample : MonoBehaviour { public Renderer targetRenderer; // 目标渲染器 void Start() { if (targetRenderer == null) { Debug.LogError("请将 Renderer 组件拖拽到 Target Renderer 字段!"); return; } // 1. 创建一个新的材质实例 (基于 Renderer 当前使用的材质) Material newMaterial = new Material(targetRenderer.sharedMaterial); // 2. 修改材质的颜色属性 (假设 Shader 中定义了 "_Color" 属性) newMaterial.color = Color.blue; // 设置为蓝色 newMaterial.SetColor("_Color", Color.yellow); // 或者使用 SetColor,参数为属性名和颜色值 // 3. 修改材质的浮点数属性 (假设 Shader 中定义了 "_Glossiness" 属性) newMaterial.SetFloat("_Glossiness", 0.8f); // 4. 修改材质的纹理属性 (假设 Shader 中定义了 "_MainTex" 属性) Texture2D texture = Resources.Load<Texture2D>("MyTexture"); // 从 Resources 文件夹加载纹理 if (texture != null) { newMaterial.SetTexture("_MainTex", texture); } else { Debug.LogWarning("纹理 'MyTexture' 未找到!"); } // 5. 将新的材质实例应用到 Renderer targetRenderer.material = newMaterial; // 注意使用 .material 会创建材质的实例,.sharedMaterial 会共享材质 } }
代码解释:
new Material(targetRenderer.sharedMaterial): 创建一个新的材质实例,并复制 targetRenderer 当前使用的共享材质的属性。这样做可以避免修改共享材质,影响到其他使用该材质的对象。
newMaterial.color = Color.blue;: 直接修改材质的 color 属性 (如果 Shader 中有定义)。
newMaterial.SetColor("_Color", Color.yellow);: 更通用的方法,使用 SetColor 函数,参数为 Shader 中定义的属性名称 ("_Color") 和颜色值。
newMaterial.SetFloat("_Glossiness", 0.8f);: 使用 SetFloat 函数修改浮点数属性。
newMaterial.SetTexture("_MainTex", texture);: 使用 SetTexture 函数修改纹理属性。
targetRenderer.material = newMaterial;: 将新创建的材质实例赋值给 targetRenderer.material。注意这里使用 material 属性会为每个渲染器创建一个独立的材质实例,而 sharedMaterial 会共享材质,修改 sharedMaterial 会影响所有使用该材质的渲染器。
4. 创建着色器 (Shader)
在 Unity 中,可以通过以下方式创建着色器:
在 Project 窗口 中,右键单击,选择 Create > Shader > Standard Surface Shader (表面着色器) 或 Create > Shader > Unlit Shader (无光照着色器) 或 Create > Shader > ShaderLab > Standard Shader (Built-in Render Pipeline) (顶点片元着色器)。
输入着色器的名称,例如 "MyShader"。
双击打开着色器文件,使用文本编辑器 (例如 Visual Studio Code) 编辑着色器代码。
代码示例 2:一个简单的无光照颜色着色器 (ShaderLab)
Shader "Unlit/SimpleColorShader" { Properties { _Color ("Main Color", Color) = (1,1,1,1) // 定义一个颜色属性,默认白色 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; fixed4 _Color; // 声明与 Properties 中属性对应的变量 v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return _Color; // 输出材质的颜色属性 } ENDCG } } }
代码解释:
Shader "Unlit/SimpleColorShader": 定义着色器的路径和名称,在材质的 Shader 下拉菜单中会显示 "Unlit/SimpleColorShader"。
Properties 块: 定义着色器可以调节的属性。
_Color ("Main Color", Color) = (1,1,1,1): 定义一个名为 _Color 的颜色属性,显示名称为 "Main Color",类型为 Color,默认值为白色 (1,1,1,1)。SubShader 块: 定义一个或多个 SubShader,Unity 会根据平台和硬件选择合适的 SubShader。
Tags { "RenderType"="Opaque" }: 设置渲染类型为不透明 (Opaque)。
LOD 100: 设置 LOD (Level of Detail) 级别。
Pass 块: 定义一个渲染通道,一个 SubShader 可以包含多个 Pass。
CGPROGRAM ... ENDCG: 包含 CG/HLSL 代码块。
#pragma vertex vert: 声明顶点着色器函数为 vert。
#pragma fragment frag: 声明片元着色器函数为 frag。
#include "UnityCG.cginc": 包含 Unity 预定义的 CG/HLSL 头文件,提供常用的宏和函数。
struct appdata: 定义顶点着色器输入结构体,包含顶点位置 vertex : POSITION。
struct v2f: 定义顶点着色器输出结构体 (Vertex to Fragment),包含裁剪空间顶点位置 vertex : SV_POSITION。
fixed4 _Color;: 在 CGPROGRAM 块中声明一个与 Properties 中 _Color 属性同名的变量,用于接收材质传递的属性值。
v2f vert (appdata v): 顶点着色器函数。
o.vertex = UnityObjectToClipPos(v.vertex);: 将模型空间顶点坐标转换为裁剪空间坐标。fixed4 frag (v2f i) : SV_Target: 片元着色器函数。
return _Color;: 返回材质的颜色属性 _Color 作为像素颜色。5. 将着色器应用到材质
创建材质后,在材质的 Inspector 窗口 中,点击 Shader 下拉菜单。
在下拉菜单中,找到并选择你创建的着色器,例如 "Unlit/SimpleColorShader"。
材质的属性面板会更新,显示着色器定义的属性,例如 "Main Color"。
调整 "Main Color" 属性,可以看到应用该材质的物体的颜色随之改变。
6. 材质实例与共享材质
在 Unity 中,材质分为 实例材质 (Instance Material) 和 共享材质 (Shared Material)。
实例材质 (Instance Material): 每个 Renderer 组件的 material 属性返回的是材质的实例。修改实例材质只会影响该 Renderer 组件,不会影响其他对象。Unity 会在运行时为每个 Renderer 创建独立的材质实例,以支持不同的材质属性设置。
共享材质 (Shared Material): Renderer 组件的 sharedMaterial 属性返回的是共享材质。修改共享材质会影响所有使用该材质的对象。共享材质通常用于多个对象共享相同材质的情况,可以节省内存。
何时使用实例材质,何时使用共享材质?
修改单个对象的材质: 使用 renderer.material 创建或修改材质实例。这是最常用的情况,例如为每个角色设置不同的颜色。
修改多个对象的共享材质: 使用 renderer.sharedMaterial 获取共享材质,并进行修改。需要谨慎使用,因为会影响所有使用该材质的对象。通常用于全局性的材质修改,例如改变所有物体的环境光颜色。
创建新的材质实例: 使用 new Material(sharedMaterial) 基于共享材质创建一个新的实例材质。
性能考虑:
材质实例化 (Material Instancing): Unity 提供了材质实例化技术,可以有效地减少 Draw Call (绘制调用) 次数,提高渲染性能。当多个对象使用相同的材质,但只有少量属性不同时,可以使用材质实例化。Unity 会将这些对象的绘制合并成一个 Draw Call。
Shader 复杂度: 复杂的 Shader 会增加 GPU 的计算负担,降低渲染性能。应根据实际需求选择合适的 Shader 复杂度,避免过度使用复杂的 Shader 效果。
纹理大小和数量: 纹理的加载和采样也会影响性能。应优化纹理大小和数量,尽量使用压缩纹理格式,并合理使用纹理图集 (Texture Atlas)。
材质 (Material) 与着色器 (Shader) 是 Unity3D 渲染系统的核心组成部分。材质定义了物体的外观属性,着色器则定义了渲染算法。理解它们之间的关系和工作原理,是掌握 Unity 渲染技术的基础。
通过本章节的学习,您应该掌握了以下知识点:
材质和着色器的概念和作用。
材质与着色器的关系:着色器是蓝图,材质是实例。
如何在 Unity 中创建和修改材质。
如何创建简单的顶点片元着色器。
材质实例和共享材质的区别和使用场景。
材质和着色器的性能优化 considerations.
深入理解材质和着色器,并灵活运用它们,将帮助您在 Unity 项目中创建出更加精美、高效的视觉效果,提升游戏的品质和用户体验。希望本章节的内容能够为您在 Unity 渲染技术的学习之路上提供有力的帮助。
后续学习方向:
深入学习 ShaderLab 语言和 CG/HLSL 编程: 掌握着色器代码的编写,才能真正自定义渲染效果。
研究 Unity 内置的各种 Shader 和光照模型: 了解 Unity 提供的渲染工具和技术。
学习 Shader Graph 可视化着色器编辑器: 使用可视化工具快速创建复杂的着色器效果。
探索高级渲染技术: 例如延迟渲染、后处理效果、体积渲染、光线追踪等。
祝您在 Unity 渲染技术的学习中取得更大的进步!