Unity-shader学习笔记(六)

Unity-shader学习笔记(六)

14 透明效果

这一部分我们将聊聊透明效果的知识点

透明是游戏中经常要使用的一种效果。在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道。一旦开启了透明混合后,假如这个物体被渲染到了屏幕上时,每个片元除了颜色值和深度值以外,还有一个属性–透明度。当透明度为1时,该像素完全不透明;为0时,该像素完全透明,也就是不会显示。

在Unity中,我们通常使用两种方法来实现透明效果:①透明度测试(但这种方法无法得到真正的半透明效果);②透明度混合。

在之前的学习中,由于只是一次渲染,也就是说我们并没有遇到过多个模型的渲染,即并没有考虑过时是渲染A,在渲染B,最后渲染C。还是其他的渲染顺序。

对于不透明物体,不考虑它们的渲染顺序也是能够得到正确的排序效果的,因为强大的深度缓冲的存在。在实时渲染中,深度缓冲是用于解决可见性问题的,它可以决定哪个物体的哪些部分会被渲染在前面,而哪些部分会被其它物体遮挡。

其基本思路是:

根据深度缓冲中的值来判断该片元据离摄像机的距离,当渲染一个片元时,需要把它的深度值和已经存在于深度缓冲中的值进行比较,如果这个值距离摄像机很远,那说明这个片元不应该被渲染到屏幕上;否则这个片元应该覆盖掉此时颜色缓冲中的像素值,并更新深度值。

但是当我们使用透明度混合时,我们是要关闭深度写入的。这两个的实现原理为:

①透明度测试:这个的思想很简单,只要一个片元的透明度不满足我们设定的某个阈值时,这个片元就会被舍弃。如果满足,就会按照普通的不透明物体的处理方式来处理它–进行深度测试、深度写入等。也即,透明度测试是不需要关闭深度写入的。这就导致它产生的效果很极端:要么透明看不见,要么完全不透明可见。

②透明度混合:这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子与已经存在颜色缓冲区中的颜色值进行混合,得到新的颜色。但是我们需要关闭深度写入,注意我们只是关闭了深度写入,并没有关闭深度测试,也就是说,在透明度混合中,深度缓冲是只读的。

透明度混合为什么要关闭深度写入呢?

因为如果不关闭的话,一个半透明物体的背后的表面我们是可以透过它而看见的,导致的就是我们无法透过这个半透明物体看到后面的物体。

14.1 渲染顺序

由于透明度混合是需要关闭深度写入,此时就要小心处理透明物体渲染的顺序。为什么呢?我们来考虑最简单的情况:假设场景中有两个物体A和B,A是半透明的,B是不透明的,从视线方向是先A再B。也就是说看前来应该是A再B前面

①假如我们先渲染B,再渲染A:B会正常写入颜色,然后A会和颜色缓冲中的B颜色进行混合,得到正确的半透明效果;

②我们先渲染A,再渲染B:A会先写入颜色华宠,随后B会和颜色缓冲中的A进行混合,这样混合的结果会完全相反,看起来B好像在A前面,得到的就是错误的半透明结构。

基于此,渲染引擎一般都会先对物体进行排序,再渲染。

(1)先渲染所有不透明物体,并开启它们的深度测试和深度写入;

(2)把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并开启他们的深度测试,但一定要关闭深度写入。

那么问题来了,(2)中的排序是怎么排的?我们知道一个物体的网格结构往往占据了空间中的某一块区域,也就是说这个网格上每一个点的深度值都可能不一样,那么我们选择哪个点的深度值来作为整个物体的深度值和其它物体进行排序呢?是中点?还是最远的点?还是最近的点?可惜的是,哪个点都不行。

通常的方法还是分割网格法,尽管这种方法实际应用时依然会出现一定的错误的遮挡,但由于它足够有效并且容易实现,所以普遍使用。为了减少错误的排序,我们就会尽可能让模型是凸面体,并考虑将复杂的模型擦分为可以独立排序的多个子模型。

