• Unity中的半透明阴影


    在Unity中渲染半透明阴影可以使用Unity提供的dither texture。在这之前,先考虑一般半透明物体的渲染流程:

    • 设置render queue为Transparent,这样不透明的物体会先渲染,然后位于被不透明物体遮挡的透明物体就可以不必渲染,减少开销
    • 设置render type为Transparent,便于一些replacement操作
    • 设置blend mode,例如fade是srcBlend = SrcAlpha,dstBlend = OneMinusSrcAlpha,而Transparent是srcBlend = One,dstBlend = OneMinusSrcAlpha
    • 关闭深度写入,zwrite = false

    Unity中的半透明阴影本质上是不透明的,只是对dither texture进行采样,根据采样的结果,clip掉一些fragment,使得shadow caster过程中只有一部分阴影信息会被绘制到shadowmap上。Unity builtin shaders提供的参考写法如下:

    struct Interpolators {
    	UNITY_VPOS_TYPE vpos : VPOS;
        ...
    };
    
    float4 MyShadowFragmentProgram (Interpolators i) : SV_TARGET {
        ...
    	half alphaRef = tex3D(_DitherMaskLOD, float3(vpos.xy*0.25,alpha*0.9375)).a;
        clip(dither - 0.01);
        ...
    }
    

    vpos表示的是当前像素在screen space下的坐标,_DitherMaskLOD是一个尺寸为4×4×16的3D纹理,这个可以从frame debug中看出:

    这个纹理长啥样呢?我们可以写一个shader手动把它输出:

    Shader "Custom/TextureViewShader"
    {
        Properties
        {
        }
        SubShader
        {
            Tags { "RenderType"="Opaque" }
            LOD 100
    
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float4 vertex : SV_POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                sampler3D _DitherMaskLOD;
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    return o;
                }
    
                fixed4 frag (v2f i) : SV_Target
                {
                    // sample the texture
                    i.uv *= 16;
                    fixed4 col = tex3D(_DitherMaskLOD, float3(i.uv, floor(i.uv.x) * 0.0625)).a;
                    return col;
                }
                ENDCG
            }
        }
    }
    

    从frame debug可知纹理是alpha8格式,因而只需输出alpha通道值:

    由于_DitherMaskLOD纹理是4×4×16的,我们实际上在v方向也重复了16次,因此真正的纹理长这样:

    可以看出,该纹理从4×4的全黑像素开始,随着维度z的增加,黑色像素每次减少1个,直至最后全部变成4×4的全白像素。通过这个规律,不难理解前面代码tex3D的采样坐标z的写法为alpha*0.9375。alpha表示透明度,0为完全透明1为完全不透明,而0.9375实际上就是15/16。这就是说在0的情况下采样的3D纹理是纯黑像素,会被clip掉,不会产生阴影;而1的情况下采样的3D纹理是全白像素,会完全产生阴影,就仿佛跟不透明物体一样。

    最后再看一下这个vpos*0.25是干啥的。vpos表示的是pixel在screen space下的坐标,x和y取值范围类似[0, screenWidth],[0, screenHeight]。乘以0.25的系数就是对取值范围进行缩放处理,换言之就是将_DitherMaskLOD纹理进行放大,使其更明显。可以看下不同缩放比例的效果对比:

    scale=1

    scale=0.25

    scale=0.0025

    最后提一点的是,由于半透明的物体,render queue设置为transparent,所以在平行光绘制阴影前的depth pass阶段,是不会把半透明物体的深度信息写入depth buffer的,不过在shadow caster阶段,半透明阴影的信息还是正常绘制到shadowmap中的。这些都可以从frame debug中看出来:

    如果你觉得我的文章有帮助,欢迎关注我的微信公众号(大龄社畜的游戏开发之路-

  • 相关阅读:
    Biba模型简介
    Fragment 与 Activity 通信
    小米2S 连接Ubuntu Android Studio
    【转】Android 实现“再按一次退出程序”
    取消 EditText 自动聚焦弹出输入法界面
    为Android Studio 项目手动下载gradle
    【转】Java读取文件方法大全
    sudo: /etc/sudoers 的模式为 0551,应为 0440
    Win7 下硬盘安装Linux Mint 17
    Linux Versus Windows, Ubuntu/Mint V XP/Vista/7
  • 原文地址:https://www.cnblogs.com/back-to-the-past/p/15394837.html
Copyright © 2020-2023  润新知