在实时渲染中要实现透明效果,通常会在渲染模型时控制他的透明通道。当开启透明混合后,当一个物体被渲染到屏幕上时,每个片元除了颜色值和深度值之外,它还有另一个属性——透明度。当透明度为1时表示该像素是完全不透明的,当为0时,则表示像素完全不会显示。

在unity中,我们通常使用两种方法来实现透明效果:第一种是使用透明度测试,这种方法其实无法得到真正的半透明效果;另一种就是透明度混合。

对于不透明物体,不考虑他们的渲染顺序也能得到正确的排序效果,这是由于强大的深度缓冲的存在。在实时渲染中,深度缓冲是用于解决可见性问题的,它可以决定那个物体的那些部分会被渲染在前面,那些部分会被其他物体遮挡。他的基本思想是:根据深度缓存中的值来判断该片元距离摄像机的距离,当渲染一个片元时,需要把它的深度值和已经存在于深度缓冲中的值进行比较,如果他的之距离摄像机更远,那么说明这个片元不应该被渲染到屏幕上;否则,这个片元应该覆盖掉此时颜色缓冲中的像素值,并把它的深度值更新到深度缓冲中。

使用深度缓冲,可以让我们不用关心不透明物体的渲染顺序,即使我们先渲染A再渲染B也不用担心B会遮盖掉A,因为再进行深度测试时会判断出B距离摄像机更远,也就不会写入到颜色缓冲中。但如果想要实现透明效果,我们就会关闭深度写入。

透明度测试:它采用一种“霸道极端”的机制,只要一个片元的透明度不满足条件,那么他对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则就会按照普通的不透明物体的处理方式来处理它,即进行深度测试,深度写入等。也就是说,透明度测试是不需要关闭深度写入的,它和其他不透明物体最大的不同就是他会根据透明度来舍弃一些片元,虽然简单,但是它产生的效果也很极端,要么完全透明,要么完全不透明。

透明度混合:这种方法可以得到真正的半透明效果。他会使用当前片元的透明度作为混合因子,与已经存储再颜色缓冲中的颜色进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序。需要注意的是,透明度混合之关闭了深度写入,但没有关闭深度测试,意味着,当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果他的深度值距离摄像机更远,那么就不会再次进行深度混合操作。这一点决定了,当一个不透明物体出现在一个透明物体前,我们先渲染了不透明物体,它依然可以正常地遮挡住透明物体。也就是说,对于透明度混合来说,深度缓冲是只读的。

为什么渲染顺序很重要

对于透明混合技术,需要关闭深度写入,此时我们就要小心处理透明物体的渲染。

为什么要关闭深度写入呢?如果不关闭深度写入,一个半透明表明背后的表面本来是可以透过它被我们看到的,但是由于深度测试时判断结果是该半透明表面距离摄像机更近,导致后面的表面将会被剔除,我们也就无法透过半透明表面看到后面的物体了。但是,我们由此破环了深度缓冲的工作机制。

不同的渲染顺序会有什么结果呢。

我们先渲染B再渲染A,那么由于不透明物体开启了深度测试和深度检验,此时深度缓冲中没有任何有效数据,因此B会首先写入颜色缓冲和深度缓冲。随后我们渲染A,透明物体仍然会进行深度测试,因此我们会发现和B相比A距离摄像机更近,我们会使用A的透明度来和颜色缓冲中的B的颜色进行混合,得到正确的半透明效果。

如果我们先渲染A再渲染B。渲染A时,深度缓冲中没有任何有效数据,因此A直接写入颜色缓冲中,但由于对半透明物体关闭了深度写入,此时A不会修改深度缓冲。等到渲染B时,B会进行深度测试,发现,深度测试中还没有数据那B就写入到颜色缓冲中了,结果B就会覆盖A的颜色。从视觉上B就出现在了A的前面就错误了。

我们还是考虑不同的渲染顺序有什么不同的结果。

第一种,先渲染B再渲染A。那么B会正常写入颜色缓冲,然后会和颜色缓冲中的B颜色进行混合,得到半透明的效果。

第二种情况,我们先渲染A再渲染B。那么A会先写入颜色缓冲,随后B会和颜色缓冲中的A进行混合,这样混合后的结果会反过来,看起来B就在A的前面,得到错误的半透明结果。

基于这两点,渲染引擎一般都会先对物体进行排序,再渲染。常用的方法是。

  1. 先渲染所有不透明的物体,并开启他们的深度测试和深度写入。
  2. 把半透明物体按照他们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并开启他们的深度测试,但关闭深度写入。

