zoukankan      html  css  js  c++  java
  • Unity快速实现平行光体积光(URP)

    体积光的光源可以是平行光、探照灯、点光源等,我们今天先来看看平行光如何制作体积光。

    体积光的原理网上已经有很多了,这里就不赘述了。着重快速实现:

    Shader "Unlit/VolumetricLightingShader"
    {
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            _Intensity("Intensity",float) = 1
        }
        SubShader
        {
            Tags { "RenderType"="Opaque" }
            LOD 100
    
            Pass
            {
                HLSLPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #define MAIN_LIGHT_CALCULATE_SHADOWS
                #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/Shadows.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/Shaders/PostProcessing/Common.hlsl"
                #define STEP_TIME 64
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                    float3 worldPos:TEXCOORD1;
                    float4 screenPos :TEXCOORD2;
                };
    
                TEXTURE2D_X_FLOAT(_CameraDepthTexture); SAMPLER(sampler_CameraDepthTexture);
                TEXTURE2D(_CameraOpaqueTexture); SAMPLER(sampler_CameraOpaqueTexture);
                TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
                float _Intensity;
                v2f vert (appdata v)
                {
                    v2f o;
                    
                    o.vertex = TransformObjectToHClip(v.vertex);
                    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                    o.screenPos = ComputeScreenPos(o.vertex);
                    return o;
                }
    
                half4 frag (v2f i) : SV_Target
                {
                    half2 screenPos = i.screenPos.xy / i.screenPos.w;
                    //rebuild world position according to depth
                    float depth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture,sampler_CameraDepthTexture, screenPos).r;
                    depth = Linear01Depth(depth, _ZBufferParams);
                    float2 positionNDC = screenPos * 2 - 1;
                    float3 farPosNDC = float3(positionNDC.xy,1)*_ProjectionParams.z;
                    float4 viewPos = mul(unity_CameraInvProjection,farPosNDC.xyzz);
                    viewPos.xyz *= depth;
                    float4 worldPos = mul(UNITY_MATRIX_I_V,viewPos);
                    
                    float noise = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, screenPos*3).r;
                    float3 startPos = i.worldPos;
                    float3 dir = normalize(worldPos - startPos);
                    startPos += dir * noise;
                    worldPos.xyz += dir * noise;
                    float len = length(worldPos - startPos);
                    float3 stepLen = dir * len / STEP_TIME;
                    half3 color = 0;
    
                    half3 sceneColor = SAMPLE_TEXTURE2D(_CameraOpaqueTexture, sampler_CameraOpaqueTexture, screenPos).rgb;
                    
                    UNITY_LOOP
                    for (int i = 0; i < STEP_TIME; i++)
                    {
                        startPos += stepLen;
                        float4 shadowPos = TransformWorldToShadowCoord(startPos);
                        float intensity = MainLightRealtimeShadow(shadowPos)*_Intensity;
                        color += intensity*_MainLightColor.rgb;
                    }
                    
                    color /= STEP_TIME;
                    color += sceneColor;
                    return half4(color.xyz,1);
                }
                ENDHLSL
            }
        }
    }

    代码如上,可以看到体积光的基础实现非常简单,在片元着色器步进采样shadowmap,看当前步进的位置是否处于阴影区域,如果不处于阴影区域,就叠加强度,最后形成散射效果。

    此shader直接挂在一个quad片上,能把相机遮住就OK,这种方式适合快速实现各种屏幕空间的效果,等效果觉得OK了,然后再慢慢的转移到URP的RenderFeature中。

    用quad片直接放在相机前面制作上有以下好处:

      1.不需要写任何c#代码,一个shader文件全搞定

      2.不需要计算屏幕空间近裁切面的世界坐标的位置,直接取片的position就可以,非常的方便

      3.快,主要就是快,这里指的快是写代码敲得快,哈哈哈~

    Shader有两个参数,第一个参数是传入一张纹理,这里需要传入噪声纹理,最好是blue noise,这个纹理是用来dither步进距离特别大的时候产生的分层效果,使得分层感不那么明显。通过调整“#define STEP_TIME 64”中的数值来控制步进次数。这里可以看一下没有dither(上图)和有dither(下图)的效果对比:

     

     可以看到分层感的改善还是非常明显的。需要注意的是这里的实现只是简单的步进叠加强度,没有进行任何的散射算法实现,常用的散射算法有米氏散射瑞利散射,各位童鞋可自行查看,套用公式即可,这里给出这个框架,公式随便套~

    如果代码看不懂,这里给出几个关键词可供百度,百度完自然就能看懂了:

    1.Raymarching

    2.基于深度还原世界坐标

    3.ShadowMap的实现原理

    如果看完还有问题,欢迎博客讨论区留言~  或者加qq群:104794354

  • 相关阅读:
    OO第三单元总结
    OO第二单元总结
    OO第一单元总结
    OO第四单元总结
    OO第三单元总结
    OO第二单元总结
    OO第一单元总结
    BUAA_OO_2020_Total_Summary
    BUAA_OO_2020_Unit3_Summary
    BUAA_OO_2020_Unit2_Summary
  • 原文地址:https://www.cnblogs.com/shenyibo/p/14185656.html
Copyright © 2011-2022 走看看