程序纹理(Procedural Texture)指的是那些由计算机生成的图像,我们通常使用一些特定的算法来创建个性化图案或非常真实的自然元素,例如木头、石子等。使用程序纹理的好处在于我们可以使用各种参数来控制纹理的外观,而这些属性不仅仅是那些颜色属性,甚至可以是完全不同类型的图案属性,这使得我们可以得到更加丰富的动画和视觉效果。
效果:
代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[ExecuteInEditMode]
public class ProceduralTextureGeneration : MonoBehaviour {
public Material material = null;
#region Material properties
[SerializeField, SetProperty("textureWidth")]
private int m_textureWidth = 512;
public int textureWidth {
get {
return m_textureWidth;
}
set {
m_textureWidth = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("backgroundColor")]
private Color m_backgroundColor = Color.white;
public Color backgroundColor {
get {
return m_backgroundColor;
}
set {
m_backgroundColor = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("circleColor")]
private Color m_circleColor = Color.yellow;
public Color circleColor {
get {
return m_circleColor;
}
set {
m_circleColor = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("blurFactor")]
private float m_blurFactor = 2.0f;
public float blurFactor {
get {
return m_blurFactor;
}
set {
m_blurFactor = value;
_UpdateMaterial();
}
}
#endregion
private Texture2D m_generatedTexture = null;
// Use this for initialization
void Start () {
if (material == null) {
Renderer renderer = gameObject.GetComponent<Renderer>();
if (renderer == null) {
Debug.LogWarning("Cannot find a renderer.");
return;
}
material = renderer.sharedMaterial;
}
_UpdateMaterial();
}
private void _UpdateMaterial() {
if (material != null) {
m_generatedTexture = _GenerateProceduralTexture();
material.SetTexture("_MainTex", m_generatedTexture);
}
}
private Color _MixColor(Color color0, Color color1, float mixFactor) {
Color mixColor = Color.white;
mixColor.r = Mathf.Lerp(color0.r, color1.r, mixFactor);
mixColor.g = Mathf.Lerp(color0.g, color1.g, mixFactor);
mixColor.b = Mathf.Lerp(color0.b, color1.b, mixFactor);
mixColor.a = Mathf.Lerp(color0.a, color1.a, mixFactor);
return mixColor;
}
private Texture2D _GenerateProceduralTexture() {
// 初始化一张二维纹理
Texture2D proceduralTexture = new Texture2D(textureWidth, textureWidth);
// 圆与圆的间距
float circleInterval = textureWidth / 4.0f;
// 圆半径
float radius = textureWidth / 10.0f;
// 模糊系数
float edgeBlur = 1.0f / blurFactor;
for (int w = 0; w < textureWidth; w++) {
for (int h = 0; h < textureWidth; h++) {
// 使用背景颜色进行初始化
Color pixel = backgroundColor;
// 依次画9个圆
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// 当前圆的圆心位置
Vector2 circleCenter = new Vector2(circleInterval * (i + 1), circleInterval * (j + 1));
// 计算当前像素与圆心的距离
float dist = Vector2.Distance(new Vector2(w, h), circleCenter) - radius;
// 模糊圆的边界
Color color = _MixColor(circleColor, new Color(pixel.r, pixel.g, pixel.b, 0.0f), Mathf.SmoothStep(0f, 1.0f, dist * edgeBlur));
// 与之前得到的颜色进行混合
pixel = _MixColor(pixel, color, color.a);
}
}
proceduralTexture.SetPixel(w, h, pixel);
}
}
proceduralTexture.Apply();
return proceduralTexture;
}
}
首先,我们需要在Start函数中进行相应的检查,以得到需要使用该程序纹理的材质。首先检查了material 变量是否为空,如果为空,就尝试从使用该脚本所在的物体上得到相应的材质。完成后,调用_UpdateMaterial 函数来为其生成程序纹理。
_UpdateMaterial 函数:它确保material不为空,然后调用_GenerateProceduralTexture 函数来生成一张程序纹理, 并赋给m_ generatedTexture变量。完成后,利用Material.SetTexture函数把生成的纹理赋给材质。材质material中需要有-一个名为_ MainTex 的纹理属性。
_GenerateProceduralTexture 函数:代码首先初始化一张二维纹理,并且提前计算了一些生成纹理时需要的变量。然后,使用了一个两层的嵌套循环遍历纹理中的每个像素,并在纹理上依次绘制9个圆形。最后,调用Texture2D.Apply函数来强制把像素值写入纹理中,并返回该程序纹理。
在Unity中,有一类专门使用程序纹理的材质,叫做程序材质(Procedural Materials)。这类材质和我们之前使用的那些材质在本质上是一样的,不同的是,它们使用的纹理不是普通的纹理,而是程序纹理。需要注意的是,程序材质和它使用的程序纹理并不是在Unity中创建的,而是使用了一个名为Substance Designer的软件在Unity外部生成的。
Substance Designer 是一个非常出色的纹理生成工具,很多3A的游戏项目都使用了由它生成的材质。我们可以从Unity的资源商店或网络中获取到很多免费或付费的Substance材质。这些材质都是以.sbsar为后缀的,我们可以直接把这些材质像其他资源一 样拖入Unity项目中。