Unity3D学习(八):《Unity Shader入门精要》——透明效果
深度缓冲(Depth Buffer)
透明度混合时应关闭深度写入(ZWrite Off)
如果不关闭深度写入,一个半透明表面背后的表面本就是透过它被我们看到的,但由于深度测试时判断结果是该半透明表面)距离摄像机更近,导致后面的表面会被剔除掉,也就无法通过半透明面观察到后面的物体。
另外注意关闭深度写入后要考虑物体的渲染顺序。如下图,一个半透明物体A和一个不透明物体B,B在A后方,如果先渲染A再渲染B会出现A被B遮挡的情况,这是错误的。因此渲染顺序在关闭深度写入的情况下极为重要。

为了保证渲染顺序正确,渲染引擎一般会对物体进行排序,再渲染,常用的方法是
- 1)先渲染所有不透明物体,并开启它们的深度测试和深度写入。
- 2)把半透明物体按他们离摄像机的远近进行排序,然后按照从后往前的顺序渲染透明物体,并开启它们的深度测试,但关闭深度写入。
但这种方法无法解决物体重叠的情况,因此需要额外的解决方案,比如分割网格等。但是也可以试着让透明通道更柔和,是穿插重叠看起来不那么明显。
Unity的渲染顺序
Unity通过一组Queue标签来决定模型归于哪个渲染队列,队列由整数索引表示,索引号越小越先被渲染。
| 渲染队列 | 渲染队列描述 | 渲染队列值 |
| Background | 这个队列被最先渲染。它被用于skyboxes等。 | 1000 |
| Geometry | 这是默认的渲染队列。它被用于绝大多数对象。不透明几何体使用该队列。 | 2000 |
| AlphaTest | 通道检查的几何体使用该队列。它和Geometry队列不同,对于在所有立体物体绘制后渲染的通道检查的对象,它更有效。 | 2450 |
| Transparent | 该渲染队列在Geometry和AlphaTest队列后被渲染。任何通道混合的(也就是说,那些不写入深度缓存的Shaders)对象使用该队列,例如玻璃和粒子效果。 | 3000 |
| Overlay | 该渲染队列是为叠加效果服务的。任何最后被渲染的对象使用该队列,例如镜头光晕。 | 4000 |
透明度测试
只要有一个片元的透明度不满足条件(通常是小于某个阈值),那么它对应的片元便会被舍弃,不做任何处理。
在Unity中是用如下函数来进行透明度测试:
clip(texColor.a - _Cutoff);
texColor.a为纹理的alpha值,_Cutoff为阈值,该函数等价于
if(texColor.a - _Cutoff < 0)
discard;
完整的代码
Shader "Unity Shader Book/Chapter 8/AlphaTest"
{
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex ("Texture", 2D) = "white" {}
_Cutoff("Alpha Cutoff",Range(0,1)) = 0.5
}
SubShader
{
Tags { "RenderType"="TransparentCutout" "Queue"="AlphaTest" "IgnoreProjector"="True" "LightMode"="ForwardBase"}
LOD 100 Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal:NORMAL;
float4 texcoord : TEXCOORD0;
}; struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv : TEXCOORD2;
}; fixed4 _Color;
fixed _Cutoff;
sampler2D _MainTex;
float4 _MainTex_ST; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o;
} fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed4 texColor = tex2D(_MainTex,i.uv); //Alpha Test
clip(texColor.a - _Cutoff);
//equal to
//if(texColor.a - _Cutoff < 0) discard;
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(worldNormal,worldLightDir));
return fixed4(diffuse + ambient,1.0);
}
ENDCG
}
}
}
效果如下,可以看到只是单纯的颜色剔除,并不算正常的透明效果。

为了得到正确的透明效果,可以使用透明度混合。
透明度混合
透明度混合即将透明物体的源颜色与其表面后方的目标颜色混合,其基本公式为:
DstColor-new(混合后的颜色) = SrcAlpha * SrcColor + (1- SrcAlpha) * DstColor。(SrcAlpha为混合因子)
该公式可以在Unity的ShaderLab语义中表示为
Blend SrcAlpha OneMinusSrcAlpha
更多语义可以参考官方文档
完整代码实现
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unity/Chapter 8/AlphaBlend"
{
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex ("Texture", 2D) = "white" {}
_AlphaScale("Alpha Scale",Range(0,1)) = 0.5
}
SubShader
{
Tags { "RenderType"="Transparent" "IgnoreProjector"="True" "Queue"="Transparent" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
ZWrite On
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float3 normal:NORMAL;
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
}; struct v2f
{
float2 uv : TEXCOORD2;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float4 pos : SV_POSITION;
}; fixed4 _Color;
fixed _AlphaScale;
sampler2D _MainTex;
float4 _MainTex_ST; v2f vert (appdata v)
{
v2f o;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o;
} fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex,i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(worldNormal,worldLightDir));
return fixed4(ambient + diffuse,texColor.a * _AlphaScale);
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
该代码得到的效果如下

嗯,这才是正常的透明效果
开启深度写入的半透明效果
上述的透明混合并没有开启深度写入(ZWrite On),因此如果物体有重叠,那么会产生错误的效果,如下

这时候需要在上文的透明度混合代码中的Pass代码块之前,插入一个新的Pass代码块
Pass
{
ZWrite On
ColorMask 0 //用于设置颜色通道的写掩码,0表示不写入任何通道
}
这样就会得到正确的效果

双面渲染的透明效果
仔细观察上文的透明效果可以发现,我们并不能透过半透明物体观察它们的内部情况,这是不合乎常理的,因此我们需要进行双面渲染。
在Unity中,我们可以通过Cull指令来控制剔除哪一面的渲染图元
Cull Back //剔除背面,只渲染前面,引擎的默认设置
Cull Front //剔除前面,仅渲染背面
Cull Off //关闭剔除
透明度测试的双面渲染
仅需要在Tags标签后插入一行
Cull Off
效果对比

