Unity基础:材质与纹理入门——给物体穿上“衣服“

Unity基础:材质与纹理入门——给物体穿上"衣服"

大家好,欢迎回到Unity教程系列。上一篇文章我们学会了如何创建和操作基本的3D物体,但你可能会觉得——那些白花花或纯色的方块看起来太无聊了。今天这篇文章,我们要让游戏物体变得赏心悦目起来。

材质和纹理是游戏视觉表现的基石。它们决定了场景中的每个物体的外观——墙壁是木头还是砖石,角色是穿着布衣还是铠甲,地面是草地还是沙漠。理解了材质与纹理的工作原理,你就能给游戏世界"穿上衣服",让它变得生动而丰富。

一、材质与纹理的基本概念

1.1 什么是材质(Material)

💡 材质(Material)是定义物体表面外观的数据集合。它告诉渲染引擎:这个物体表面应该是什么颜色、有多少反光、是粗糙还是光滑、是否透明、是否发光等等。

一个材质本质上是一个Shader(着色器)+ 一组参数值的组合。Shader决定了"怎么算颜色",材质的参数决定了"具体算什么颜色"。

类比理解:如果把Shader比作一道菜的配方(决定怎么做),材质就是按照这个配方做出来的具体一道菜(放了多少盐、多少辣椒)。

1.2 什么是纹理(Texture)

💡 纹理(Texture)是一张二维图片,它被"包裹"在3D模型的表面,提供精细的视觉细节。纹理是材质最重要的参数之一——没有纹理,物体就是纯色的;有了纹理,物体可以有木纹、砖纹、布料纹理等丰富的细节。

常见的纹理类型

  • Albedo / Base Map(基色贴图):定义物体表面的基本颜色和图案。这是最常用的纹理,比如一块木板的木纹、一面墙的砖纹。
  • Normal Map(法线贴图):模拟表面凹凸细节而不增加模型面数。蓝色调的图片,让低面数模型看起来有高面数模型的细节。
  • Metallic Map(金属度贴图):指定表面的哪些部分是金属、哪些部分不是。白色区域是金属,黑色区域不是。
  • Smoothness / Roughness Map(光滑度贴图):指定表面的哪些区域更光滑、哪些更粗糙。
  • Emission Map(自发光贴图):指定表面的哪些区域会自发光(如机器人的眼睛、能量核心等)。
  • Height Map(高度贴图):提供表面的高度信息,用于视差映射等高级效果。
  • Ambient Occlusion Map(环境光遮蔽贴图):模拟角落和缝隙中的环境光阴影。

1.3 材质、纹理和Shader的关系

这三者之间的关系可以用一句话概括:Shader是渲染算法,材质是算法的参数集,纹理是参数的一种(图片类型的参数)

Shader (Standard) ────定义了"基于物理的渲染(PBR)"的计算方法 │ └── Material (Mat_Wood) ────使用Standard Shader,并设置了具体的参数 ├── Albedo: wood_texture.png(木材贴图作为基色) ├── Normal Map: wood_normal.png(木材的法线贴图) ├── Metallic: 0(木材不是金属) ├── Smoothness: 0.3(木材比较粗糙) └── Tiling: (1, 1)

二、创建和应用材质

2.1 在Unity中创建材质

① 在Project窗口中,进入你想要存放材质的文件夹(如果没有Materials文件夹,先创建一个)。
② 右键 → Create → Material。
③ 新材质出现在Project窗口中,命名为"M_FirstMaterial"(建议材质以M_前缀命名,方便搜索和管理)。
④ 选中材质,在Inspector中看到材质的属性面板。

2.2 材质属性面板详解

以Unity内置的Standard Shader为例(Built-in渲染管线),材质面板的主要属性:

Rendering Mode(渲染模式)

  • Opaque(不透明):默认模式,用于完全不透明的物体(墙壁、石头、木箱等)。这是性能最好的模式。
  • Cutout(镂空):用于有完全透明区域的物体(树叶、栅栏的铁丝网),配合Alpha Cutoff阈值使用。要么完全透明,要么完全不透明。
  • Fade(渐隐):物体整体可以半透明,适合淡入淡出效果。但有一些光照计算上的限制。
  • Transparent(透明):用于玻璃、水等需要真实透明度的物体,支持不同区域的不同透明度。

