美文网首页
URP卡通水体渲染

URP卡通水体渲染

作者: BacteriumFox | 来源:发表于2021-02-01 20:22 被阅读0次

    URP卡通水体渲染

    效果图

    思路

    复刻于:https://roystan.net/articles/toon-water.html

    总体思路为:使用深度纹理来渐变水体颜色,使用噪声来营造漂浮的泡沫,使用法线纹理计算物体边缘来增加水面物体的泡沫。

    需要注意的是,URP管线下,在Shader中的法线深度纹理是需要扩展URP的管线功能才会有的。

    1. 我们通过顶点在屏幕空间中的位置与深度值来计算水深。

                      //--1.处理深度法线纹理
                      // 采样法线深度纹理(xy/w将坐标从正交投影转换为透视投影)
                      half4 sample = SAMPLE_TEXTURE2D(_CameraDepthNormalsTexture, sampler_CameraDepthNormalsTexture, i.screenPosition.xy / i.screenPosition.w);
                      float depth;
                      float3 normal;
                      DecodeDepthNormal(sample, depth, normal);
                      //输出深度,从深度法线纹理中获取到的深度亮度值相比直接从深度纹理不够高,*1000以提升亮度
                      depth = depth * 1000;
                      
                      // 水的表面和它背后的物体之间的距离,以单位表示。
                      float depthDifference = depth - i.vertex.w;
                      return depthDifference;
      
    2. 使用得到的水深深度值,插值最浅水面颜色和最深水面颜色。

    3. 接下来来使用噪声纹理模拟漂浮的泡沫,其中,我们使用一个截止阈值来控制漂浮泡沫的多少。

                      //--3.通过噪声绘制泡沫
                      // 通过噪声纹理获取表面的波浪
                      float surfaceNoiseSample = SAMPLE_TEXTURE2D(_SurfaceNoise, sampler_SurfaceNoise, i.noiseUV).r;
                      return waterColor + surfaceNoiseSample;
      
    4. 利用深度纹理来插值漂浮泡沫的截止阈值,以此来模拟水面较浅的部分的泡沫

    5. 使用一张RG通道的法线纹理和Vector.XY来模拟泡沫的浮动轨迹,来让水面动起来

    6. 使用物体法线纹理与水面的法线纹理进行点击,来得到水体中物体与水平面的交界面。将该值叠加到控制泡沫的阈值来实现水面物体边缘的泡沫效果。

      1. 我们改进Shader默认的透明度混合方案,来优化水面的泡沫透明度。

        float4 alphaBlend(float4 top, float4 bottom)
        {
          float3 color = (top.rgb * top.a) + (bottom.rgb * (1 - top.a));
          float alpha = top.a + bottom.a * (1 - top.a);
        
          return float4(color, alpha);
        }
        
      2. 最后使用smoothstep函数来优化泡沫边缘的锯齿。

        #define SMOOTHSTEP_AA 0.01
        
        …
        
        float surfaceNoise = smoothstep(surfaceNoiseCutoff - SMOOTHSTEP_AA, surfaceNoiseCutoff + SMOOTHSTEP_AA, surfaceNoiseSample);
        

    完整代码

    Shader "URP/ToonWater"
    {
        Properties
        {
            // 当水面最浅时,水的颜色
            _DepthGradientShallow ("Depth Gradient Shallow", Color) = (0.325, 0.807, 0.971, 0.725)
            
            // 当水面最深的时候,水的颜色
            _DepthGradientDeep ("Depth Gradient Deep", Color) = (0.086, 0.407, 1, 0.749)
            
            // 水面下的最大深度,低于该值水面颜色不在发送变换。
            _DepthMaxDistance ("Depth Maximum Distance", Float) = 1
            
            // 渲染物体相交于表面所产生的泡沫颜色。
            _FoamColor ("Foam Color", Color) = (1, 1, 1, 1)
            
            // 用来产生波浪的噪声纹理。
            _SurfaceNoise ("Surface Noise", 2D) = "white" { }
            
            // 用于控制噪音滚动速度
            _SurfaceNoiseScroll ("Surface Noise Scroll Amount", Vector) = (0.03, 0.03, 0, 0)
            
            // 截止阈值,用于控制漂浮泡沫数量
            _SurfaceNoiseCutoff ("Surface Noise Cutoff", Range(0, 1)) = 0.777
            
            // 这个纹理的红色和绿色通道用来抵消噪声纹理,从而在波中产生失真。
            _SurfaceDistortion ("Surface Distortion", 2D) = "white" { }
            
            // 用这个值乘以失真。
            _SurfaceDistortionAmount ("Surface Distortion Amount", Range(0, 1)) = 0.27
            
            // 控制水面以下的距离将有助于渲染泡沫。
            _FoamMaxDistance ("Foam Maximum Distance", Float) = 0.4
            _FoamMinDistance ("Foam Minimum Distance", Float) = 0.04
            
            ///_FoamDistance ("Foam Distance", Float) = 0.4
        }
        SubShader
        {
            Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
            
            HLSLINCLUDE
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            
            CBUFFER_START(UnityPerMaterial)
            
            float4 _BaseMap_ST;
            
            float4 _DepthGradientShallow;
            float4 _DepthGradientDeep;
            float4 _FoamColor;
            
            float _DepthMaxDistance;
            float _FoamMaxDistance;
            float _FoamMinDistance;
            
            float _SurfaceNoiseCutoff;
            float _SurfaceDistortionAmount;
            
            float2 _SurfaceNoiseScroll;
            
            /// float _FoamDistance;
            CBUFFER_END
            ENDHLSL
            
            Pass
            {
                Tags { "LightMode" = "UniversalForward" "Queue" = "Transparent" }
                Blend SrcAlpha OneMinusSrcAlpha
                HLSLPROGRAM
                
                #define SMOOTHSTEP_AA 0.01
                
                #pragma vertex vert
                #pragma fragment frag
                
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
                
                struct appdata
                {
                    float4 vertex: POSITION;
                    float4 uv: TEXCOORD0;
                    float3 normal: NORMAL;
                };
                
                struct v2f
                {
                    float4 vertex: SV_POSITION;
                    float2 noiseUV: TEXCOORD0;
                    float2 distortUV: TEXCOORD1;
                    float4 screenPosition: TEXCOORD2;
                    float3 viewNormal: NORMAL;
                };
                
                
                TEXTURE2D(_SurfaceDistortion);
                SAMPLER(sampler_SurfaceDistortion);
                float4 _SurfaceNoise_ST;
                // 水波噪声纹理
                TEXTURE2D(_SurfaceNoise);
                SAMPLER(sampler_SurfaceNoise);
                float4 _SurfaceDistortion_ST;
                
                // 声明深度法线纹理,注意该名称是指定的
                TEXTURE2D(_CameraDepthNormalsTexture);
                SAMPLER(sampler_CameraDepthNormalsTexture);
                
                v2f vert(appdata v)
                {
                    v2f o;
                    // 初始化变量
                    ZERO_INITIALIZE(v2f, o);
                    
                    VertexPositionInputs vertexPositionInputs = GetVertexPositionInputs(v.vertex.xyz);
                    o.vertex = vertexPositionInputs.positionCS;
                    // 计算顶点在着色器中的屏幕空间位置
                    o.screenPosition = ComputeScreenPos(o.vertex);
                    // 泡沫噪声纹理
                    o.noiseUV = TRANSFORM_TEX(v.uv, _SurfaceNoise);
                    // 漂浮失真纹理
                    o.distortUV = TRANSFORM_TEX(v.uv, _SurfaceDistortion);
                    
                    // 水面在视角空间的法线纹理
                    // o.viewNormal = COMPUTE_VIEW_NORMAL;
                    o.viewNormal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
                    return o;
                }
                
                // 解码深度
                inline float DecodeFloatRG(float2 enc)
                {
                    float2 kDecodeDot = float2(1.0, 1 / 255.0);
                    return dot(enc, kDecodeDot);
                }
                
                // 解码法线
                float3 DecodeNormal(float4 enc)
                {
                    float kScale = 1.7777;
                    float3 nn = enc.xyz * float3(2 * kScale, 2 * kScale, 0) + float3(-kScale, -kScale, 1);
                    float g = 2.0 / dot(nn.xyz, nn.xyz);
                    float3 n;
                    n.xy = g * nn.xy;
                    n.z = g - 1;
                    return n;
                }
                
                inline  void  DecodeDepthNormal(float4 enc, out float depth, out float3 normal)
                {
                    depth = DecodeFloatRG(enc.zw);
                    normal = DecodeNormal(enc);
                }
                
                //混合两种颜色使用相同的算法,我们的着色器使用混合屏幕。这通常被称为“普通混合”,类似于Photoshop等软件如何混合两个图层。
                float4 alphaBlend(float4 top, float4 bottom)
                {
                    float3 color = (top.rgb * top.a) + (bottom.rgb * (1 - top.a));
                    float alpha = top.a + bottom.a * (1 - top.a);
                    
                    return float4(color, alpha);
                }
                
                half4 frag(v2f i): SV_Target
                {
                    //--1.处理深度法线纹理
                    // 采样法线深度纹理(xy/w将坐标从正交投影转换为透视投影)
                    half4 sample = SAMPLE_TEXTURE2D(_CameraDepthNormalsTexture, sampler_CameraDepthNormalsTexture, i.screenPosition.xy / i.screenPosition.w);
                    float depth;
                    float3 normal;
                    DecodeDepthNormal(sample, depth, normal);
                    //输出深度,从深度法线纹理中获取到的深度亮度值相比直接从深度纹理不够高,*1000以提升亮度
                    depth = depth * 1000;
                    
                    // 水的表面和它背后的物体之间的距离,以单位表示。
                    float depthDifference = depth - i.vertex.w;
                    //return depthDifference;
                    
                    //--2.绘制颜色
                    // 根据深度插值水的颜色。
                    float waterDepthDifference01 = saturate(depthDifference / _DepthMaxDistance);
                    float4 waterColor = lerp(_DepthGradientShallow, _DepthGradientDeep, waterDepthDifference01);
                    // return waterColor;
                    
                    //--3.通过噪声绘制泡沫
                    // 通过噪声纹理获取表面的波浪
                    // float surfaceNoiseSample = SAMPLE_TEXTURE2D(_SurfaceNoise, sampler_SurfaceNoise, i.noiseUV).r;
                    // return waterColor + surfaceNoiseSample;
                    
                    //--6.通过偏移来做泡沫漂浮动画
                    /// float2 noiseUV = float2(i.noiseUV.x + _Time.y * _SurfaceNoiseScroll.x, i.noiseUV.y + _Time.y * _SurfaceNoiseScroll.y);
                    /// float surfaceNoiseSample = SAMPLE_TEXTURE2D(_SurfaceNoise, sampler_SurfaceNoise, noiseUV).r;
                    
                    //--7.通过失真纹理加强泡沫左右漂浮效果
                    float2 distortSample = (SAMPLE_TEXTURE2D(_SurfaceDistortion, sampler_SurfaceDistortion, i.distortUV).xy * 2 - 1) * _SurfaceDistortionAmount;
                    // 通过噪声纹理获取表面的波浪
                    float2 noiseUV = float2((i.noiseUV.x + _Time.y * _SurfaceNoiseScroll.x) + distortSample.x, (i.noiseUV.y + _Time.y * _SurfaceNoiseScroll.y) + distortSample.y);
                    float surfaceNoiseSample = SAMPLE_TEXTURE2D(_SurfaceNoise, sampler_SurfaceNoise, noiseUV).r;
                    
                    //--4.通过阈值来控制泡沫数量
                    /// 通过阈值来控制漂浮泡沫数量
                    ///  float surfaceNoise = surfaceNoiseSample > _SurfaceNoiseCutoff ? 1: 0;
                    ///  return waterColor + surfaceNoise;
                    
                    //--6.通过比对法线来实现物体周围的泡沫
                    // 比对所有物体的法线与水平面法线来获取物体的边缘
                    float3 normalDot = saturate(dot(normal, i.viewNormal));
                    //return float4(normal,1);
                    float foamDistance = lerp(_FoamMaxDistance, _FoamMinDistance, normalDot.x);
                    float foamDepthDifference01 = saturate(depthDifference / foamDistance);
                    
                    //--5.通过深度阈值来控制边缘泡沫
                    // 通过深度来控制泡沫数量,来绘制边缘泡沫
                    ///float foamDepthDifference01 = saturate(depthDifference / _FoamDistance);
                    float surfaceNoiseCutoff = foamDepthDifference01 * _SurfaceNoiseCutoff;
                    // 通过阈值来控制漂浮泡沫数量
                    ///float surfaceNoise = surfaceNoiseSample > surfaceNoiseCutoff ? 1: 0;
                    ///return waterColor + surfaceNoise;
                    
                    //--8.优化抗锯齿
                    float surfaceNoise = smoothstep(surfaceNoiseCutoff - SMOOTHSTEP_AA, surfaceNoiseCutoff + SMOOTHSTEP_AA, surfaceNoiseSample);
                    
                    //--7.使用自定义混合方式改进泡沫颜色
                    float4 surfaceNoiseColor = _FoamColor;
                    surfaceNoiseColor.a *= surfaceNoise;
                    return alphaBlend(surfaceNoiseColor, waterColor);
                }
                ENDHLSL
                
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:URP卡通水体渲染

          本文链接:https://www.haomeiwen.com/subject/oofoaktx.html