UnityShader笔记之透明效果(一)渲染顺序以及透明度测试

更新时间:2023-07-07 06:04:40 阅读: 评论:0

UnityShader笔记之透明效果(⼀)渲染顺序以及透明度测试    在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道。当开启透明混合后,当⼀个物体被渲染到屏幕上时,每个⽚元除了颜⾊值和深度值外,它还有另⼀个属性--透明度。
Unity中常⽤两种⽅式来实现透明效果:第⼀种是透明度测试,这种其实⽆法得到真正的半透明效果;另⼀种是透明度混合。
从渲染顺序看,对于不透明(opaque)物体,我们不需要考虑它的渲染顺序,因为深度缓冲区(depth buffer)已经帮我们处理好了,它会根据⽚元深度值和深度缓冲中的值作⽐较(开启深度测试情况下),当其值距摄像机更远时,则不会渲染,否则会覆盖掉颜⾊缓冲中的像素,并把深度值更新到深度缓冲中(开启深度写⼊情况下)。
但要实现透明效果就不能这么做了,因为当使⽤透明度混合时,我们关闭了深度写⼊(ZWrite).
透明度测试和透明度混合的基本原理如下:
透明度测试:只有⼀个⽚元的透明度不满⾜条件(通常是⼩于某个阙值),那么它对应的⽚元就会被舍弃。被舍弃的⽚元将不会再进⾏任何处理,也不会对颜⾊缓冲产⽣任何影响。否则会被当做不透明处理,进⾏深度测试,深度写⼊等。所以透明度测试是不需要关掉深度写⼊的。除了会舍弃⽚元,其实和不透明物体渲染差不多。
透明度混合:这种⽅法可以得到真正的半透效果。它会使⽤当前⽚元的透明度作为混合因⼦,与颜⾊缓冲中的颜⾊进⾏混合,得到新的颜⾊。这⾥需要注意,透明度混合需要关闭深度写⼊。但没有关闭深度测试,所以当混合渲染⼀个⽚元时,它还是会⽐较它的深度和深度缓冲中的深度,如果它的深度值离摄像机更远,则不再进⾏混合操作。所以当不透明物体在透明物体前⾯时,先渲染了不透明物体,它仍可以遮挡主透明物体。所以对于透明度混合来说,深度缓冲只是可读的。
渲染顺序很重要
为什么透明度混合要关闭深度写⼊呢?如果不关闭深度写⼊,⼀个半透明表⾯背后的表⾯本来是可以透过它被看到的,但由于深度测试时该半透明表⾯离摄像机更近,导致后⾯的表⾯会被剔除,那就⽆法透过半透明表⾯看到后⾯物体了。但是这样也同时破坏了深度缓冲的⼯作机制,所以渲染顺序就⾮常重要了。
根据上图我们看看不同的渲染顺序会有什么结果:
1、先渲染B,再渲染A,由于不透明物体开启了深度测试和写⼊,所以B会先写⼊颜⾊缓冲和深度缓冲。然后渲染A,深度测试发现A离摄像机更近,则⽤A的透明度来和颜⾊缓冲中的B的颜⾊混合,得到正确结果。
2、先渲染A,再渲染B,A是半透,则先写⼊颜⾊缓冲,但不会写⼊深度缓冲。再渲染B,由于深度缓冲没有值,则B直接写⼊深度缓冲,进⽽直接写⼊颜⾊缓冲,则覆盖了A,这个效果不是我们想要的。
上古之神的低语所以我们应该在不透明物体渲染完之后再渲染半透物体。
那么如果都是半透物体,渲染顺序还重要吗?答案是肯定的。
1、先渲染B,再渲染A,则B先写⼊颜⾊缓冲,然后A会和颜⾊缓冲中的B进⾏混合,得到正确结果。
2、先渲染A,再渲染B,则A先写⼊颜⾊缓冲,随后B会和A进⾏混合,这样混合结果就反过来了,得到错误结果。
基于以上说明,渲染引擎⼀般会先对物体进⾏排序,再渲染。常⽤⽅法是:
1、先渲染所有不透明物体,并开启它们的深度测试和写⼊。
2、把半透明物体按它们离摄像机远近排序,然后按照从后往前的顺序渲染,开启深度测试,关闭深度写⼊。
下⾯我们看看Unity的渲染顺序:
Unity提供了⾃⼰的渲染队列(render queue),可以⽤SubShader的Queue标签来决定模型属于哪个渲染队列。内部⽤⼀系列整数索引来表⽰每个渲染队列,索引号越早被渲染。如下表:
因此,如果⽤透明度测试实现透明效果,可以设置如下标签:
SubShader{
Tags { "Queue" = "AlphaTest" }
Pass {
...
}
}
同样,透明度混合如下:
SubShader{
保护环境作文400字Tags { "Queue" = "Transparent" }
Pass {
学习的壁纸ZWrite off
...
}
}
透明度测试
通常,我们会在⽚元着⾊器中⽤clip函数来进⾏透明度测试。
clip是Cg中的⼀个函数,它的定义如下:
函数:void clip(float4 x);  void clip(float3 x);  void clip(float2 x); void clip(float1 x);  void clip(float x);
双子男和天蝎女参数:裁剪时使⽤的标量或⽮量条件。
描述:如果给定参数的任何⼀个分量是负数,就会舍弃当前像素的输出颜⾊。它等同于如下代码:
void clip(float4 x)
{
if (any x < 0)
discard;
}
下⾯⽤⼀张透明纹理,实现透明度测试效果,先看透明纹理:
形容美人的诗句
透明度测试图如下:
健脑的食物
准备⼯作:美食街
(1)新建场景,命名为Scene_8_3,默认情况下场景中包含⼀个摄像机和⼀个平⾏光,并使⽤了内置的天空盒。在Window->Rendering->Lighting Settings->Lighting->Skybox中去掉场景中的天空盒⼦。
(2)新建⼀个材质,命名AlphaTestMat。
(3)新建⼀个UnityShader,命名为AlphaTest。把这个shader赋值给上⾯的材质。
(4)创建⼀个⽴⽅体cube,并把上⾯的材质赋值给该模型。创建⼀个平⾯,使平⾯位于⽴⽅体下⾯。
(5)保存场景。下⾯直接上代码。
Shader "Unlit/AlphaTest"
{
Properties
{
_Color("Main 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"="ForwardBa"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "inc"
//为了和Properties语义块中声明的属性建⽴联系,
//需要定义和各个属性类型相匹配的变量
fixed4 _Color;
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff; //由于_Cutoff的范围在[0,1],因此可以使⽤fixed精度来存储它。
/
/定义顶点着⾊器的输⼊和输出结构体,接着定义顶点着⾊器:
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldNormal = al);
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);
clip(texColor.a - _Cutoff);//Alpha test
//if((texColor.a - _Cutoff) < 0.0){
//    discard;
//    } //Equal to
fixed3 albedo = b *_b;
fixed3 ambient = UNITY_ *albedo;
fixed3 diffu = _b*albedo *max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffu, 1.0);
}
ENDCG
}
}
Fallback"Transparent/Cutout/VertexLit"
}
1)上⾯的代码,为了在材质⾯板中控制透明度测试时使⽤的阙值,在Properties语义块中声明⼀个范围在[0,1]之间的属性_Cutoff: _Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
_Cutoff 参数⽤于决定我们调⽤clip进⾏透明度测试使⽤的判断条件。它的范围是[0,1],因为纹理像素的透明度就在此范围。
2)SubShader语义块中定义了⼀个Pass语义块:
我们知道渲染顺序的重要性,在Unity中透明度测试使⽤的渲染队列是名为AlphaTest的队列,因此我们要把Queue标签设置为AlphaTest。它的顺序位于不透明物体和半透的值之间。
RenderType标签可以让Unity把这个Shader归⼊提前定义的组(TranparentCutout)中,以指明该Shader是⼀个使⽤了透明度测试的Shader。RanderType标签通常被⽤于着⾊器替换功能。IgnoreProjector设置为True,该Shader不会受投影器(Projectors)的影响。
通常使⽤了透明度测试的shader都应该在Subshader中设置这三个标签。
Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout" }
LightMode标签是Pass标签中的⼀种,⽤于定义该Pass在Unity中的光照流⽔线中的⾓⾊。只有定义了正确的LightMode,才能正确得到⼀些Unity的内置光照变量,例如_LightColor()。
3)使⽤CGPROGRAM和ENDCG来包围住Cg代码⽚,来定义重要的顶点着⾊器和⽚元着⾊器代码。
为了使⽤Unity内置的⼀些变量,如_LightColor0,需要包含进Unity的内置⽂件inc
#include "inc"
4)有关透明度测试的代码:
clip(texColor.a - _Cutoff);
clip函数的定义,它会判断它的参数,即texColor.a - _Cutoff是否为负数,如果是就会舍弃该⽚元的输出。即texColor.a ⼩于材质参数
_Cutoff时,该⽚元就会产⽣完全透明的效果。
被注掉的代码,替换clip函数效果是⼀样的。
if (texColor.a - _Cutoff < 0.0) {
discard;
}
5)为Unity Shader设置合适的Fallback,使⽤内置的Transparent/Cutout/VertexLit来作为回调Shader。不仅能保证在我们编写的Subshader⽆法在当前显卡上⼯作时可以有合适的代替Shader,还可以保证使⽤透明度测试的物体可以正确地向其他物体投射阴影。
6)材质⾯板中的Alpha cutoff参数⽤于调整透明度测试,
我们控制_Cutoff的值,可以得到不同的效果:
测试效果图如下:

本文发布于:2023-07-07 06:04:40,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/1083135.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:深度   渲染   透明度
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图