Albedo(基色/反照率)

  • 颜色选择器:设置物体的基本颜色
  • 纹理槽:拖拽贴图到这里作为基色纹理
  • 颜色与纹理的关系:纹理的每个像素颜色会乘以这里设置的颜色。如果颜色设为白色,纹理以原始颜色显示;如果颜色设为红色,纹理整体偏红。

Metallic(金属度):0为完全非金属(电介质),1为完全金属。真实世界中的物体要么是金属要么不是(很少有中间值),所以通常设为0或1。光滑的非金属(如光滑塑料)和光滑的金属(如不锈钢)看起来完全不同——金属会反射出有色高光。

Smoothness(光滑度):0为完全粗糙(漫反射),1为完全光滑(镜面反射)。金属和非金属都可以有不同的光滑度。

Normal Map(法线贴图):给表面添加凹凸细节。法线贴图是一张蓝紫色的特殊贴图,通过改变每个像素的法线方向来模拟凹凸不平的表面效果。

Height Map(高度贴图):配合视差映射(Parallax Mapping)使用,提供更强的深度感和立体感。

Emission(自发光):让材质从内部发光。可以设置发光颜色、强度和发光贴图。用于制作灯光面板、屏幕、魔法符文、能量核心等发光物体。注意:自发光材质在Baked GI中可以影响周围的静态物体。

Tiling(平铺)和Offset(偏移)

  • Tiling的X和Y值控制纹理在物体表面的重复次数。设为(2,2)意味着纹理在水平方向和垂直方向各重复2次,图案变得更密集。
  • Offset控制纹理的偏移量,用于微调纹理在物体表面的位置。

2.3 给物体应用材质

有三种方式给GameObject应用材质:

方式一:拖拽(最常用)
将材质从Project窗口直接拖到Scene视图中的物体上。松手后物体立即改变外观。

方式二:从Inspector赋值
① 选中物体
② 在Inspector中找到MeshRenderer组件
③ 在Materials数组中找到Element 0
④ 将材质拖到槽位中,或点击槽位右侧的圆形选择器搜索材质

方式三:拖到Hierarchy
将材质从Project窗口拖到Hierarchy中的GameObject名称上。

2.4 多个材质与子网格

一个MeshRenderer可以包含多个材质槽(对应模型的不同子网格SubMesh)。例如一个角色模型可能分成了"身体"和"武器"两个子网格,在Materials数组中分别使用不同的材质。

在代码中操作材质:

// 获取并修改物体的第一个材质// 注意:这会创建材质实例的副本!Materialmat=GetComponent<Renderer>().material;mat.color=Color.red;// 获取共享材质(不会创建实例副本,推荐用于批量操作)MaterialsharedMat=GetComponent<Renderer>().sharedMaterial;// 获取所有材质Material[]materials=GetComponent<Renderer>().materials;materials[0].color=Color.blue;GetComponent<Renderer>().materials=materials;

⚠️ 注意materialsharedMaterial的区别:

  • material:返回该物体的材质实例副本。修改它只会影响这一个物体,但会增加DrawCall。
  • sharedMaterial:返回共享的材质资源。修改它会影响所有使用该材质的物体,但不会创建副本。

如果在运行时修改了material属性,Unity会自动创建该材质的副本(称为Material Instance),这会导致额外的内存占用和DrawCall。如果确实需要运行时修改大量物体的材质属性,建议使用MaterialPropertyBlock

三、纹理的导入与设置

3.1 获取纹理资源

你可以从以下途径获取纹理:

Unity Asset Store:搜索"Free Textures",有大量免费的纹理包可以直接下载导入。

免费纹理网站

  • Poly Haven(polyhaven.com):提供高质量PBR纹理集
  • AmbientCG(ambientcg.com):免费PBR材质库
  • OpenGameArt(opengameart.org):游戏开发纹理资源

自己制作:使用Photoshop、GIMP、Substance Designer等工具制作自定义纹理。

3.2 导入纹理到Unity

