Unity3d 人物换装之 一个Shader处理3张图片 减少DrawCall
2015-03-24 00:51
411 查看
这里写的这个方法并不会提高效率。看看过程就好了,千万别用到项目中。
在上一篇
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
在上一篇
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
相关文章推荐
- [Unity3D]Shader学习笔记之Shader与DrawCall
- 一个关于图片处理的代码
- 一个ArcGIS Javascript API的BUG和处理办法 - 调用两次Draw工具条deactivate方法导致的错误
- android滑动一个路线后 人物图片按此路线移动的实现
- Unity3D Shader官方教程翻译(九)----Shader语法:Pass的雾化处理
- Unity3D Shader官方教程翻译(八)----Shader语法:Pass的纹理处理
- 一个VB.NET写的简单图片缩放处理组件源代码,支持添加半透明效果小图标
- 自己封装的一个Java版图片工具,具备压缩,伸缩变换,透明处理,格式转换等功能.
- Android在处理图片减少出现OOM的方式
- 【DFB】IDirectFBInputDevice 方式处理事件 --键盘移动一个图片显示
- Unity3D Shader官方教程翻译(九)----Shader语法:Pass的雾化处理
- 一个ArcGIS Javascript API的BUG和处理办法 - 调用两次Draw工具条deactivate方法导致的错误
- imagedraw4.0 图片处理控件破解版
- c#怎么把一个彩色的图片处理成灰色的呢。
- Unity3D优化之Draw Call Batching
- 一个简单的在页面处理图片大小的方法
- 一个不错的PHP图片处理类
- 图片缩放处理的一个示例
- android滑动一个路线后 人物图片按此路线移动的实现
- svg engine 一个处理矢量图片的开源项目