14.2 Unity Shader中的渲染顺序表示

实际上我们在介绍Shader结构的时候,就提到过渲染队列(render queue)。一共有五种:

名称 队列或索引值 描述
Background 1000 这是最先被渲染的,一般用于渲染需要绘制在背景上的物体
Geometry 2000 大多数物体使用这个队列,包括不透明物体
AlphaTest 2450 需要透明度测试的物体使用这个队列
Transparent 3000 使用了任何透明度混合(例如关闭了深度写入的shader)的物体都应该使用该队列
Overlay 4000 用于实现一些叠加效果,任何需要在最后渲染的物体都应该使用该队列

于是,当要通过透明度测试实现透明效果时,代码应该为:

SubShader{
    Tags { "Queue" = "AlphaTest" }
    Pass {
        ......
    }
}

当要通过透明度混合来实现透明效果时,代码应该为:

SubShader{
    Tags { "Queue" = "AlphaTest" }
    Pass {
        ZWrite Off
        ......
    }
}

14.3 透明度测试

通常我们会在片元着色器中使用CG的clip函数来进行透明度测试:

函数:void clip(float4 x); void clip(float3 x); void clip(float2 x); void clip(float x);

参数:裁剪时使用的标量或矢量条件

描述:如果给定的参数的任何一个分量小于阈值(clip函数的判断值是0,我们使用时传入的参数是纹理透明度和阈值的差值),接舍弃当前像素的输出颜色

void clip(float4 x){
    if(any(x < 0))
        discard;
}

具体实现与之前的实现没有什么区别,只是在片元着色器中加入了透明度测试的内容:

①在Properties中加入_Cutoff属性,可以在材质的属性面板调节它的值,而它的值又将决定clip函数的判断(其实这就是那个阈值)

_Cutoff("Alpha Cutoff", Range(0,1))//范围在[0,1],因为纹理像素的透明度就是此范围

②在SubShader的开始设置渲染顺序

Tags { "Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }

Queue标签设置为AlphaTest,表明要透明度测试;IgnoreProjector标签设置为True,表明这个shader不会受到投影器(Projectors)的影响;RenderType标签设置为TransparentCutout,就是指明该shader是一个使用了透明度测试的shader。

一般来说,使用了透明度测试的shader都要设置这三个标签。

③在片元着色器中判断透明度

//注释部分与未注释部分是等价的
clip(texColor.a - _Cutoff);
//if ((texColor.a - _Cutoff) < 0.0) {
	//	discard;
//}

材质参数(阈值)_Cutoff可以调控,texColor的a分量(即透明度分量)为材质原有的。

④Fallback的修改

Fallback "TransparentCutout/Cutout/VertexLit"

与之前使用的Diffuse和Specular不同,原因以后再说。

14.4 透明度混合

透明度混合的原理:它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新颜色。还是那句话,由于关闭了深度写入,要十分注意物体的渲染顺序。

为了使用混合,要在Pass代码块中使用Unity提供的混合命令:Blend。其语义有:

语义 描述
Blend Off 关闭混合
Blend SrcFactor DstFactor 开启混合并设置混合因子,该片元产生的颜色会乘以SrcFactor,目标颜色会乘以DstFactor,然后将两者相加再将和存于颜色缓冲中
Blend SrcFactor DstFactor,SrcFactorA DstFactorA 和第二个一样,只是透明通道的混合因子不同
BlendOp BlendOperation 和第二个不同的是并不是讲两个颜色简单混合相加而是使用BlendOperation进行其他操作

我们接下来会使用第二种进行透明度混合。我们将SrcFactor设为SrcAlpha,经过混合后新的颜色为:
D s t C o l o r n e w = S r c A l p h a ∗ S r c C o l o r + ( 1 − S r c A l p h a ) ∗ D s t C

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值