Unity Shader 之 透明效果
透明效果
透明效果一般有两种实现方法:
- 第一种,使用透明度测试(Alpha Test)
- 第二种,使用透明度混合(Alpha Blending)
透明度测试和透明度混合机制:
透明度测试(Alpha Test):只要一个片元的透明度不满足条件(小于某阀值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就按照普通的不透明物体处理,即进行深度测试、深度写入。透明度测试不需要关闭深度写入。
透明度混合(Alpha Blending):使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色值。但是,这需要关闭深度写入。虽然关闭了深度写入,但是没有关闭了深度缓冲,此时深度缓冲还是可读的。
不同的渲染顺序带来的结果:(A半透明,B不透明)

第一种:先渲染B,再渲染A.
结果:能得到正确的渲染效果。
第二种:先渲染A,再渲染B.
结果:不能得到正确的渲染效果,因为在渲染A时已经关闭了深度写入,所以A不会修改深度缓冲,等到渲染B时,B直接覆盖了A的颜色。
渲染常用顺序:
- 先渲染所有不透明物体,开启深度测试和深度写入。
- 半透明物体按离摄像机距离远近排序,从后往前渲染,开启深度测试,关闭深度写入。
部分相互重叠的物体不适用,解决方法是分割网格。
unity shader的渲染顺序
Unity为了解决渲染顺序问题提供了渲染队列(render queue)解决方案。

实现透明度测试时需要添加代码:
SubShader{
Tags {"Queue" = "AlphaTest"}
Pass{
...
}
}
实现透明度混合需要添加代码:
SubShader{
Tags {"Queue" = "Transform"}
Pass{
ZWrite Off
...
}
}
透明度测试
关键函数
clip函数是CG中的一个函数:
void clip(float4 x);
void clip(float3 x);
void clip(float2 x);
void clip(float1 x);
void clip(float x);
全部代码
Shader "Unity Shaders Book/Chapter 8/Alpha Test" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
}
SubShader {
Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(_Object2World, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
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);
// Alpha test
clip (texColor.a - _Cutoff);
// Equal to
// if ((texColor.a - _Cutoff) < 0.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(ambient + diffuse, 1.0);
}
ENDCG
}
}
FallBack "Transparent/Cutout/VertexLit"
}
效果

透明度混合
透明度混合的本质就是用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新颜色。
混合公式如下:

unity提供的混合命令:

透明度混合完整代码
Shader "Unity Shaders Book/Chapter 8/Alpha Blend" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(_Object2World, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
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"
}
效果

