Unity3d 人物換裝之 一個Shader處理3張圖片 減少DrawCall,

來源:互聯網
上載者:User

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 200CGPROGRAM#pragma surface surf Lambertsampler2D _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 initializationvoid 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 framevoid 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 200CGPROGRAM#pragma surface surf Lambertsampler2D _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 initializationvoid 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 framevoid Update () {}}

最後看運行結果,DrawCall減少到2 ,Character也完整的顯示出來了。



工程樣本下載:

http://pan.baidu.com/s/1o6ytCoU




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.