这里写的这个方法并不会提高效率。看看过程就好了,千万别用到项目中。
在上一篇
中,我通过一个例子,将三个带有不同颜色 RGB的立方体,合并Mesh和材质到Character这一个GameObject中。这样原本对3个GameObject的操作只需要对Character这一个GameObject进行操作就好了。但是我们的任务还没有完成。
合并之前的游戏:
合并之后的游戏:
大家注意看合并之前和合并之后,虽然GameObject数量减少了,但是DrawCall一个都没有减少哦!之前是4个,合并之后仍然是4个。
简单的来说呢,就是一个材质球,一个DrawCall。也就是说呢,一个Shader,一个DrawCall。
既然知道了一个Shader一个DrawCall,那我们就开始着手去处理,把红、绿、蓝这三张图片,在一个Shader中进行处理,只使用一个材质球,这样就只有1个DrawCall了。
我们来创建一个Shader,就叫CombineShader吧,在默认的Shader代码基础上,删掉MainTex这个纹理,添加我们自己的三个纹理:_Red 、_Green 、_Blue .
Shader "Custom/CombineShader" {
Properties {
_Red ("Base (RGB)", 2D) = "white" {}
_Green ("Base (RGB)", 2D) = "white" {}
_Blue ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _Red;
sampler2D _Green;
sampler2D _Blue;
struct Input {
float2 uv_RedTex;
float2 uv_GreenTex;
float2 uv_BlueTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_Red, IN.uv_RedTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
在上面的未完成的Shader中,我取了_Red 的纹理来做取样。我们接着修改脚本代码,使合并之后的GameObject Character使用CombineShader创建的材质。
using UnityEngine;
using System.Collections;
public class NewBehaviourScript : MonoBehaviour {
// Use this for initialization
void Start () {
//获取纹理;
Texture redTex=transform.Find("CubeRed").GetComponent<MeshRenderer>().sharedMaterial.mainTexture;
Texture greenTex=transform.Find("CubeGreen").GetComponent<MeshRenderer>().sharedMaterial.mainTexture;
Texture blueTex=transform.Find("CubeBlue").GetComponent<MeshRenderer>().sharedMaterial.mainTexture;
//合并材质;
Shader combineShader = Shader.Find("Custom/CombineShader");
Material combineMaterial = new Material(combineShader);
combineMaterial.SetTexture("_Red", redTex);
combineMaterial.SetTexture("_Green", greenTex);
combineMaterial.SetTexture("_Blue", blueTex);
//合并Mesh;
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
CombineInstance[] combine = new CombineInstance[meshFilters.Length];
for (int i = 0; i < meshFilters.Length;i++ )
{
combine[i].mesh = meshFilters[i].sharedMesh;
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
meshFilters[i].gameObject.SetActive(false);
}
transform.gameObject.AddComponent<MeshRenderer>();
transform.gameObject.AddComponent<MeshFilter>();
transform.GetComponent<MeshFilter>().mesh = new Mesh();
transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine, false);
transform.gameObject.SetActive(true);
//设置材质;
transform.GetComponent<MeshRenderer>().sharedMaterial = combineMaterial;
}
// Update is called once per frame
void Update () {
}
}
运行之后能看到,在Character这个GameObject使用的材质球中,需要输入三张纹理图片。
现在再看,DrawCall数量已经降到2了,也就是说,合并之后 由原来的3个DrawCall 降到了 1个DrawCall。
但是还是有问题呢,为什么只显示一个立方体,哈哈,是我们代码写错了。
transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine, false);
应该改为
transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine, true);
好了,这次可以显示了,但是为什么没有贴图?
为什么没有贴图?因为我们只是在Unity中设置了贴图,但是在Shader中还没有去使用它们。
将Shader修改如下:
Shader "Custom/CombineShader" {
Properties {
_Red ("Base (RGB)", 2D) = "white" {}
_Green ("Base (RGB)", 2D) = "white" {}
_Blue ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _Red;
sampler2D _Green;
sampler2D _Blue;
struct Input {
float2 uv_RedTex;
float2 uv_GreenTex;
float2 uv_BlueTex;
float4 color:COLOR;
};
void surf (Input IN, inout SurfaceOutput o)
{
half4 colorIn;
if(IN.color.a<0.33)
{
colorIn=tex2D(_Red,IN.uv_RedTex);
}
else if(IN.color.a<0.6)
{
colorIn=tex2D(_Green,IN.uv_GreenTex);
}
else
{
colorIn=tex2D(_Blue,IN.uv_BlueTex);
}
o.Albedo=colorIn.rgb;
o.Alpha=colorIn.a;
}
ENDCG
}
FallBack "Diffuse"
}
我们看到在surf 中对顶点颜色的Alpha值进行了判断处理,这是利用顶点色Color的属性,在代码中进行赋值,来区分当前顶点原来是属于哪一个立方体的。比如说color.a是0,那么原来就属于红色立方体,就给它从红色纹理来取样。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class NewBehaviourScript : MonoBehaviour {
// Use this for initialization
void Start () {
//获取纹理;
Texture redTex=transform.Find("CubeRed").GetComponent<MeshRenderer>().sharedMaterial.mainTexture;
Texture greenTex=transform.Find("CubeGreen").GetComponent<MeshRenderer>().sharedMaterial.mainTexture;
Texture blueTex=transform.Find("CubeBlue").GetComponent<MeshRenderer>().sharedMaterial.mainTexture;
//合并材质;
Shader combineShader = Shader.Find("Custom/CombineShader");
Material combineMaterial = new Material(combineShader);
combineMaterial.SetTexture("_Red", redTex);
combineMaterial.SetTexture("_Green", greenTex);
combineMaterial.SetTexture("_Blue", blueTex);
//合并Mesh;
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
CombineInstance[] combine = new CombineInstance[meshFilters.Length];
List<Color> combineMeshColor=new List<Color>(); //Combine之后Mesh的Color;
for (int i = 0; i < meshFilters.Length;i++ )
{
combine[i].mesh = meshFilters[i].sharedMesh;
//处理顶点位置;
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
//处理顶点颜色;
Vector2[] UVArray = meshFilters[i].sharedMesh.uv;
float bodyPart = i / (float)meshFilters.Length;
for (int uvindex = 0; uvindex < UVArray.Length; uvindex++)
{
combineMeshColor.Add(new Color(bodyPart, bodyPart, bodyPart, bodyPart));
}
meshFilters[i].gameObject.SetActive(false);
}
Mesh combineMesh = new Mesh();
combineMesh.CombineMeshes(combine, true);
combineMesh.colors = combineMeshColor.ToArray();
combineMesh.name = gameObject.name;
transform.gameObject.AddComponent<MeshRenderer>();
transform.gameObject.AddComponent<MeshFilter>();
transform.gameObject.GetComponent<MeshFilter>().sharedMesh = combineMesh;
//设置材质;
transform.GetComponent<MeshRenderer>().material = combineMaterial;
transform.gameObject.SetActive(true);
}
// Update is called once per frame
void Update () {
}
}
最后看运行结果,DrawCall减少到2 ,Character也完整的显示出来了。
工程示例下载:
http://pan.baidu.com/s/1o6ytCoU