最简单的方式:将图片文件(PNG、JPG、TGA等)直接从文件管理器拖拽到Unity的Project窗口中。

导入后,Unity会自动处理纹理并显示在Project窗口中。选中纹理,在Inspector中可以看到纹理导入设置(Texture Import Settings):

Texture Type(纹理类型):这是最重要的设置,决定了纹理的用途:

  • Default:默认类型,用于3D模型的纹理
  • Normal Map:法线贴图,Unity会进行特殊处理
  • Sprite (2D and UI):用于2D精灵和UI元素
  • Lightmap:光照贴图专用
  • Editor GUI and Legacy GUI:编辑器界面用
  • Cursor:自定义鼠标光标
  • Cookie:灯光Cookie纹理
  • Single Channel:单通道纹理

Texture Shape(纹理形状)

  • 2D:普通2D纹理(最常用)
  • Cube:立方体贴图(天空盒、环境反射用)

sRGB (Color Texture):如果纹理是颜色信息(如Albedo贴图),勾选此选项。如果是数据纹理(如法线贴图、金属度贴图),需要取消勾选以确保数据精度不被颜色空间转换影响。

Alpha Source:Alpha通道的来源。可以是从纹理文件本身读取,或者从灰度图生成。

Generate Mip Maps:是否生成MipMap(多级渐远纹理)。MipMap可以优化远距离的纹理渲染质量并提升性能,但会增加约33%的内存占用。对于3D场景中的纹理,一般建议开启。

Max Size:纹理的最大尺寸限制。例如设为1024,则导入的4K纹理会被缩小到1024×1024。这是控制纹理内存占用的重要设置——在移动平台上,通常把非关键纹理控制在512或1024以下。

Compression:压缩格式。不同平台有不同的最佳压缩格式:

  • PC:DXT(RGB)或BC7(RGBA高质量)
  • iOS:ASTC(最佳选择)
  • Android:ETC2(通用兼容)或ASTC(较新设备)

3.3 纹理的过滤模式与包裹模式

Filter Mode(过滤模式)

  • Point (no filter):最近邻采样。像素风格明显,适合像素风游戏。
  • Bilinear:双线性过滤。在4个相邻像素之间插值,质量适中,性能适中。
  • Trilinear:三线性过滤。在双线性基础上还在MipMap级别之间插值。质量最好但内存带宽开销最大。

Aniso Level(各向异性过滤级别):提高倾斜角度观察纹理时的清晰度。对于地面纹理特别有用。值为0时不使用,值越高效果越好但性能开销越大。

**Wrap Mode(包裹模式)**控制纹理UV坐标超出0-1范围时的行为:

  • Repeat(重复):纹理在超出范围时重复平铺。适合地板、墙壁等需要连续纹理的场合。
  • Clamp(钳制):纹理边缘像素被拉伸以填充超出区域。适合UI元素、独立图标等。
  • Mirror(镜像):纹理以镜像方式重复。
  • Mirror Once:镜像一次后钳制。

四、动手实践

4.1 创建木箱材质

① 创建一个新材质,命名为"M_WoodCrate"。
② 将Albedo颜色设为偏暖的棕色。
③ 将Metallic设为0(木材不是金属)。
④ 将Smoothness设为0.2-0.3(木材比较粗糙)。
⑤ 如果有法线贴图,拖到Normal Map槽中,你会看到木材纹理出现了明显的凹凸感。
⑥ 将材质拖到场景中的一个Cube上——现在它看起来像一个木箱了。

4.2 做一面砖墙

① 创建新材质,命名为"M_BrickWall"。
② Albedo颜色偏红棕色。
③ 由于墙壁通常很大,适当提高Tiling值。将Tiling的X和Y都设为(3,3)或更高,让纹理图案更密集。
④ Metallic设为0,Smoothness设为0.1。
⑤ 将材质应用到墙壁Cube上。

4.3 创建一个金属球

① 创建新材质,命名为"M_PolishedMetal"。
② 将Albedo颜色设为深灰色或暗金色。
③ 将Metallic设为1(完全金属)。
④ 将Smoothness设为0.7-0.9(光滑金属)。
⑤ 将材质应用到球体上。观察——球体现在看起来像一个真正的金属球,能反射周围环境(通过天空盒Cubemap)。

