2013-03-09 11:23:47 minitail_ 阅读数 4797
• ###### 深入浅出Unity3D——第一篇

Unity3D基础知识、游戏算法一网打尽。

74825 人正在学习 去看看 何韬

DrawMeshLine类：

```using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class DrawMeshLine : MonoBehaviour
{
//线容器
List<Vector3> LinePointContainer;
//线宽
public float LineWidth = 0.5f;
//地形层
public int Layer = 8;
//线物体父物体
GameObject LineObj;

void Start ()
{
LinePointContainer = new List<Vector3>();
LineObj = new GameObject("Lines");
}

void Update ()
{
if(Input.GetMouseButtonDown(0))
{
Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray,out hit))
{
if(hit.transform.gameObject.layer != Layer)return;
}
if(LinePointContainer.Count == 1)
{
//二维初始面片圆
//				GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
//				sphere.transform.parent = LineObj.transform;
//				sphere.transform.position = hit.point;
//				sphere.transform.localScale = new Vector3(LineWidth * 2,0.01f,LineWidth * 2);
//				sphere.renderer.material.SetColor("_Color",new Color(255,0,0,255) / 255);

//三维初始球
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.parent = LineObj.transform;
sphere.transform.position = new Vector3(hit.point.x,hit.point.y + LineWidth,hit.point.z);
sphere.transform.localScale = new Vector3(LineWidth * Mathf.Sqrt(8.0f),LineWidth * Mathf.Sqrt(8.0f),LineWidth * Mathf.Sqrt(8.0f));
}
if(LinePointContainer.Count > 1)
{
//画二维面片线
//DrawLine2D(LinePointContainer[LinePointContainer.Count - 2],LinePointContainer[LinePointContainer.Count - 1]);
//画三维立体线
DrawLine3D(LinePointContainer[LinePointContainer.Count - 2],LinePointContainer[LinePointContainer.Count - 1]);
}
}
if(Input.GetMouseButtonDown(1))
{
//清空线容器
if(LinePointContainer.Count < 3)return;
DrawLine3D(LinePointContainer[LinePointContainer.Count - 1],LinePointContainer[0]);
LinePointContainer.Clear();
}
if(Input.GetKeyDown(KeyCode.Escape))
{
//清除所有线物体
LinePointContainer.Clear();
ClearLineObjects();
}
}

/// <summary>
/// 二维线
/// </summary>
/// <param name='PointA'>
/// 初始点
/// </param>
/// <param name='PointB'>
/// 结束点
/// </param>
void DrawLine2D(Vector3 PointA,Vector3 PointB)
{
float HorDisABx = PointB.x - PointA.x;
float HorDisABz = PointB.z - PointA.z;
float HorDisAB = Mathf.Sqrt(Mathf.Pow(HorDisABx,2) + Mathf.Pow(HorDisABz,2));

float offsetX = HorDisABz * LineWidth / HorDisAB;
float offsetZ = HorDisABx * LineWidth / HorDisAB;

Vector3 Point1 = new Vector3(PointA.x - offsetX,PointA.y,PointA.z + offsetZ);
Vector3 Point2 = new Vector3(PointA.x + offsetX,PointA.y,PointA.z - offsetZ);
Vector3 Point3 = new Vector3(PointB.x + offsetX,PointB.y,PointB.z - offsetZ);
Vector3 Point4 = new Vector3(PointB.x - offsetX,PointB.y,PointB.z + offsetZ);

//点位置 测试用
//		GameObject go1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
//		go1.transform.position = Point1;
//		GameObject go2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
//		go2.transform.position = Point2;
//		GameObject go3 = GameObject.CreatePrimitive(PrimitiveType.Cube);
//		go3.transform.position = Point3;
//		GameObject go4 = GameObject.CreatePrimitive(PrimitiveType.Cube);
//		go4.transform.position = Point4;

GameObject go = new GameObject((LinePointContainer.Count - 1).ToString());
go.transform.parent = LineObj.transform;
Mesh mesh = go.AddComponent<MeshFilter>().mesh;
mesh.vertices = new Vector3[]{Point1,Point2,Point3,Point4};
mesh.triangles = new int[]{2,1,0,0,3,2};

GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.parent = LineObj.transform;
sphere.transform.position = PointB;
sphere.transform.localScale = new Vector3(LineWidth * 2,0.01f,LineWidth * 2);
}

/// <summary>
/// 三维线
/// </summary>
/// <param name='PointA'>
/// 初始点
/// </param>
/// <param name='PointB'>
/// 结束点
/// </param>
void DrawLine3D(Vector3 PointA,Vector3 PointB)
{
float HorDisABx = PointB.x - PointA.x;
float HorDisABz = PointB.z - PointA.z;
float HorDisAB = Mathf.Sqrt(Mathf.Pow(HorDisABx,2) + Mathf.Pow(HorDisABz,2));

float offsetX = HorDisABz * LineWidth / HorDisAB;
float offsetZ = HorDisABx * LineWidth / HorDisAB;

Vector3 Point1 = new Vector3(PointA.x - offsetX,PointA.y,PointA.z + offsetZ);
Vector3 Point2 = new Vector3(PointA.x + offsetX,PointA.y,PointA.z - offsetZ);
Vector3 Point3 = new Vector3(PointB.x + offsetX,PointB.y,PointB.z - offsetZ);
Vector3 Point4 = new Vector3(PointB.x - offsetX,PointB.y,PointB.z + offsetZ);

GameObject go1 = new GameObject((LinePointContainer.Count - 1).ToString() + "_1");
go1.transform.parent = LineObj.transform;
Mesh mesh1 = go1.AddComponent<MeshFilter>().mesh;
mesh1.vertices = new Vector3[]{Point1,Point2,Point3,Point4};
mesh1.triangles = new int[]{2,1,0,0,3,2};

Vector3 Point5 = new Vector3(PointA.x - offsetX,PointA.y + 2 * LineWidth,PointA.z + offsetZ);
Vector3 Point6 = new Vector3(PointA.x + offsetX,PointA.y + 2 * LineWidth,PointA.z - offsetZ);
Vector3 Point7 = new Vector3(PointB.x + offsetX,PointB.y + 2 * LineWidth,PointB.z - offsetZ);
Vector3 Point8 = new Vector3(PointB.x - offsetX,PointB.y + 2 * LineWidth,PointB.z + offsetZ);

GameObject go2 = new GameObject((LinePointContainer.Count - 1).ToString() + "_2");
go2.transform.parent = LineObj.transform;
Mesh mesh2 = go2.AddComponent<MeshFilter>().mesh;
mesh2.vertices = new Vector3[]{Point5,Point6,Point7,Point8};
mesh2.triangles = new int[]{2,1,0,0,3,2};

GameObject go3 = new GameObject((LinePointContainer.Count - 1).ToString() + "_3");
go3.transform.parent = LineObj.transform;
Mesh mesh3 = go3.AddComponent<MeshFilter>().mesh;
mesh3.vertices = new Vector3[]{Point6,Point2,Point3,Point7};
mesh3.triangles = new int[]{2,1,0,0,3,2};

GameObject go4 = new GameObject((LinePointContainer.Count - 1).ToString() + "_4");
go4.transform.parent = LineObj.transform;
Mesh mesh4 = go4.AddComponent<MeshFilter>().mesh;
mesh4.vertices = new Vector3[]{Point1,Point5,Point8,Point4};
mesh4.triangles = new int[]{2,1,0,0,3,2};

GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.parent = LineObj.transform;
sphere.transform.position = new Vector3(PointB.x,PointB.y + LineWidth,PointB.z);
sphere.transform.localScale = new Vector3(LineWidth * Mathf.Sqrt(8.0f),LineWidth * Mathf.Sqrt(8.0f),LineWidth * Mathf.Sqrt(8.0f));
}

void ClearLineObjects ()
{
for(int i = 0 ; i < LineObj.transform.childCount;i ++)
{
GameObject go = LineObj.transform.GetChild(i).gameObject;
Destroy(go);
}
}
}
```

