学习笔记21

本文探讨了Unity中Shader透明效果的实现方法,包括透明度测试、Cutout、Fade及透明混合模式等内容,并讨论了如何在C#脚本中控制这些效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Rendering 11

之前的话题无论是简单的材质还是复杂的材质都是opaque,不透明物体的。这里开始引入透明效果来完善shader。

现在应用到场景中没有什么透明效果,但是这里有一个selection outline相关的东西。

另外,这个是我们alpha存放的地方。

一个是albedo中,这个是针对每一个像素都有自己的alpha,同时Tint中有存储一个模型整体的透明度。

这里有一个注意点,就是我们曾经用albedo的alpha通道进行一个,

最简单的透明玩法,透明度测试。

另外这个clip语句是有消耗的,如果正常情况下材质根本就不涉及cutout操作,那么就完全没必要做这个判断,所以类似之前的那些代码,加上一个语义判断。

引入了这个来对材质面板中的是否透明进行调节。

关于脚本GUI控制shader和材质面板的原理。

首先,因为脚本一定会对应到一个shader(有可能是多个,多个情况和一个是类似的),重要的是一个shader可能搞出来多个材质实例。

那么当我们对一个材质进行修改的时候,就比如把其中一个材质的opaque修改成cutout,那么C#里面就会有定义相应关键词的逻辑,那么这个材质对应效果就会发生相应的改动(如果我们shader里面写了相关的判断代码)。

那么为啥其他共用同一个shader的材质不会变,他们都是有GUI来控制的啊。我们写代码的时候,也没说根据情况来看是动的哪个材质的选项,然后修改哪个材质。

其实这一点是有控制的,也就是说我们改的谁材质参数,C#是知道的,知道他就会只针对改的这个材质来做相应的逻辑执行。

那么到底是通过什么来控制的呢?其实就是上边的这个targets。

这里他写的target是一个物体集合,什么物体:being inspected,被仔细看的。其实就是对应inspector面板。

这么说并不明确,他应该说:being selected。

这个函数相当于整个C#代码的入口函数。在editor中会有一个targets参数,注意是复数,如果是target那就表示数组中的第一个。

回到刚刚说的being selected。其实就是我们在材质面板中操作的时候,会选择一个或者可能是多个材质来进行操作。

因为开始操作材质了,所以C#代码就开始起作用了,就开始从他的入口执行了。

这时候会传递进来参数,参数中的targets,其实就是我们选中的材质。也就是这样和具体某一个材质联系起来的。

所以现在再来看这段代码,这就是为啥在这里加了for循环的原因(上一节课讲到了多材质编辑的内容,其实也是这个原理)

因为选中多个材质之后,修改rendering mode(我们自定义的)属性,会修改到所有选中的材质的属性。所以把所有被修改的材质都对他们的渲染队列进行一个修改。

这里在C#中修改渲染队列,是因为如果使用Tags来写渲染队列,那就只能fixed了。

所以这里使用这个自定义render queue

这里的赋值会覆盖掉内部的渲染队列设置。overrule否决。

关于这个渲染Type的tags,是和replace shader相关的一个技术,除此以外好像没有啥用。

关于这个replace shader:

unity, 替换shader渲染(Rendering with Replaced Shaders) - wantnon - 博客园 (cnblogs.com)

(120条消息) Unity使用替换shader渲染(Rendering with Replaced Shaders)_coffeecato的博客-优快云博客_unity 替换渲染

Unity - Manual: Replacing shaders at runtime (unity3d.com)

大致描述一下他的作用即工作的原理:

就是说我想在场景渲染的时候,突然把场景的某些物体给它换一个shader来渲染。要想实现这个就用这个replace shader技术。

具体操作是通过C#代码你去控制时机,然后有一个函数专门用来把特定的物体的shader给替换掉。

那么这个函数自然也就需要两个参数了,一个是替换谁,一个是用什么shader来给人家做替换。

替换成shader这个不用多说,直接指定一个就好,而替换谁,这个其实就是和我们的render type有关系了。

首先说我们用作替换的shader,它里面可以有多个subShader,而每一个subShader有自己的 type。

我们被替换的shader也有他的type。所以替换就是:根据我们用作替换的shader的type,去看看物体的type,看看是否相同,如果相同那就给替换掉。

没有的话,这个物体就不被渲染。

当然上边两种情况,和第二个参数没啥关系,只传递进去一个shader,也可以做到啊,第二个参数有啥用呢?

当第二个参数传进去一个""空字符串的时候,就不一样了,这时候不管三七二十一,所有物体都被我们的替换shader来渲染。

而上边那种根据type比对的情况,第二个参数常常写"RenderType"

之前的透明效果其实是不能够满足很多的半透明的需求的,而且他是base on fragment的,是binary 的,所以会有锯齿。

这里由于fade他又有自己的一套rendertype和queue设置,所以这里越来越多,不过好在他们是一套一套的,一对一的。

所以可以把两个类型封装成一个结构,然后创建一个结构的数组,把这几套都写出来放在数组中。然后直接从数组中取。

这里有一个小trick,就是使用mode,也就是枚举,其实他是从0开始的int。然后这里直接拿他作为下标采样。

只需要我们提前把mode数组放置元素的顺序和枚举renderingMode的顺序做一个对应就好了。