Unity自定义的五个渲染队列

透明度测试

从图中可以看到,透明测试得到的透明效果很极端,要么完全透明,要么完全不透明,他的效果往往像是在不透明物体上挖一个空洞。而且得到的透明效果在边缘处往往参差不起有锯齿,因为在边界纹理的透明度的变化精度问题。为了得到更加柔滑的透明效果,就可以使用透明度混合。

代码

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 = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, 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 testclip (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提供混合命令——Blend。Blend是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 OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#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 = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, 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已经按照像素级别的深度排序结果进行透明渲染。但这种方法的缺点在于,多使用一个pass会对性能造成一定的影响。

这个新添加的Pass的目的仅仅是为了把模型的深度信息写入深度缓冲中,从而剔除模型中被自身遮挡的片元。因此,在pass的第一行开启了深度写入。在第二行,我们使用一个新的渲染命令——ColorMask。在ShaderLab中,ColorMask用于设置颜色通道的写掩码。

当ColorMask为0时,意味着该Pass不写入任何颜色通道,即不会输出任何颜色。这正是我们需要的——该pass只需要写入深度缓存即可。

代码

Shader "Unity Shaders Book/Chapter 8/Alpha Blending With ZWrite" {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"}// Extra pass that renders to depth buffer onlyPass {ZWrite OnColorMask 0}Pass {Tags { "LightMode"="ForwardBase" }ZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#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 = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, 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"
}

ShaderLab的混合命令

混合还有很多其他的用处,不仅仅是用于透明度的混合。当片元着色器产生一个颜色的时候,可以选择与颜色缓存中的颜色进行混合。这样一来混合就和两个操作数有关:源颜色和目标颜色。源颜色我们可用S表示,指的是由片元着色器产生的颜色值:目标颜色我们用D表示,指的是从颜色缓存中读取到的颜色值。对对他们进行混合后得到的输出颜色,我们用O来表示,他会重新写入到颜色缓存中,需要注意的是,当我们谈及混合中的源颜色目标颜色和输出颜色时,他们都包含了RGBA四个通道的值,而并非仅仅是RGB通道。

想要使用混合,我们必须开启它。在Unity中,当我们使用Blend命令时,除了设置混合状态以外也开启了混合。但是,在其他图形api中我们是需要手动开启的。

混合操作

常见的混合类型

通过混合操作和混合因子命令的组合,我们可以得到一些类似Photoshop混合模式中的混合效果:

//正常,即透明度混合

Blend SrcAlpha OneMinusSrcAlpha

//柔和相加

Blend OneMinnusDstColor One

//正片叠低,即相乘

Blend DstColor Zero

//两倍相乘

Blend DstColor SrcColor

//变暗

BlendOp Min

Blend One One

//变亮

BlendOp Max

Blend One One

//滤色

Blend OneMinusDstColor One

//等同于

Blend One OneMinusSrcColor

//线性减淡

Blend One One

双面渲染的透明效果

虽然上面使用Min和Max混合操作时仍然设置了混合因子,但实际上他们并不会对结果有任何影响,因为Min和Max混合操作回忽略混合因子。另一点是,虽然上面有混合模式并没有设置混合操作的种类,但是他们默认就是使用加法操作,相当于设置了BlendOpAdd。

在显示生活中,如果一个物体是透明的,意味着我们不但能透过她看到其他物体还能看到她的内部结构。在前面无论是透明度测试还是透明度混合,我们都没有办法观察到正方形内部以及背面的形状,导致物体看起来就像是只有半个一样。这是因为默认情况下渲染引擎剔除了物体背面的渲染图元,只渲染物体的正面。如果我们想要得到双面的渲染效果,我们可以使用Cull命令来控制需要剔除那个面的渲染图元。

Cull Back Front Off

如果设置为Back,那么那些背对摄像机的渲染图元就不会被渲染,这也是默认情况下的剔除状态:如果设置为Front,那么那些朝向摄像机的渲染图元就不会被渲染:如果设置为Off,就会关闭剔除功能,那么所有的渲染图元都会被渲染,但由于这时需要渲染的图元数目会成倍增加,因此除非是特殊效果,例如这里的双面渲染的透明效果,通常情况是不会关闭剔除功能的。

代码

Shader "Unity Shaders Book/Chapter 8/Alpha Test With Both Side" {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" }// Turn off cullingCull OffCGPROGRAM#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 = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, 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);clip (texColor.a - _Cutoff);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透明效果相关推荐

  1. [Unity Shader]凌波微步效果

    [Unity Shader]凌波微步效果 相信很多人都看过天龙八部,里面的段誉有一个技能就是凌波微步:移动的时候人先到,衣角跟随其后.说白了就是移动时有一个残影跟着他.下面先看下最终效果 下面我们看如 ...

  2. Unity Shader - 描边效果

    原文链接:http://blog.csdn.net/puppet_master/article/details/54000951 简介 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个 ...

  3. Unity Shader 动画效果出现残影

    问题 复现<Unity Shader入门精要>的动画效果时,我发现我的动画都出现了残影. 一开始认为是程序写错了,采样出现了问题,但是复查对比其他资料很多遍也没发现错误. 解决方法 后来反 ...

  4. 【Unity Shader 消融效果_案例分享】

    1.实现逻辑 消融效果主要是利用了Shader中的clip()函数,也就是透明测试功能,在ASE中叫"Opacity Mask". 消融效果是基于一张"Noise&quo ...

  5. Unity Shader放大镜效果

    小菜最近看到了一篇关于Shader实现的放大镜效果,酷炫的效果让小菜倍生好奇,冲动之下还是搬来练练手,刚好巩固下自己的顶点片元着色器编码. 有没有想学习的冲动!!! 文章的开始先来介绍一款好用的vs编 ...

  6. Unity Shader 新手引导效果

    这两天实现了下新手引导需要的遮罩镂空shader效果,记录一下. 1.圆形镂空shader代码: //计算片元世界坐标和目标中心位置的距离 float dis = distance(IN.worldP ...

  7. unity Shader 扭曲效果

    原理 所谓扭曲就是扰动一个物体的uv坐标,表现出来就是一个扰动效果,如火焰扭曲空气,和水波对水底的影响. 1. 在shader 中我们需要一张当前渲染的纹理 2. 给一个物体渲染并使用当前纹理,用屏幕 ...

  8. Unity Shader 贴花效果(一)

    本文实现的是一个Mesh Decal方法的贴花方案,参考了本篇博文链接: unity的贴花方案.链接的文章是转载的,我并没有找到原文地址.本篇文章主要是学习和自己的理解为主. 先看未贴花之前的效果 这 ...

  9. unity shader 毛玻璃效果 周围发光效果 Depth.shader

    实现原理 将像素的法线和 从镜头到像素位置的方向 转为世界空间坐标 计算镜头到位置方向 和 法线的夹角 最终像素输出的颜色 = 夹角 * 颜色 效果适用范围 适用于穿透显形 毛玻璃 Shader代码 ...

最新文章

  1. python定义浮点数数组_tensorflow之tf.record实现存浮点数数组
  2. W3C HTML 工作组联合主席Paul Cotton谈HTML5发展愿景
  3. 图形用户界面和交互输入方法---图形数据的输入功能
  4. Matlab norm 用法小记
  5. ORACLE时间常用函数(字段取年、月、日、季度)
  6. NetCore 2.0 + Swagger 的WebAPI 模板例子
  7. 树莓派RaspberryPi的RPi.GPIO使用指南
  8. php 删除数字索引元素,PHP重置数组为连续数字索引的几种方式
  9. 爬虫库之BeautifulSoup学习(三)
  10. PDF如何添加下划线 捷速PDF编辑器一键搞定
  11. 游戏是怎么赚钱的?(科普篇 )
  12. 删除Word模板文件
  13. 新浪邮箱开通imap服务器,新浪邮箱申请
  14. opencv 3 core组件进阶(2 ROI区域图像叠加图像混合;分离颜色通道、多通道图像混合;图像对比度,亮度值调整)...
  15. 基于PaddleGAN项目人脸表情动作迁移学习(五)图像补帧上色与超分修复
  16. javascript中document用法
  17. html标签的多级列表,word如何将标题样式关联多级列表
  18. 在java中如何根据三角函数中的正弦值 余弦值 正切值求该角度值
  19. RJ-45双绞线的制作和测试-网络实验1
  20. 搜索中的 Query 理解及应用

热门文章

  1. 华为2019勇敢之星软件岗实习生笔试-截取字符串
  2. 精益数据分析笔记--指标
  3. python-numpy基础知识
  4. 基于快速换模的双复合挤出机生产效率的改善
  5. uniapp小程序实现上拉加载更多
  6. YYCache 设计思路
  7. 多目标进化算法--多项式变异(PM)
  8. ​在分类中如何处理训练集中不平衡问题​
  9. python编程快速上手项目答案-python编程快速上手之第5章实践项目参考答案
  10. 富士变频器专用软件,loader软件。可用于富士在售变频器所有机型