urp管线的自学hlsl之路 第十六篇 屏幕深度制作护盾特效
雪风carel
编辑于 2020年06月24日 00:39
收录于文集
共38篇

简单的利用屏幕深度特效

      上一篇一键说了屏幕颜色的获取,这一篇学习一下屏幕深度的内容,这里写了一个简单的特效,该特效在unity里很常见了,会连连看的童鞋分分钟能搞定。这里在urp里手写一遍加深理解。

    第一步,我们要获取屏幕uv。上一篇里讲了三种方式获取,而当时使用了第三种最简单的方式;该篇我们采取第二种------手动计算一遍屏幕uv。在顶点着色器里,计算屏幕uv,并把xy存储为未经透除的uv通道,zw正常存储。

顶点着色器里计算屏幕uv

      在片元着色器里进行透视除法,在检测一下平台矫正uv。这样我们就能得到正常的uv,当然如果你嫌弃这种方式麻烦,可以使用上一篇的方式获得屏幕uv。

屏幕uv的计算

第二步,拿到0-1线性的屏幕深度图。管线配置文件里要打开深度图,然后shader里采样这张图,这张深度图的名字是_CameraDepthTexture。

采样深度图

然后在顶点着色器里用屏幕uv去采样这张图,并用float Linear01Depth(float depth, float4 zBufferParam)函数去把它变换成线性深度图。

得到正常的深度缓冲的图

该函数定义在Common.hlsl里,注意它和build in的同名函数的参数不同,请注意。

深度图变换到0-1的线性深度函数

第三步,拿到模型像素在线性0-1的深度值。这个比较简单,直接取片元着色器里的HClip的Z分量,再次使用上面的函数即可得到正常深度。

得到模型像素的深度

把模型深度在和屏幕深度图相减,进行一些加减乘除手动微调,即可得到和场景模型接触的效果。

接触效果

第四步,计算一个菲涅尔,可以使用“入门精要”的公式计算,这里本人弄了个山寨版的,效果还行。

随意计算的菲涅尔,公式并不完全准确

山寨菲尼尔

第五步,计算扫光效果,利用世界坐标的y轴作为“uv”的滚动效果,这是本人在连连看里经常使用的一个公式,如果读者看不懂公式也没关系,可以手动在连连看里还原一下看看效果。

利用世界坐标制作扫光效果

光带会缓慢从下向上滚动,类型滚uv

第六步,采样一下主纹理,本人是在百度上搜了一张图,在ps里小修了一下拿来用。

百度随便找的一张图然后ps修了一下

把它贴到球上,调整一下tile,shader里顺便滚一下uv。

采样的时候滚uv

最后把它们全部加到一起,小特效就做出来了。

        本篇的内容重点并不是特效本身,而是如果获取屏幕深度并把它转到线性空间里,同时也说明了一下如何获取模型像素的线性深度值。有时候在水体的制作里也会用到:它和岸边的交界线的制作原理也是一致的,通过他可以做一些水体和岸,水中裸露的石头的交互;在配合上一篇的屏幕空间颜色来得到折射效果;在采样一下反射球/天空盒来得到反射效果;配合一下菲涅尔,来点法线的滚uv,(如果可以再来点去曲面细分+快速傅里叶变换的顶点动画)就可以做出一个漂亮的水体效果了。不吹了,再吹牛下去就吹不动了,下面附源码,直接复制时,若出现报错,是每行代码前面的空格错误,删掉即可。

       该篇完成后,下一篇的内容将是URP的自定义后处理。

Shader "WX/URP/depth&#​34;