虽说现在我们还没有加入任何的半透明的代码,但是,当我们把模式切换成半透明渲染的时候,我们可以看到渲染的顺序以及draw call的名字都变了。

变成了Transparent的,因为只要我们修改成半透明,C#那边就会对shader加上我们自定义的renderQueue。

然后整个物体的渲染队列就变了,他的渲染顺序啥的就会跟着变。

最后这里又提到渲染顺序的问题,其实渲染队列的核心就是顺序,而顺序的目的基本上就是为了性能考虑。

因为渲染一些特殊的效果,像透明等,这都要比普通渲染有着更多的性能消耗,所以一般把这些放在后面渲染,如果提前渲染的话有可能渲染了又被别人挡住了,就白渲染了。

两个重点,一个是他俩要写在一个#pragma里面,而且最前面需要加一个下划线。

这里关于混合模式的修改,涉及多pass的情况。

实现出半透明效果之后,这里又来问题了,我们直接hard code写上那些Blend模式,如果我们把rendeirng mode换成其他的就又不行了。

所以这里角度把他们搞成变量,然后在C#中来处理一下。

这里前面的关键词其实是可以不加的,因为我们是自定义UI,加上也没问题。

然后在我们C#中控制的时候,这些东西恰恰和我们之前写的RenderingSetting一样,正好是一套一套的。

这里是直接修改属性,所以SetInt接口。

这里可以对batching进行一个打开关闭,

如果没有关闭场景里的两个面片合并渲染了,我们没办法看出他们的实际渲染顺序。

 

打开之后,

在这个视角下是先远处的那个,然后近处的。

换到这边的视角下,还是先远的那个,再近的这个。

其实第二个图中近的那个,他的高度要高一些,也就是它把后面的那个给挡住了,这时候先第二个再第一个问题不大。

但是第一个图就问题大了。先渲染的远处的,也就是更高的,更高的这个挡住了低的那个的一部分,它渲染之后写入了深度,

近处的那个再渲染的时候,就被裁剪掉了。

透明物体Unity先后再前,因为透明要和后面的混合!

关于排序还有一个问题,就是因为排序使用的是中心点的位置,所以当物体体积很大的时候或者是一个大的面片的时候。

两个物体之间很容易overlap,这样当摄像机视角移动的时候,会导致他们的相对顺序发生变化,渲染顺序也随着变化。/

那么效果上就会带来一些突变。

其实如果我们解决了刚刚上边的那个大问题,其实这个混合先后顺序其实都好说,有点差错也没那么明显/

所以结论就是,关闭深度写入。

这里又深入了一层,就是我们的fade效果还是不够逼真,fade是把最终的颜色进行了一个混合fade,但是实际情况,高光往往不会进行fade

不会进行混合,而是直接累加上去。而diffuse部分是需要混合的。

所以为了实现这一点,我们把

系数这么设置,对于src部分直接是1,意思是被混合的那个比例完全不变,而当前像素的系数从之前的alpha变成1.

这是为啥,因为我们要做的是,自己手动把alpha给乘上去,然后混合的时候只需要乘以1就好了。这样和之前就一样了。

不同的是,我们自己乘以alpha,我们可以只在diffuse部分乘以alpha,而之前的fade玩法是让混合的时候帮我们乘/。

那就是对颜色整体,包括diffuse和specular都去相乘了。

我们在混合之前乘以Alpha这招叫做:premultiplied alpha blend

这里在写的时候,出了一个bug,就是我们在面板中选中这个transparent模式,但是他不会又反应,也就是不能切换到这个transparent模式。

就是选项无论本来是啥,opaque也好,cutout也好,我们点击切换到transparent都会直接换成Opaque。

分析下这个流程,在某一次我们点击了transparency之后,他的mode就会返回transparent,这个无疑。

然后进入下面的if,首先会设置关键词,这个也没问题。然后相关的setting,然后就是下一帧了,从头进来,会判断

Keyword Enabled 这个是材质面板去控制:

材质面板去查看这个关键词是否激活。这里其实根据debug输出判断就是这里的问题。

返回的是false。

然后根据最后的原因:是我在Shader中漏掉了pragma声明。

应该可以推断出这里可能检测关键词是否enabled的时候,他和shader内部是否声明这个关键词是有关系的。。

然后再往下分析,由于本该进入第三个if,但是没进,说明现在都没有进入,所以最后显示的标签就变成了Opaque,

相关的效果也是Opaque的。

这个是最终的效果。

这里由于是一个预乘操作,所以这里有些情况是直接把预乘之后的结果放在了纹理中,

其实周围多了一些白色的,形成一个正方体的轮廓,这是因为环境光的作用,高光的作用。

最后考虑 energy conservation,

透明是投射,而高光不受透明的影响,他是反射部分,当我们考虑了反射之后,我们的透明度其实要改变的。

因为能量守恒,总光的数量是不变的。

所以高光考虑之后,透射的光就会变少。

关于是公式的理解,从透明度上理解,1-a就是透明度,然后他和反射率呈反比,所以这里乘以1-r。

得到了透明度,再用1减去就是不透明度。

公式化简一下,写code。

这里也都只是 simplification,因为实际的透射光的多少和体积也有关系。

最后说了一个one way mirror,意思就是表现出one way是因为一边太暗了,如果两边一样强的光,就不会又这个结果了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值