unity3d 设备上模型变黑 相关内容

2015-01-22 15:00:45 wolf96 阅读数 11134
• ###### 深入浅出Unity3D——第一篇

Unity3D基础知识、游戏算法一网打尽。

74825 人正在学习 去看看 何韬

1.    描边

2.    着色

# 就从最初的描边开始

_Outline挤出描边的粗细
_Factor挤出多远

```Properties {
_Color("Main Color",color)=(1,1,1,1)
_Outline("Thick of Outline",range(0,0.1))=0.02
_Factor("Factor",range(0,1))=0.5
}```

Cull Front 裁剪了物体的前面（对着相机的），把背面挤出
ZWrite On 像素的深度写入深度缓冲，如果关闭的话，物体与物体交叠处将不会被描边，因为此处无z值后渲染的物体会把此处挤出的描边“盖住”

dir=normalize(v.vertex.xyz);

float3 dir2=v.normal;

dir2为法线方向

D=dot(dir,dir2);
D为计算该点位置朝向和法线方向的点积，通过正负值可以确定是指向还是背离几何中心的，正为背离，负为指向

dir=dir*sign(D);

dir=dir*_Factor+dir2*(1-_Factor);

v.vertex.xyz+=dir*_Outline;

```		v2f vert (appdata_full v) {
v2f o;
float3 dir=normalize(v.vertex.xyz);
float3 dir2=v.normal;
float D=dot(dir,dir2);
dir=dir*sign(D);
dir=dir*_Factor+dir2*(1-_Factor);
v.vertex.xyz+=dir*_Outline;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
return o;
}```

