第九章 高级纹理(4)
@
程序纹理
程序纹理(Procedural Texture)指的是那些由计算机生成的图像,我们通常使用一些特定的算法来创建个性化图案或非常真实的自然元素,例如木头、石子等。使用程序纹理的好处在于我们可以使用各种参数来控制纹理的外观,而这些属性不仅仅是那些颜色属性,甚至可以是完全不同类型的图案属性,这是我们可以得到更加丰富的动画和视觉效果。在本节中,我们首先尝试用算法来实现一个非常简单的程序材质。然后,我们会介绍Unity里一类专门使用程序纹理的材质——程序材质。
1. 在Unity中实现简单的程序纹理
在这一节里,我们会使用一个简单的算法来生成一个波点纹理,如下图所示。
我们可以在脚本里调整一些参数,如背景颜色、波点颜色等,以控制最终生成的纹理外观。
(1)新建一个立方体,将一个材质赋予它,但是在这个材质我们并没有为其赋予任何纹理,这是因为我们想要用脚本创建程序纹理。
在本节里,我们将会使用脚本创建一个程序纹理。
(2)为了能让改脚本能够在编辑器模式下运行,我们首先在类的开头添加如下的代码:
[ExecuteInEditMode]
public class ProceduralTextureGeneration:MomoBehaviour:{
}
(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 m_backgroundColor{
get{
return m_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
region和#endregion仅仅是为了组织代码,并没有其他作用。由于我们生成的纹理是由若干圆点构成的,因此在上面的代码中,我们声明了4个纹理属性:纹理的大小,数值通常是2的整数幂;纹理的背景颜色;圆点的颜色;模糊因子,这个参数是用来模糊圆形边界的。注意到,对于每个属性我们使用了get/set的方法,为了在面板上修改属性时仍可以执行set函数,我们使用了一个开源插件SetProperty。这使得我们当修改了材质属性时,可以执行_UpdateMaterial函数来使用新的属性重新生成程序纹理。
(5)为了保存生成的程序纹理,我们声明一个Texture2D类型的纹理变量:
private Texture2D m_generatedTexture=null;
(6)下面开始编写各个函数。首先,我们需要在Start函数中进行相应的检查,以得到需要使用该程序纹理的材质:
void Start(){
if (material==null){
Render render = gameObject.GetComponent<Renderer>();
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_genegratedTexture=_GenerateProceduralTexture();
material.SetTexture("_MainTex",m_generatedTexture);
}
}
它确保material不为空,然后调用_GenerateProceduralTexture函数来生成一张程序纹理,并赋给
(8)_GenerateProceduralTexture函数的代码如下:
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;
代码首先初始化一张二维纹理,并且提前计算了一些生成纹理时所需要的变量。然后使用了一个两层的嵌套循环遍历纹理中的每个像素,并且一次在纹理上绘制了9个圆形。最后调用了Texture2D.Apply函数强制把像素值写入纹理中,并返回该程序纹理。
保存脚本后返回纹理,调整相应的参数后可以得到类似下图的效果。我们可以调整脚本面板中的材质参数来得到不同的程序纹理。如下图所示:
至此,我们已经学会如何通过脚本来创建一个程序纹理,再赋给相应的材质了。
2. Unity的程序材质
在Unity中,有一类专门使用程序纹理的材质,叫做程序材质(Procedural Material)。这类材质和我们之前使用的那些材质本质上是一样的,不同的是它们使用的纹理不是普通的纹理,而是程序纹理。需要注意的是,程序材质和它使用的程序纹理并不是在Unity中创建的,而是使用一个名为Substance Designer的软件在Unity外部生成的。
Substance Designer是一个非常出色的纹理生成工具,很多3A游戏项目都使用了由它生成的材质。我们可以从Unity的资源商店或网络中获取到很多免费或付费的Substance材质。这些材质都是以.sbar为后缀的。如下图所示,我们可以直接把这些材质向其他资源一样拖入Unity项目中。
当把这些文件导入Unity后,Unity就会生成一个程序纹理资源(Procedural Material Asset)。程序纹理资源可以包含一个或多个程序材质,例如下图中就包含了两个程序纹理——Cereals和Cereals_1,每个程序纹理都是用了不同的程序纹理参数,因此Unity为它们生成了不同的程序纹理,例如Cereals_Diffuse和Cereals_1_Diffuse等。
通过单击程序材质,我们可以在程序纹理的面板上看到该材质使用的UnityShader及其属性、生成程序纹理使用的纹理属性、材质预览信息等。
程序材质的使用和普通材质是一样的,我们把它们拖拽到相应的模型上即可。程序纹理的强大之处很大原因在于它的多变性,我们可以通过调整程序纹理的属性来控制纹理的外观,甚至可以生成看似完全不同的纹理。下图给出了调整Cereals程序材质的不同纹理属性得到的不同材质效果。
可以看出,程序材质的自由度很高,而且可以和Shader配合得到非常出色的视觉效果,它是一种非常强大的材质类型。