{

    Properties

    {

        _MainTex("MainTex&#​34;,2D)="white&#​34;{}

       _BaseColor("BaseColor&#​34;,Color)=(1,1,1,1)

        _depthoffset("depthoffset&#​34;,float)=1

        [HDR]_emissioncolor("EmissionColor&#​34;,Color  )=(1,1,1,1)

        

    }

    SubShader

    {

        Tags{

        "RenderPipeline&#​34;="UniversalRenderPipeline&#​34;

        "Queue&#​34;="Transparent&#​34;

        }

        HLSLINCLUDE

        #include &#​34;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        //#include &#​34;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

        //#include &#​34;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"

        //#pragma  shader_feature_local _ADDLIGHT_ON 

        //#pragma multi_compile _ _MAIN_LIGHT_SHADOWS

        //#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE

        //#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS

        //#pragma multi_compile _ _SHADOWS_SOFT

        CBUFFER_START(UnityPerMaterial)

        float4 _MainTex_ST;

        half4 _BaseColor;

        float _depthoffset;

        float4 _emissioncolor;

        CBUFFER_END

        TEXTURE2D( _MainTex);

        SAMPLER(sampler_MainTex);

        SAMPLER(_CameraDepthTexture);

         struct a2v

         {

             float4 positionOS:POSITION;

             float4 normalOS:NORMAL;

             float2 texcoord:TEXCOORD;

         };

         struct v2f

         {

             float4 positionCS:SV_POSITION;

             float2 texcoord:TEXCOORD;

             

             float4 sspos:TEXCOORD2;

             float3 positionWS:TEXCOORD3;

             float3 normalWS:TEXCOORD4 ; 

         };

        ENDHLSL

        pass

        {

        Tags{

         "LightMode&#​34;="UniversalForward&#​34;

         "RenderType&#​34;="Transparent&#​34;

        }

        Blend  SrcAlpha OneMinusSrcAlpha 

            HLSLPROGRAM

            #pragma vertex VERT

            #pragma fragment FRAG

            v2f VERT(a2v i)

            {

                v2f o;

                o.positionCS=TransformObjectToHClip(i.positionOS.xyz);

                o.texcoord=TRANSFORM_TEX(i.texcoord,_MainTex);

                //屏幕坐标sspos,xy保存为未透除的屏幕uv,zw不变

                o.sspos.xy=o.positionCS.xy*0.5+0.5*float2(o.positionCS.w,o.positionCS.w);

                o.sspos.zw=o.positionCS.zw;

                o.positionWS=TransformObjectToWorld(i.positionOS.xyz);

                o.normalWS=normalize(TransformObjectToWorldNormal(i.normalOS.xyz));

                return o;

            }

            half4 FRAG(v2f i):SV_TARGET

            {

            float2 uv=i.texcoord;

            uv.x+=_Time.y*0.2;//滚uv,以每秒钟滚0.2圈的速度旋转

                half4 tex=SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,uv)*_BaseColor;

                //开始计算屏幕uv  

                i.sspos.xy/=i.sspos.w;//透除

                #ifdef UNITY_UV_STARTS_AT_TOP//判断当前的平台是openGL还是dx

                i.sspos.y=1-i.sspos.y;

                #endif//得到正常的屏幕uv,也可以通过i.positionCS.xy/_ScreenParams.xy来得到屏幕uv

                //计算(山寨简化版)菲涅尔

                float3 WSview=normalize(_WorldSpaceCameraPos-i.positionWS);

                float fre=(1-dot(i.normalWS,WSview))*_depthoffset;

                //计算缓冲区深度,模型深度

                float4 depthcolor= tex2D(_CameraDepthTexture,i.sspos.xy);

                float depthbuffer=Linear01Depth(depthcolor,_ZBufferParams);//得到线性的深度缓冲

                //计算模型深度

                float depth=i.positionCS.z;

                depth=Linear01Depth(depth,_ZBufferParams);//得到模型的线性深度

                float edge= saturate(depth-depthbuffer+0.005)*100*_depthoffset;//计算接触光

                //return edge;

                //计算扫光,这步看不懂建议用连连看还原,这是一个做特效的通用公式

                float flow=saturate( pow(1-abs(frac(i.positionWS.y*0.3-_Time.y*0.2)-0.5),10)*0.3);

                float4 flowcolor=flow*_emissioncolor;

                //return flowcolor*2;

                return float4(tex.xyz,edge+fre)+flowcolor;

            }

            ENDHLSL

        }

    }

        

}