Unity3d 人物换装之 一个Shader处理3张图片 减少DrawCall

这里写的这个方法并不会提高效率。看看过程就好了,千万别用到项目中。


在上一篇

Unity3d人物换装之Mesh合并(材质合并)

中,我通过一个例子,将三个带有不同颜色 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




评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值