```Shader "Tut/Shader/Toon/miaobian" {
Properties {
_Color("Main Color",color)=(1,1,1,1)
_Outline("Thick of Outline",range(0,0.1))=0.02
_Factor("Factor",range(0,1))=0.5
}
pass{
Tags{"LightMode"="Always"}
Cull Front
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
float _Factor;
float4 _Color;
struct v2f {
float4 pos:SV_POSITION;
};

v2f vert (appdata_full v) {
v2f o;
float3 dir=normalize(v.vertex.xyz);
float3 dir2=v.normal;
float D=dot(dir,dir2);
dir=dir*sign(D);
dir=dir*_Factor+dir2*(1-_Factor);
v.vertex.xyz+=dir*_Outline;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
return o;
}
float4 frag(v2f i):COLOR
{
float4 c = _Color / 5;
return c;
}
ENDCG
}
pass{
Tags{"LightMode"="ForwardBase"}
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

float4 _LightColor0;
float4 _Color;
float _Steps;
float _ToonEffect;

struct v2f {
float4 pos:SV_POSITION;
float3 lightDir:TEXCOORD0;
float3 viewDir:TEXCOORD1;
float3 normal:TEXCOORD2;
};

v2f vert (appdata_full v) {
v2f o;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.normal=v.normal;
o.lightDir=ObjSpaceLightDir(v.vertex);
o.viewDir=ObjSpaceViewDir(v.vertex);

return o;
}
float4 frag(v2f i):COLOR
{
float4 c=1;
float3 N=normalize(i.normal);
float3 viewDir=normalize(i.viewDir);
float3 lightDir=normalize(i.lightDir);
float diff=max(0,dot(N,i.lightDir));
diff=(diff+1)/2;
diff=smoothstep(0,1,diff);
c=_Color*_LightColor0*(diff);
return c;
}
ENDCG
}//
}
}
```

# 开始卡通着色旅程

## 简单的举几个例子

《深渊传说》是传说系列的早期作品之一，用的也是卡通渲染