开启深度写入的半透明效果
前面提到由于关闭深度写入而造成的错误排序的情况。可以使用两个Pass的办法来解决
第一个Pass: 开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中
第二个Pass: 进行正常的透明度混合,由于上个Pass已经的到了像素的正确深度信息,所以可以得到正确的渲染结果。
这种办法的缺点是对性能有一定影响
ShaderLab的混合命令
ShaderLab中的混合因子
| 参数 | 描述 |
|---|---|
| One | The value of one - use this to let either the source or the destination color come through fully. |
| Zero | The value zero - use this to remove either the source or the destination values. |
| SrcColor | The value of this stage is multiplied by the source color value. |
| SrcAlpha | The value of this stage is multiplied by the source alpha value. |
| DstColor | The value of this stage is multiplied by frame buffer source color value. |
| DstAlpha | The value of this stage is multiplied by frame buffer source alpha value. |
| OneMinusSrcColor | The value of this stage is multiplied by (1 - source color). |
| OneMinusSrcAlpha | The value of this stage is multiplied by (1 - source alpha). |
| OneMinusDstColor | The value of this stage is multiplied by (1 - destination color). |
| OneMinusDstAlpha | The value of this stage is multiplied by (1 - destination alpha). |
ShaderLab中的混合操作
| 操作 | 描述 |
|---|---|
| Add | Add source and destination together. |
| Sub | Subtract destination from source. |
| RevSub | Subtract source from destination. |
| Min | Use the smaller of source and destination. |
| Max | Use the larger of source and destination. |
| LogicalClear | Logical operation: Clear (0) DX11.1 only. |
| LogicalSet | Logical operation: Set (1) DX11.1 only. |
| LogicalCopy | Logical operation: Copy (s) DX11.1 only. |
| LogicalCopyInverted | Logical operation: Copy inverted (!s) DX11.1 only. |
| LogicalNoop | Logical operation: Noop (d) DX11.1 only. |
| LogicalInvert | Logical operation: Invert (!d) DX11.1 only. |
| LogicalAnd | Logical operation: And (s & d) DX11.1 only. |
| LogicalNand | Logical operation: Nand !(s & d) DX11.1 only. |
| LogicalOr | Logical operation: Or (s | d) DX11.1 only. |
| LogicalNor | Logical operation: Nor !(s | d) DX11.1 only. |
| LogicalXor | Logical operation: Xor (s ^ d) DX11.1 only. |
| LogicalEquiv | Logical operation: Equivalence !(s ^ d) DX11.1 only. |
| LogicalAndReverse | Logical operation: Reverse And (s & !d) DX11.1 only. |
| LogicalAndInverted | Logical operation: Inverted And (!s & d) DX11.1 only. |
| LogicalOrReverse | Logical operation: Reverse Or (s | !d) DX11.1 only. |
| LogicalOrInverted | Logical operation: Inverted Or (!s | d) DX11.1 only. |
双面渲染的透明效果
上面的透明度测试和透明度混合都无法将物体的背面显示出来,这是因为渲染引擎剔除了物体的背面,所以需要在渲染透明物体使用Cull指令控制需要剔除的渲染图元。
- Cull Back | Front | Off
分别是:剔除背面的图元 | 剔除前面的图元 | 关闭功能
完整代码
Shader "Unity Shaders Book/Chapter 8/Alpha Blend With Both Side" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass {
Tags { "LightMode"="ForwardBase" }
// First pass renders only back faces
Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(_Object2World, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
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
}
Pass {
Tags { "LightMode"="ForwardBase" }
// Second pass renders only front faces
Cull Back
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(_Object2World, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
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"
}
效果

Unity Shader 之 透明效果的更多相关文章
- unity shader 纹理&透明效果
1.纹理映射基础 (1)纹理映射通过(u,v)坐标实现.注意:这句话时博主当时面试一家外企被问到的问题. (2)添加纹理属性:——MainTex("Main Tex",2D)=&q ...
- Unity Shader实现描边效果
http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用S ...
- Unity Shader 屏幕后效果——颜色校正
屏幕后效果指的是,当前整个场景图已经渲染完成输出到屏幕后,再对输出的屏幕图像进行的操作. 在Unity中,一般过程通常是: 1.建立用于处理效果的shader和临时材质,给shader脚本传递需要控制 ...
- Unity Shader 屏幕后效果——全局雾
Unity内置的雾效需要在每个shader中分别编写,造成了极大的不便.这里利用屏幕后处理产生可单独控制且自由度更高的雾效. 屏幕后雾效的本质在于,通过深度纹理重构出每个像素在世界空间中的位置,根据得 ...
- Unity Shader 屏幕后效果——景深
景深效果的原理是,在摄像机的近裁剪平面和远裁剪平面之间可以设置一个焦距,在这个距离所在的平面上的物体最为清晰,而这个距离之前或之后的物体成像是一种模糊状态(根据距离逐渐模糊,最终达到最为模糊的状态). ...
- Unity Shader 屏幕后效果——Bloom外发光
Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成. 一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客: https:/ ...
- Unity Shader 屏幕后效果——高斯模糊
高斯模糊是图像模糊处理中非常经典和常见的一种算法,也是Bloom屏幕效果的基础. 实现高斯模糊同样用到了卷积的概念,关于卷积的概念和原理详见我的另一篇博客: https://www.cnblogs.c ...
- Unity Shader 屏幕后效果——边缘检测
关于屏幕后效果的控制类详细见之前写的另一篇博客: https://www.cnblogs.com/koshio0219/p/11131619.html 这篇主要是基于之前的控制类,实现另一种常见的屏幕 ...
- Unity Shader 屏幕后效果——摄像机运动模糊(速度映射图实现)
速度映射图主要是为了得到每个像素相对于前一帧的运动矢量,其中一种方法是使用摄像机的深度纹理来推导. 推导过程如下: 先由深度纹理逆推出NDC(归一化的设备坐标)下的顶点坐标,利用VP矩阵(视角*投影矩 ...
随机推荐
- CF851 C 暴力
给出n个5维下的点,求点a不与其它任意的b,c重合,向量ab,ac的夹角都为钝角,这样的点个数,并打印它们. 转换二维下的求角度的函数为五维的,而且由于要求角度大于90度,在二维情况下最多有4个点,也 ...
- ZOJ 3774 二次剩余
LINK 题意:简单粗暴,求菲波那契数列每个数的m次的前n项和模1e9+7 思路:斐波那契通项式, 注意到有很多根号5,求二次剩余为5模1e9+7的解,显然我们可以直接找一个(383008016),然 ...
- Atcoder #014 agc014_C BFS
LINK 题意:给定起点和最大操作次数$k$,地图'#'为上锁房间, 每次可以走$k$步,并任意解锁$k$个房间,问到达地图边界的最小次数. 思路:其实上锁与否并没有关系,因为先把$k$步走的次数用完 ...
- Assert 的用法
Assert Assert是断言的意思,头文件为assert.h, assert是一个宏 功 能: 测试一个条件并可能使程序终止 用 法: void assert(int test); 在单元测试中经 ...
- CSS3之伪元素选择器和伪类选择器
伪类选择器,和一般的DOM中的元素样式不一样,它并不改变任何DOM内容.只是插入了一些修饰类的元素,这些元素对于用户来说是可见的,但是对于DOM来说不可见.伪类的效果可以通过添加一个实际的类来达到. ...
- 【BZOJ】4316: 小C的独立集 静态仙人掌
[题意]给定仙人掌图,求最大独立集(选择最大的点集使得点间无连边).n<=50000,m<=60000. [算法]DFS处理仙人掌图 [题解]参考:[BZOJ]1023: [SHOI200 ...
- Xcode变量概览-summary
问题描述 在Xcode中断点调试时,鼠标停留在变量上,就能看到变量的信息.但对于自定义对象,通常Xcode提供的直接信息非常有限,像这样 想要了解这个对象具体的内容,需要展开左边的箭头 当开发者想要知 ...
- JS设计模式——1.富有表现力的JS
创建支持链式调用的类(构造函数+原型) Function.prototype.method = function(name, fn){ this.prototype[name] = fn; retur ...
- PHP对象5: define / const /static
define定义全局常量: define('PATH', '/data/home/www'); const也是定义常量, 一般用于类中, 饰成员属性,不可以修饰方法,如下: class Test{ c ...
- ubuntu14.04安装使用NviDIA显卡驱动
想给自己的ubuntu换N卡驱动的原因: 一方面,由于自己电脑在编译源代码8线程全开(make -j8)时,CPU温度呼呼涨到八九十度,从而常常导致系统保护自动关机,让人有点不爽.网上有说ubuntu ...