LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

UnityShader入门精要⑥(透明效果)

2021/10/17

本文为学习UnityShader时所记录的笔记,供学习产出和日后复习使用。

学习资料为冯乐乐(问就是我女神)的《UnityShader入门精要》

上次写博客是七号来着,已经过去有一段时间了,这段时间有点忙所以shader的学习一直被挤压

今天刚好离憨色演唱会过去了两个星期,日子过的还是蛮快的

这星期经历了科三一次过(现在在练科二),还有买了两本书(虚幻4和程序员职业素养)但也没什么时间看

这星期状态还可以,精神不错,就是学习知识的吸收率不太高,摸鱼现象还是有的(有待提升)

好消息是自己的c++现在还不错,有大佬建议我春招可以直接试试看冲大厂,我也是这么想的

考虑到之前写的博客又臭又长,更像是傻瓜指南而不是技术总结,接下来我会改变写博客的方式。

自己这星期学的蛮少的,时间是一个因素,难度也是一个因素

那么一库走!


透明度测试,透明度缓冲

透明效果由控制渲染问题的透明通道(Alpha Channel)来实现,即为RGB,A中的A。

有两种实现透明效果的方法:透明度测试(Alpha Test),透明度缓冲(Alpha Blending)

透明度测试:一个片元的透明度不满足条件(一般是低于阈值),就直接舍弃。

效果很极端,要么完全透明看不见,要么完全不透明就像什么都没做。无需关闭深度写入。

透明度缓冲:****关闭了深度写入,但没有关闭深度测试,即深度缓冲是只读的。


渲染顺序

为了得到正确的结果,引擎一般对物体进行排序,再渲染。

先渲染所有不透明物体,并开启深度测试和深度写入

其次将半透明物体按照从后往前(距离摄像机)的顺序渲染,开启深度测试但关闭深度写入。

重叠情况依然无法得到完美的解决,但可以通过分割网格或让透明通道更柔和来改善情况。


渲染队列标签

unity为解决渲染顺序问题提供了几个渲染队列(详见入门精要p165表格)

我们在unity中通过设置SubShader的Queue标签来决定模型归于哪个队列。

透明度测试:

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

透明度缓冲:

    SubShader{
        Tags{"Queue"="Transparent" }
        pass{//关闭深度写入 ZWrite Off

透明度测试

以基础的纹理采样代码作为样本进行改进

初始值多设一个Cutoff作透明度的判断条件

        _Cutoff("Alpha Cutoff",Range(0,1))=0.5

设渲染队列为AlphaTest,”IgnoreProjector”=”True”意思是Shader不会受投影器影响

RenderType则是将这个Shader划入提前定义好的组(类似于layer和tags?)

    SubShader{
        Tags{
            "Queue"="AlphaTest"
            "IgnoreProjector"="True"
            "RenderType"="TransparentCutput"
            }
        pass{
            Tags{"LightMode"="ForwardBase"}

在做纹理采样得到纹理值时,用a分量做一个裁剪

                fixed4 texColor=tex2D(_MainTex,i.uv);
                //Alpha Test
                clip(texColor.a-_Cutoff);

完成编写后,调大材质中的cutoff时,立方体上的网格会逐渐消失。


透明度混合

透明度测试更复杂,使用当前片元的透明度作为混合因子与已经储存在颜色缓冲中的颜色值进行混合。

进行混合需要使用混合指令Blend,其中我们使用了Blend SrcFactor DstFactor这种语义。

(源颜色乘以SrcFactor ,目标颜色乘以DstFactor)

(详细的混合命令请见入门精要p169)

以基础的纹理采样代码作为样本进行改进

材质还是用之前的主颜色Color,主材质MainTex,还有一个_AlphaScale用来控制透明度。

修改标签,关闭深度写入,设置混合因子(和上边的大同小异):

    SubShader{
        Tags{
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            }
        pass{
            Tags{"LightMode"="ForwardBase"}
            
            //关闭深度写入,设置混合因子
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha//这里的目标颜色混合因子被设为了OneMinusSrcAlpha,以得到半透明效果

接下来都一样,只需要修改片元着色器的返回值就行(a通道)

                return fixed4(ambient+diffuse,texColor.a*_AlphaScale);
                
                Fallback “Transparent/VertexLit”

完成编写后,可以调节材质面板的Alpha Scale以控制整体透明度。

关闭深度写入会带来各种问题,依然需要改进。


开启深度缓冲的半透明效果

我们使用两个pass来渲染模型,第一个pass开启深度写入,不输出颜色,是为了将模型的深度值写入深度缓冲。

第二个pass进行正常的透明度混合,因为上一个pass得到了逐像素的正确的深度信息,这个pass就可以进行逐像素的透明渲染。

多使用一个pass会对性能造成一定影响。

和上部分的标签设置相同:

    SubShader{
        Tags{
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            }
        Pass{
            ZWrite On
            ColorMask 0
            }
        
        pass{
            Tags{"LightMode"="ForwardBase"}
            
            //关闭深度写入,设置混合因子
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            
            CGPROGRAM
            ·······
            
            Fallback “Diffuse”

第一个Pass是为了将深度信息写入深度缓冲,ColorMask 0是意味着该Pass不写入任何颜色通道

然后第二个pass就是正常的逐像素的透明渲染。


双面渲染的透明效果

透明度测试

代码几乎与透明度测试一模一样,就加了一行代码:

    SubShader{
        Tags{
            "Queue"="AlphaTest"
            "IgnoreProjector"="True"
            "RenderType"="TransparentCutput"
            }
        pass{
            Tags{"LightMode"="ForwardBase"}
            Cull Off//关闭剔除功能

透明度缓冲

我们将渲染分为两个pass,一个只渲染背面,一个只渲染正面。

也就是在两个pass中使用Cull剔除不同朝向的渲染图元:

    SubShader
    {
        Tags{
            "Queue"="AlphaTest"
            "IgnoreProjector"="True"
            "RenderType"="TransparentCutput"
            }
        pass{
            Tags{"LightMode"="ForwardBase"}
            Cull Front
            ...
            }
            
        pass{
            Tags{"LightMode"="ForwardBase"}
            Cull Back
            ...
            }  
    }
 Fallback "Transparent/VertexLit"  
 }

呜呜呜今天晚上写这么少又摸鱼了,明天估计不能看直播水群边做了

(不要对直播有什么奇奇怪怪的责任感)