《仙乐传说》的战斗结束画面

《无尽传说2》，是传说系列比较近的作品，画面明显比前做好了许多，但万变不离其宗，还是用的卡通着色，（= =没玩过这作）

## 开始动手操刀卡通着色

_Color物体的颜色
_Outline挤出描边的粗细
_Factor挤出多远
_ToonEffect卡通化程度（二次元与三次元的交界线）
_Steps色阶层数

```	Properties {
_Color("Main Color",color)=(1,1,1,1)//物体的颜色
_Outline("Thick of Outline",range(0,0.1))=0.02//挤出描边的粗细
_Factor("Factor",range(0,1))=0.5//挤出多远
_ToonEffect("Toon Effect",range(0,1))=0.5//卡通化程度（二次元与三次元的交界线）
_Steps("Steps of toon",range(0,9))=3//色阶层数
}```

float diff=max(0,dot(N,i.lightDir));

diff=(diff+1)/2;

diff=smoothstep(0,1,diff);

float toon=floor(diff*_Steps)/_Steps;

diff=lerp(diff,toon,_ToonEffect);

c=_Color*_LightColor0*(diff);

```		float4 frag(v2f i):COLOR
{
float4 c=1;
float3 N=normalize(i.normal);
float3 viewDir=normalize(i.viewDir);
float3 lightDir=normalize(i.lightDir);
float diff=max(0,dot(N,i.lightDir));//求出正常的漫反射颜色
diff=(diff+1)/2;//做亮化处理
diff=smoothstep(0,1,diff);//使颜色平滑的在[0,1]范围之内
float toon=floor(diff*_Steps)/_Steps;//把颜色做离散化处理，把diffuse颜色限制在_Steps种（_Steps阶颜色），简化颜色，这样的处理使色阶间能平滑的显示
diff=lerp(diff,toon,_ToonEffect);//根据外部我们可控的卡通化程度值_ToonEffect，调节卡通与现实的比重

c=_Color*_LightColor0*(diff);//把最终颜色混合
return c;
}```

float atten=1/(dist);

half3 h = normalize (lightDir + viewDir);

float nh = max (0, dot (N, h));
float spec = pow (nh, 32.0);

float toonSpec=floor(spec*atten*2)/ 2;

spec=lerp(spec,toonSpec,_ToonEffect);

```		float4 frag(v2f i):COLOR
{
float4 c=1;
float3 N=normalize(i.normal);
float3 viewDir=normalize(i.viewDir);
float dist=length(i.lightDir);//求出距离光源的距离
float3 lightDir=normalize(i.lightDir);
float diff=max(0,dot(N,i.lightDir));
diff=(diff+1)/2;
diff=smoothstep(0,1,diff);
float atten=1/(dist);//根据距光源的距离求出衰减
float toon=floor(diff*atten*_Steps)/_Steps;
diff=lerp(diff,toon,_ToonEffect);

half3 h = normalize (lightDir + viewDir);//求出半角向量
float nh = max (0, dot (N, h));
float spec = pow (nh, 32.0);//求出高光强度
float toonSpec=floor(spec*atten*2)/ 2;//把高光也离散化
spec=lerp(spec,toonSpec,_ToonEffect);//调节卡通与现实高光的比重

c=_Color*_LightColor0*(diff+spec);//求出最终颜色
return c;
}```