4.4 制作发光物体

① 创建新材质,命名为"M_GlowMagic"。
② Albedo设为白色或浅色。
③ 勾选Emission选项。
④ 将Emission的颜色设为亮色(比如亮蓝色或亮橙色),调节强度。
⑤ 将材质应用到场景中的一个物体上。即使在暗处,这个物体也会发出光芒。

五、代码中动态操作材质

5.1 修改材质属性

usingUnityEngine;publicclassMaterialController:MonoBehaviour{privateRendererrend;voidStart(){rend=GetComponent<Renderer>();}// 改变颜色publicvoidChangeColor(Colorcolor){rend.material.color=color;}// 修改金属度(使用Shader属性名)publicvoidSetMetallic(floatvalue){rend.material.SetFloat("_Metallic",Mathf.Clamp01(value));}// 修改光滑度publicvoidSetSmoothness(floatvalue){rend.material.SetFloat("_Glossiness",Mathf.Clamp01(value));}// 闪烁效果协程publicSystem.Collections.IEnumeratorFlicker(floatduration,floatinterval){floatelapsed=0;ColororiginalColor=rend.material.color;while(elapsed<duration){rend.material.color=originalColor*Random.Range(0.3f,1f);yieldreturnnewWaitForSeconds(interval);elapsed+=interval;}rend.material.color=originalColor;}}

5.2 MaterialPropertyBlock——避免创建材质副本

当需要在运行时修改材质属性时,直接使用renderer.material会创建材质实例的副本,增加内存占用和DrawCall。对于批量物体的属性修改,更好的做法是使用MaterialPropertyBlock

usingUnityEngine;publicclassEfficientMaterialChange:MonoBehaviour{privateRendererrend;privateMaterialPropertyBlockpropBlock;voidStart(){rend=GetComponent<Renderer>();propBlock=newMaterialPropertyBlock();}publicvoidSetColorWithoutInstance(Colorcolor){// 使用MaterialPropertyBlock不会创建材质副本rend.GetPropertyBlock(propBlock);propBlock.SetColor("_Color",color);rend.SetPropertyBlock(propBlock);}}

六、纹理导入常见问题

6.1 纹理显示模糊

降低Max Size可以减小纹理内存占用,但设置过小会导致模糊。确保Max Size不小于纹理在屏幕上实际需要的分辨率。对于UI和近距离观察的纹理,保持较高的Max Size。

6.2 纹理重复太多(或太少)

调整Tiling值。Tiling越大,纹理在物体表面重复越多次。一般大面积的地面和墙壁需要较大的Tiling值(2-5),而小型道具通常用1即可。

6.3 材质在Build后显示异常

检查Shader是否被正确打包。在Graphics Settings中确保使用的Shader被包含在"Always Included Shaders"列表中,或者在资源中有引用。

七、本篇总结

✅ 本文核心知识点回顾:

  • 材质(Material)定义物体表面外观——颜色、金属度、光滑度、透明度等
  • 纹理(Texture)是二维图片,为材质提供精细的视觉细节
  • Albedo、Normal Map、Metallic、Smoothness是PBR材质的核心属性
  • 纹理导入设置中的Max Size和Compression直接影响运行时的内存占用
  • renderer.material会创建材质实例副本,批量修改应使用sharedMaterial或MaterialPropertyBlock
  • Tiling控制纹理的平铺密度,Offset控制纹理的位置偏移

📝 下一篇文章将学习Camera相机组件——透视投影与正交投影的区别,以及如何通过控制相机来决定玩家看到什么。

动手任务
① 创建至少5个不同外观的材质(木材、金属、石头、玻璃、发光)
② 尝试下载免费纹理贴图并导入到材质中使用
③ 调整Tiling和Offset,观察纹理变化
④ 写一个简单脚本,在运行时动态改变物体的颜色


关于作者:拥有多年Unity开发经验的游戏开发者,专注Unity系统化教学与最佳实践分享。欢迎在评论区交流讨论!