(一)渲染顺序

唔姆这一章,我们将学习到如何制作逼真的半透明效果。before复习如何实现的代码,再重复提一遍渲染顺序吧。

渲染的顺序对半透明物体的渲染很重要!随便想想就能想通啦,我就不多说了。

但不论如何调整渲染的顺序,总是会有效果很差的时候,就像上面的两种,感觉和以前学depthbuffer时遇到的问题差不多。毕竟很难说这样的几个模型谁先谁后嘛~

解决办法书里给了,但也比较粗暴。一种是选择把模型拆分再进行排序,另一种只能让透明通道更加柔和,使穿插看起来不那么明显。当然,也可以开启深入写入来近似模拟物体的半透明。这点在后面的代码有写到嗷。

下面就要进入到代码时间了,哈吉马路哟~

(二)透明度测试

听名字,还挺哈人的,在了解原理后,这确实是个非常实诚有粗暴的算法。

核心算法就是在frag的这么几行。

只要alpha通道的值没达标就直接吧整个变都剔除了。很基础的想法,让人觉得我上我也行,但很多算法都是这么一步步从笨蛋到巧妙的,我从来没期待过,我能一下子写出来一开始就很健壮又漂亮的代码。

但多改几次,总是会比上次好一点点,这样马马虎虎的代码我还是有自信的。多写几年,上面的想想也不是不可以,所以这半年要努力、奋斗!

总之,回到透明度测试来,在shader的最后记得设置好Fallback “Transparent/Cutout/VertexLit”。至于效果,有当然还是有啦,但是极端了点,透明效果的边缘往往会有锯齿,这是在边界处理纹理的透明度变化的精度问题。为了更好的透明效果,就要引出下面要讲的透明度混合啦!

(三)透明度混合

透明度混合之前,我们来看下Unity关于Blend的一些命令,非常重要!感觉我总是打完就完了(doge)

上面的混合因子,看不懂很正常~,毕竟他要一两个小节以后才正式讲解,真是不合理!我直接贴过来了。

唔姆,注意这里的OneMinus是1-的意思,后面我们用到Blend SrcAlpha OneMinusSrcAlpha,实际上就是Orgb=SrcAlpha*Srgb+(1-SrcAlpha)*Drgb的意思啦。具体的图片我就放到下面了,但原理还是一定要记住点的!

当然混合类型布置上面的一个举例,他给的实在是太多了,有机会都试一下,真得去试试,不然咋能学会呢?

效果如下:

混合真是博大精深呢~

哟西,回到shader的编写身上。在透明度混合中需要添加全新的tag。

这三个tag基本是所有要透明效果的shader都要加上的,第一个就不讲了,放到Transparent的队列里,第二个让shader不收到projectors的影响(虽然我还不知道啥事projectors),第三个用来指明该shader使用了透明度混合。

第二个要注意的就是这边!!!我这边尝试这个Tags{“LightMode”=”Forwardbase”}就不得行。这是怎么回事呢?我写到这的时候突然感觉可能是Forward打错字了,希望不要这样,这样就太笨蛋了。

后面两行很重要,要关了深度写入,并开始混合。

以上。下面我还是放个完整代码吧,混合还是要好好看看的。

Shader "Unity Study/Chapter8/AlphaBlend"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color Tint",COLOR)=(1,1,1,1)
        _AlphaScale("Alpha Scale",Range(0,1))=1   //控制整体透明度
    }
    SubShader
    {
        Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout" }  

        Pass
        {
            //下面三行重点,关闭深度写入,并将源颜色的混合因子改为SrcAlpha,目标的设为OneMinusSrcAlpha
            //Tags{"LightMode"="ForwordBase"}   //为何我这里加上这一行就不行,草还真是拼错了

            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc" // for _LightColor0
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                float3 worldNormal:TEXCOORD0;
                float worldPos:TEXCOORD1;
                float2 uv : TEXCOORD2;
            };


            float4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _AlphaScale;
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
                o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldNormal=normalize(i.worldNormal);
                fixed3 worldLightDir=normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed3 albedo=tex2D(_MainTex,i.uv).rgb*_Color.rgb;
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
                fixed3 diffuse=_LightColor0.rgb*albedo*max(0,dot(worldNormal,worldLightDir));

                fixed3 finalColor=ambient+diffuse;
                return fixed4(finalColor,tex2D(_MainTex,i.uv).a,_AlphaScale);
            }
            ENDCG
        }
    }
    //FallBack "Transparent/VertexLit"     //这里的fallback跟之前的不一样,要记住!!!!!!!!!
}

(四)开深度写入的透明效果

为什么要开深度写入呢?毕竟世界上总有一些鬼畜的模型,只能进行逐像素的深度排序后才能好好地混合。

实现比想象的简单好多,快乐,但会消耗点性能,道理之中。

大体原理,就是真的另开了一个pass深度写入了一遍,但使用ColorMask不对颜色缓冲做任何操作,只写深度,之后一个pass就跟上面写的一模一样了。

(五)双面渲染的透明效果

核心就是这一句 Cull Back/Front,这一行管理剔除哪些

实在是没想到,但做完以后会有种理所当然的感觉,不可思议。

AlphaTest和AlphaBlend都能实现。

AlphaTest真的简单的有点夸张了,只要把背面剔除关掉就好了,真好。

到了AlphaBlend的双面渲染就没这么ez了,毕竟,你想啊~,AlphaBlend需要关闭深度写入并向保证从后向前渲染最好。要是现在把背面剔除一关,我们就无法保证一个物体的正背面图元的渲染顺序了。

解决方案就把原来的pass变两个就好了,一个只渲染背面,一个只渲染正面,代码如下,很简单吧~

就此,透明效果的章节到此结束,明天一定要写第九章呀!

您必须 登录 才能发表评论