unity shader之——高级纹理之程序纹理

程序纹理指的是由那些计算机生成的图像,我们通常使用一些特定的算法来创建个性化图案或非常真实的自然元素,例如木头、石子等。使用程序纹理的好处在于我们可以使用各种参数来控制纹理的外观,而这些属性不仅仅是那些颜色属性,甚至可以是完全不同类型的图案属性,这使得我们可以得到更加丰富的动画和视觉效果。本篇文章我们尝试用算法来实现一个简单的程序纹理和程序材质。

一、unity中实现简单的程序纹理

我们使用一个算法来生成一个波点纹理,如下图:


我们可以在脚本中调整一下参数,如背景颜色、波点颜色等,以控制最终生成的纹理外观。

实现:

1.首先创建一个材质,不用赋值任何纹理,因为要用脚本来生成程序纹理,创建工具脚本ProceduralTextureGeneration.cs。

2.为了让该脚本可以在编辑器模式下运行,我们首先在类的开头添加:

[ExecuteInEditMode]
public class ProceduralTextureGeneration : MonoBehaviour {

3.声明一个材质,这个材质将使用该脚本中生成的程序纹理:

public Material material = null;

4.然后声明该程序纹理使用的各种参数:

#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

由于我们生成的纹理是由若干圆点构成的,因此在上面代码中声明了四个纹理属性:纹理的大小,通常是2的整数幂;纹理的背景颜色;圆点的颜色;模糊因子,这个参数是用来模糊圆形边界的。每个属性我们使用了get/set方法,为了在面板上修改属性时仍可以执行set函数,使用了一个开源插件SetProperty。这使得我们修改了材质属性时,可以执行_UpdateMaterial函数来使用新的属性重新生成程序纹理。

5.为了保存生成的程序纹理,声明一个Texture2D类型的纹理变量:

private Texture2D m_generatedTexture = null;

6.下面开始编写各个函数,首先需要在start函数中进行相应的检查,以得到需要使用该程序纹理的材质:

// Use this for initialization
	void Start () {
		if (material == null) {
			Renderer renderer = gameObject.GetComponent();
			if (renderer == null) {
				Debug.LogWarning("Cannot find a renderer.");
				return;
			}
 
			material = renderer.sharedMaterial;
		}
 
		_UpdateMaterial();
	}

如果material为空尝试从该物体上得到相应材质,检查完成后调用_UpdateMaterial函数来为其生成程序纹理。

7._UpdateMaterial函数代码:

private void _UpdateMaterial() {
		if (material != null) {
			m_generatedTexture = _GenerateProceduralTexture();
			material.SetTexture("_MainTex", m_generatedTexture);
		}
	}

调用_GenerateProceduralTexture()函数来生成一张程序纹理,并赋值给纹理变量最后赋值给材质中的纹理属性。

8._GenerateProceduralTexture()函数代码:

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);
 
		// The interval between circles
		float circleInterval = textureWidth / 4.0f;
		// The radius of circles
		float radius = textureWidth / 10.0f;
		// The blur factor
		float edgeBlur = 1.0f / blurFactor;
 
		for (int w = 0; w < textureWidth; w++) {
			for (int h = 0; h < textureWidth; h++) {
				// Initalize the pixel with background color
				Color pixel = backgroundColor;
 
				// Draw nine circles one by one
				for (int i = 0; i < 3; i++) {
					for (int j = 0; j < 3; j++) {
						// Compute the center of current circle
						Vector2 circleCenter = new Vector2(circleInterval * (i + 1), circleInterval * (j + 1));
 
						// Compute the distance between the pixel and the center
						float dist = Vector2.Distance(new Vector2(w, h), circleCenter) - radius;
 
						// Blur the edge of the circle
						Color color = _MixColor(circleColor, new Color(pixel.r, pixel.g, pixel.b, 0.0f), Mathf.SmoothStep(0f, 1.0f, dist * edgeBlur));
 
						// Mix the current color with the previous color
						pixel = _MixColor(pixel, color, color.a);
					}
				}
 
				proceduralTexture.SetPixel(w, h, pixel);
			}
		}
 
		proceduralTexture.Apply();
 
		return proceduralTexture;
	}

代码首先初始化一张二维纹理,并且提前计算了一些生成纹理时需要的变量。然后使用了一个两层的嵌套循环遍历纹理中的每个像素,并在纹理上依次绘制9个圆形,最后利用Texture2D.Apply函数来强制把像素值写入纹理中,并返回该程序纹理。后面可以调整脚本面板中的参数来得到不同的程序纹理了。

二、unity的程序材质

在unity中有一类专门使用程序纹理的材质,叫程序材质,它们本质上和那些普通材质是一样的,不同的是它们使用的纹理不是普通纹理,而是程序纹理。程序材质和它使用的程序纹理不是在unity中创建的,而是使用了一个名为Substance Designer的软件在unity外部生成的。

Substance Designer是一个非常出色的纹理生成工具,很多3A游戏项目都使用了它生成的材质,后缀是.sbsar。当把这些文件导入unity后会生成一个程序纹理资源,程序纹理资源可以包含一个或多个程序材质,每个程序纹理都使用了不同的纹理参数,因此unity为他们生成了不同的程序纹理。

程序材质的使用和普通材质一样,拖拽到模型上即可,程序纹理的强大之处在于它的多变性,我们可以通过调整程序纹理的属性来控制程序纹理的外观。程序材质的自由度很高,他可以和shader配合得当非常出色的视觉效果,是一种非常强大的材质类型。

本文转自 CSDN,为博主 小橙子0 原创文章,
遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cgy56191948/article/details/101641140

最新文章