```Shader "Tut/Shader/Toon/toon" {
Properties {
_Color("Main Color",color)=(1,1,1,1)//物体的颜色
_Outline("Thick of Outline",range(0,0.1))=0.02//挤出描边的粗细
_Factor("Factor",range(0,1))=0.5//挤出多远
_ToonEffect("Toon Effect",range(0,1))=0.5//卡通化程度（二次元与三次元的交界线）
_Steps("Steps of toon",range(0,9))=3//色阶层数
}
pass{//处理光照前的pass渲染
Tags{"LightMode"="Always"}
Cull Front
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
float _Factor;
struct v2f {
float4 pos:SV_POSITION;
};

v2f vert (appdata_full v) {
v2f o;
float3 dir=normalize(v.vertex.xyz);
float3 dir2=v.normal;
float D=dot(dir,dir2);
dir=dir*sign(D);
dir=dir*_Factor+dir2*(1-_Factor);
v.vertex.xyz+=dir*_Outline;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
return o;
}
float4 frag(v2f i):COLOR
{
float4 c=0;
return c;
}
ENDCG
}//end of pass
pass{//平行光的的pass渲染
Tags{"LightMode"="ForwardBase"}
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

float4 _LightColor0;
float4 _Color;
float _Steps;
float _ToonEffect;

struct v2f {
float4 pos:SV_POSITION;
float3 lightDir:TEXCOORD0;
float3 viewDir:TEXCOORD1;
float3 normal:TEXCOORD2;
};

v2f vert (appdata_full v) {
v2f o;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);//切换到世界坐标
o.normal=v.normal;
o.lightDir=ObjSpaceLightDir(v.vertex);
o.viewDir=ObjSpaceViewDir(v.vertex);

return o;
}
float4 frag(v2f i):COLOR
{
float4 c=1;
float3 N=normalize(i.normal);
float3 viewDir=normalize(i.viewDir);
float3 lightDir=normalize(i.lightDir);
float diff=max(0,dot(N,i.lightDir));//求出正常的漫反射颜色
diff=(diff+1)/2;//做亮化处理
diff=smoothstep(0,1,diff);//使颜色平滑的在[0,1]范围之内
float toon=floor(diff*_Steps)/_Steps;//把颜色做离散化处理，把diffuse颜色限制在_Steps种（_Steps阶颜色），简化颜色，这样的处理使色阶间能平滑的显示
diff=lerp(diff,toon,_ToonEffect);//根据外部我们可控的卡通化程度值_ToonEffect，调节卡通与现实的比重

c=_Color*_LightColor0*(diff);//把最终颜色混合
return c;
}
ENDCG
}//
pass{//附加点光源的pass渲染
Blend One One
Cull Back
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

float4 _LightColor0;
float4 _Color;
float _Steps;
float _ToonEffect;

struct v2f {
float4 pos:SV_POSITION;
float3 lightDir:TEXCOORD0;
float3 viewDir:TEXCOORD1;
float3 normal:TEXCOORD2;
};

v2f vert (appdata_full v) {
v2f o;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.normal=v.normal;
o.viewDir=ObjSpaceViewDir(v.vertex);
o.lightDir=_WorldSpaceLightPos0-v.vertex;

return o;
}
float4 frag(v2f i):COLOR
{
float4 c=1;
float3 N=normalize(i.normal);
float3 viewDir=normalize(i.viewDir);
float dist=length(i.lightDir);//求出距离光源的距离
float3 lightDir=normalize(i.lightDir);
float diff=max(0,dot(N,i.lightDir));
diff=(diff+1)/2;
diff=smoothstep(0,1,diff);
float atten=1/(dist);//根据距光源的距离求出衰减
float toon=floor(diff*atten*_Steps)/_Steps;
diff=lerp(diff,toon,_ToonEffect);

half3 h = normalize (lightDir + viewDir);//求出半角向量
float nh = max (0, dot (N, h));
float spec = pow (nh, 32.0);//求出高光强度
float toonSpec=floor(spec*atten*2)/ 2;//把高光也离散化
spec=lerp(spec,toonSpec,_ToonEffect);//调节卡通与现实高光的比重

c=_Color*_LightColor0*(diff+spec);//求出最终颜色
return c;
}
ENDCG
}//
}
}
```

_RimPower边缘光亮度程度

_ToonRimStep边缘光色阶数

float rim = 1.0 - saturate(dot(N,normalize (viewDir)));

rim = rim+1;