透明度混合的双面渲染
将Pass代码块复制一份,一个Pass负责渲染前面,一个Pass负责渲染后面就行,格式如下
Shader "........."{
propeties
{
/*.....*/
}
SubShader{
Pass
{
Tags{.......}
Cull Front //剔除前面
/*其余代码保持不变
....
*/
}
Pass
{
Tags{.......}
Cull Back //剔除后面
/*其余代码保持不变
....
*/
}
}
}
实现的效果如下

Unity3D学习(八):《Unity Shader入门精要》——透明效果的更多相关文章
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
摘录自 冯乐乐的<Unity Shader入门精要> 笛卡尔坐标系 1)二维笛卡尔坐标系 在游戏制作中,我们使用的数学绝大部分都是计算位置.距离.角度等变量.而这些计算大部分都是在笛卡尔坐 ...
- Unity Shader入门精要之 screen post-processing effect
本篇记录了学习Unity Shader入门精要的屏幕后处理的一些知识点. OnRenderImage(RenderTexture src, RenderTexture dest) 以上函数是Unity ...
- Unity Shader入门精要学习笔记 - 第15章 使用噪声
转载自 冯乐乐的 <Unity Shader 入门精要> 消融效果 消融效果常见于游戏中的角色死亡.地图烧毁等效果.这这些效果中,消融往往从不同的区域开始,并向看似随机的方向扩张,最后整个 ...
- Unity Shader入门精要学习笔记 - 第14章非真实感渲染
转载自 冯乐乐的 <Unity Shader 入门精要> 尽管游戏渲染一般都是以照相写实主义作为主要目标,但也有许多游戏使用了非真实感渲染(NPR)的方法来渲染游戏画面.非真实感渲染的一个 ...
- Unity Shader入门精要学习笔记 - 第11章 让画面动起来
转自 冯乐乐的 <Unity Shader入门精要> Unity Shader 中的内置变量 动画效果往往都是把时间添加到一些变量的计算中,以便在时间变化时画面也可以随之变化.Unity ...
- Unity Shader入门精要学习笔记 - 第10章 高级纹理
转载自 冯乐乐的 <Unity Shader入门精要> 立方体纹理 在图形学中,立方体纹理是环境映射的一种实现方法.环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以看起来像镀了层 ...
- Unity Shader入门精要学习笔记 - 第9章 更复杂的光照
转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...
- Unity Shader入门精要学习笔记 - 第8章 透明效果
转载自 冯乐乐的 <Unity Shader入门精要> 透明是游戏中经常要使用的一种效果.在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道.当开启透明混合后,当一个物体被渲染 ...
- Unity Shader入门精要读书笔记(一)序章
本系列的博文是笔者读<Unity Shader入门精要>的读书笔记,这本书的章节框架是: 第一章:着手准备. 第二章:GPU流水线. 第三章:Shader基本语法. 第四章:Shader数 ...
- 【我的书】《Unity Shader入门精要》出版上市
重要的事 先说重要的事,就是我的书籍<Unity Shader入门精要>在经过无数次跳票后,终于出版上市了(泪目-)! 购买传送门: 亚马逊 当当 京东 截止到我写这篇文章的时候,京东是没 ...
随机推荐
- hive语句嵌入python脚本(进行map和reduce,实现左外连接)
在Hive语句中使用脚本(如python和shell)进行map和reduce:利用命令transform(或者指定map和reduce),配合加入的脚本文件add file 请看:http://ww ...
- 关于post利用之Python
今天大师兄放出了自己用PHP写的KTV点歌系统,注明,欢迎调戏,于是乎就尝试了下. 地址就不上了,到现在没补漏洞,我可不想被大师兄K…… 首先试试JavaScript脚本能否恶搞下 来个最基础的警告框 ...
- Android不同系统版本依然能调用到正确的API方法Demo——Service调用startForeground举例
private static final Class<?>[] mSetForegroundSignature = new Class[] { boolean.class}; privat ...
- gdb学习(一)[第二版]
概述 gdb是GNU debugger的缩写,是编程调试工具. 功能 1.启动程序,可以按照用户自定义的要求随心所欲的运行程序. 2.可让被调试的程序在用户所指定的断点处停住 (断点可以是条件表达式) ...
- WebService开发指南
WebServiceInAurora Web Service Web Service是一种面向服务的架构的技术,通过标准的Web协议提供服务,目的是保证不同平台的应用服务可以互操作.在Aurora框架 ...
- centos 下安装mysql-5.6.11
这次是在centos6.4下安装mysql,在安装之前,你要先确定你的linux已经安装了这些包: wget, gcc-c++, ncurses-devel ,cmake, make ,perl 如果 ...
- 多Region下HBase写入问题
最近在集群上发现hbase写入性能受到较大下降,测试环境下没有该问题产生.而生产环境和测试环境的区别之一是生产环境的region数量远远多于测试环境,单台regionserver服务了约3500个re ...
- 图像处理程序框架—MFC相关知识点
CDC:Windows使用与设备无关的图形设备环境(DC :Device Context) 进行显示 . MFC基础类库定义了设备环境对象类----CDC类. CDC与CGdiObject的关系 说道 ...
- prototype.js 和json.js 冲突
1.冲突简述和分析 prototype.js与json.js并不是完全兼容的.主要冲突在于json.js为Object的原型增加了一个toJSONString的方法. 冲突之一:是prototype中 ...
- POSTGRESQL 并发控制
http://meidayhxp.blog.163.com/blog/static/117608156201210243837491/ 这个内容是官方Doc中的一章,具体是那一版的,还未确认. 第九章 ...