rim = pow(rim, _RimPower);

float toonRim = floor(rim * _ToonRimStep) / _ToonRimStep;

rim = lerp(rim, toonRim, _ToonEffect);

c=_Color*_LightColor0*(diff) * rim * mc*2;

```		float4 frag(v2f i):COLOR
{
half4 mc = tex2D (_MainTex, i.uv_MainTex);
float4 c=1;
float3 N=normalize(i.normal);
float3 viewDir=normalize(i.viewDir);
float3 lightDir=normalize(i.lightDir);
float diff=max(0,dot(N,i.lightDir));
diff=(diff+1)/2;
diff=smoothstep(0,1,diff);
float toon=floor(diff*_Steps)/_Steps;
diff=lerp(diff,toon,_ToonEffect);
float rim = 1.0 - saturate(dot(N, normalize (viewDir)));//求出正常的边缘光程度
rim = rim+1;//使之加亮
rim = pow(rim, _RimPower);//外部变量_RimPower控制边缘光亮度大小
float toonRim = floor(rim * _ToonRimStep) / _ToonRimStep;//再对边缘光进行离散化
rim = lerp(rim, toonRim, _ToonEffect);//调节卡通与现实的比重
c=_Color*_LightColor0*(diff) * rim * mc*2;//进行最终颜色混合
return c;
}```

-----------------------by wolf96http://blog.csdn.net/wolf96

unity3d 设备上模型变黑 相关内容

2015-11-23 18:59:56 chenghai37 阅读数 2843
• ###### 深入浅出Unity3D——第一篇

Unity3D基础知识、游戏算法一网打尽。

74825 人正在学习 去看看 何韬
3Dmax建模必须注意的细节。（注意！！是必须！！）可以简单参考我做的插头的命名和模型的各个物体的塌陷程度等~

1模型结构要正确
2尽量不要出现破面黑面
3附加物体不要出现穿插。
4拼装位置一定要准确

1.由于Unity看不到单面模型的反面，故导出请仔细检查单面物体。如玻璃.
2.材质、贴图、模型名称中不能出现中文。进unity，不要出现中文名，以免出现乱码，报错。
3.unity只支持3dmax的标准材质（Standard），贴图通道也只支持位图（Bitmap）和自发光。（所以什么vary材质什么的就不要用了。）
4.max默认的uv平铺在unity里是出错的，要加uvw贴图修改器去调整。
5.相同材质的模型用同一个材质，以减轻系统与后期材质制作负担。
6.可以塌陷的模型尽量塌陷。（就是将每个做好的模型转换成可编辑多边形）
7.单个模型四边面数不能超过3万。极限是三万，大概在两万五就可以。（经过我的实际测试单个模型点、面最多不能超过65534，否则unity会自动将单个物体进行切片处理以达到unity的自身存储的要求，我猜测是unity软件编写的时候用的是int类型来存储每个模型的数据，int类型正好是2^32，最大的范围是65536，去掉2个正负号正好65534）
8.工具轴心要统一.将工具对好要拆卸的物体，然后调整物体轴心，确保物体工具轴心统一。（这个非常重要，unity中的轴心完全依靠建模软件，所以每个物体的轴心都需要在3dmax中与物体校准！）
9.物体导入unity报错？
1框选整个模型，看看是否要重复的点，焊接点。
2多个角度渲染看看，模型有无黑面，有黑面就会导致出错，布尔过的物体最容易出错，线不够，就会导致边塌到里面去。布尔过的物体要塌陷成多边形，其它加修改器的物体也要塌陷成多边形。
3检查模型的布线，布线不规整，或是布线的点，一条边上的点在边上，另外一个跑到很远或是插到模型的其他部位，都会导致出错。
4烘焙过的物体材质你要重新上一个。采用法线贴图烘焙过的物体，模型本身虽然没有什么问题，但是你导出时也是会报错的。

unity3d 设备上模型变黑 相关内容

2018-05-06 23:02:47 moonlightpeng 阅读数 5399
• ###### 深入浅出Unity3D——第一篇

Unity3D基础知识、游戏算法一网打尽。

74825 人正在学习 去看看 何韬

1，导入后缩小变黑。

2，但修改比例会正常。

unity3d 设备上模型变黑 相关内容

2015-04-10 23:53:48 dbtxdxy 阅读数 5127
• ###### 深入浅出Unity3D——第一篇

Unity3D基础知识、游戏算法一网打尽。

74825 人正在学习 去看看 何韬

现实生活中有许多半透明的情形：玻璃、饮料瓶、塑料袋等等。半透明一个很重要的特点就是可以显示它后面的物体的部分颜色。这个“部分”具体是多少我们常用透明度来表示。

## 一个简单的透明例子

想做出透明效果，第一步需要通知Shader我们要使用透明效果了。最简单的方法是在#pragma后面增加一个alpha参数，例如#pragma surface surf Lambert alpha。

``````Shader "Custom/SimpleAlpha" {
Properties {
_MainColor ("Main Color", Color) = (1, 1, 1, 1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_Alpha ("Alpha", Range(0, 1)) = 0.5
}
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Lambert alpha

fixed4 _MainColor;
sampler2D _MainTex;
fixed _Alpha;

struct Input {
float2 uv_MainTex;
};

void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = _MainColor.rgb * c.rgb;
o.Alpha = _Alpha;
}
ENDCG
}
FallBack "Diffuse"
}``````

调整Alpha值，可以看到透明的效果：

为了更好地观察透明效果，我们可以在立方体后面添加一个红色的平面。这时候，你可能发现一个奇怪的现象，立方体明明在平面的前面，但是看上去却被平面遮挡了，像这样：

``Tags { "Queue" = "Geometry" "RenderType"="Opaque" }``

``Tags { "Queue" = "Geometry+1" "RenderType"="Opaque" }``

此时渲染顺序就正确了：

其实Unity已经给我们定义了专门放置透明材质的层次，我们干嘛不用呢，所以最好的方法是这样写：

``Tags { "Queue" = "Transparent" "RenderType"="Opaque" }``

## 使用AlphaTest实现透明效果

另一种实现透明的方式是利用裁剪。这种方式的基本原理就是利用片段的透明度和我们给定的一个透明度做对比，当片段的透明度大于给定透明度时就表现为不透明，否则就是透明(其实是被丢掉)。因此这种方式作出的效果除了完全透明的部分就是完全不透明的部分。

要使用AlphaTest，需要在#pragma后面添加alphatest:xxx，这个xxx就是我们给定的透明度。下面是一个简单的AlphaTest的例子：

``````Shader "Custom/AlphaTest" {
Properties {
_MainColor ("Main Color", Color) = (1, 1, 1, 1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_TestVal ("Test Value", Range(0, 1)) = 0.5
}
Tags { "Queue" = "Transparent" "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Lambert alphatest:_TestVal

fixed4 _MainColor;
sampler2D _MainTex;

struct Input {
float2 uv_MainTex;
};

void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = _MainColor.rgb * c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}``````

为了体现效果我们需要一幅透明度渐变的贴图：

下面是_AlphaTest在不同值下的效果：

这种AlphaTest看上去比半透明的方式更有效率，事实上，大部分情况确实如此。一个例外是在移动设备上，AlphaTest在移动设备上效率非常低，甚至不如第一种半透明的方式。

本次的工程文件在这里

## 结束语

透明材质有非常好的表现效果，可以说在游戏开发过程中是必不可少的，恰当的使用透明也可以极大地节省模型的面数。但是，使用透明会有一定的性能开销，尤其是在移动设备上。可以说，开发移动平台的游戏，资源和效率都是锱铢必较的，开发一款效果又好效率又高的产品真的不容易。OK，下回再见~

unity3d 设备上模型变黑 相关内容