|
后处理深度雾相较于Unity内置的无效使用起来更加方便,不需要在每个着色器中添加相关的代码,而且性能和效果更好。并且效果可以覆盖天空盒。
使用Unity2022.2.0制作
新建FogRenderFeature.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class FogRenderFeature : ScriptableRendererFeature
{
public Shader m_shader;
public Color m_FogColor;
public float m_FogStart;
public float m_FogEnd;
public float m_NoiseCellSize;
public float m_NoiseRoughness;
public float m_NoisePersistance;
public Vector3 m_NoiseSpeed;
public float m_NoiseScale;
Material m_Material;
FogPass m_RenderPass = null;
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (renderingData.cameraData.cameraType == CameraType.Game)
renderer.EnqueuePass(m_RenderPass);
}
public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
{
if (renderingData.cameraData.cameraType == CameraType.Game)
{
m_RenderPass.ConfigureInput(ScriptableRenderPassInput.Color);
m_RenderPass.SetTarget(renderer.cameraColorTargetHandle, m_FogColor, m_NoiseCellSize, m_NoiseRoughness, m_NoisePersistance, m_NoiseSpeed, m_NoiseScale, m_FogStart, m_FogEnd);
}
}
public override void Create()
{
if (m_shader != null)
m_Material = new Material(m_shader);
m_RenderPass = new FogPass(m_Material);
}
protected override void Dispose(bool disposing)
{
CoreUtils.Destroy(m_Material);
}
}新建FogPass.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
internal class FogPass : ScriptableRenderPass
{
ProfilingSampler m_ProfilingSampler = new ProfilingSampler("FogBlit");
Material m_Material;
RTHandle m_CameraColorTarget;
Color m_FogColor;
//float m_FogDensity;
float m_FogStart;
float m_FogEnd;
float m_NoiseCellSize;
float m_NoiseRoughness;
float m_NoisePersistance;
Vector3 m_NoiseSpeed;
float m_NoiseScale;
public FogPass(Material material)
{
m_Material = material;
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
}
public void SetTarget(RTHandle colorHandle, Color fogColor ,float noiseCellSize, float noiseRoughness, float noisePersistance ,Vector3 noiseSpeed, float noiseScale,float fogStart, float fogEnd)
{
m_CameraColorTarget = colorHandle;
m_FogStart = fogStart;
m_FogEnd = fogEnd;
m_FogColor = fogColor;
m_NoiseCellSize = noiseCellSize;
m_NoiseRoughness = noiseRoughness;
m_NoisePersistance = noisePersistance;
m_NoiseSpeed = noiseSpeed;
m_NoiseScale = noiseScale;
}
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
ConfigureTarget(m_CameraColorTarget);
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
var cameraData = renderingData.cameraData;
if (cameraData.camera.cameraType != CameraType.Game)
return;
if (m_Material == null)
return;
CommandBuffer cmd = CommandBufferPool.Get();
using (new ProfilingScope(cmd, m_ProfilingSampler))
{
m_Material.SetColor("_FogColor", m_FogColor);
m_Material.SetFloat("_FogStart", m_FogStart);
m_Material.SetFloat("_FogEnd", m_FogEnd);
m_Material.SetFloat("_NoiseCellSize", m_NoiseCellSize);
m_Material.SetFloat("_NoiseRoughness", m_NoiseRoughness);
m_Material.SetFloat("_NoisePersistance", m_NoisePersistance);
m_Material.SetVector("_NoiseSpeed", m_NoiseSpeed);
m_Material.SetFloat("_NoiseScale", m_NoiseScale);
m_Material.SetMatrix("_InverseView", renderingData.cameraData.camera.cameraToWorldMatrix);
Blitter.BlitCameraTexture(cmd, m_CameraColorTarget, m_CameraColorTarget, m_Material, 0);
}
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
CommandBufferPool.Release(cmd);
}
}
新建Shader
后处理雾的实现原理:最终的颜色 = lerp(雾效颜色,物体颜色,雾效混合因子)
雾效混合因子利用深度模拟雾效的衰减
常见的雾效有三种
Linear
雾从Start开始到End越来越浓,End之后也为最大浓度。(这次就是用得这种方法)
- z:是片段ClipSpace下的z
- dmin:就是下图的Start
- dmax:就是下图的End
Exponential
指数模式,有参数fogDensity(取值范围0-1),表示雾的浓度,浓度越大雾越大。
exp(-density*z)
Exponential Squared
指数平方,有参数fogDensity,越大表示雾的浓度。
exp(-(density*z)^2)
后处理雾效需要从View space Depth(深度图)重建世界位置
添加上3D Layered Noise 增加风吹动的效果
https://www.ronja-tutorials.com/?search=Noise
Shader "LY/Fog"
{
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
LOD 100
ZWrite Off Cull Off
Pass
{
Name "FogBlitPass"
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#pragma vertex Vert
#pragma fragment frag
TEXTURE2D_X(_CameraOpaqueTexture); SAMPLER(sampler_CameraOpaqueTexture);
TEXTURE2D_X(_CameraDepthTexture); SAMPLER(sampler_CameraDepthTexture);
float _Intensity;
float4x4 _InverseView;
half4 _FogColor;
float _FogDensity;
float3 _NoiseSpeed;
float _NoiseCellSize, _NoiseRoughness, _NoisePersistance, _NoiseScale,_FogStart,_FogEnd;
#define OCTAVES 4
float rand3dTo1d(float3 value, float3 dotDir = float3(12.9898, 78.233, 37.719))
{
//make value smaller to avoid artefacts
float3 smallValue = cos(value);
//get scalar value from 3d vector
float random = dot(smallValue, dotDir);
//make value more random by making it bigger and then taking the factional part
random = frac(sin(random) * 143758.5453);
return random;
}
float3 rand3dTo3d(float3 value)
{
return float3(
rand3dTo1d(value, float3(12.989, 78.233, 37.719)),
rand3dTo1d(value, float3(39.346, 11.135, 83.155)),
rand3dTo1d(value, float3(73.156, 52.235, 09.151))
);
}
float easeIn(float interpolator)
{
return interpolator * interpolator;
}
float easeOut(float interpolator)
{
return 1 - easeIn(1 - interpolator);
}
float easeInOut(float interpolator)
{
float easeInValue = easeIn(interpolator);
float easeOutValue = easeOut(interpolator);
return lerp(easeInValue, easeOutValue, interpolator);
}
float perlinNoise(float3 value)
{
float3 fraction = frac(value);
float interpolatorX = easeInOut(fraction.x);
float interpolatorY = easeInOut(fraction.y);
float interpolatorZ = easeInOut(fraction.z);
float cellNoiseZ[2];
[unroll]
for (int z = 0; z <= 1; z++)
{
float cellNoiseY[2];
[unroll]
for (int y = 0; y <= 1; y++)
{
float cellNoiseX[2];
[unroll]
for (int x = 0; x <= 1; x++)
{
float3 cell = floor(value) + float3(x, y, z);
float3 cellDirection = rand3dTo3d(cell) * 2 - 1;
float3 compareVector = fraction - float3x, y, z);
cellNoiseX[x] = dot(cellDirection, compareVector);
}
cellNoiseY[y] = lerp(cellNoiseX[0], cellNoiseX[1], interpolatorX);
}
cellNoiseZ[z] = lerp(cellNoiseY[0], cellNoiseY[1], interpolatorY);
}
float noise = lerp(cellNoiseZ[0], cellNoiseZ[1], interpolatorZ);
return noise;
}
float sampleLayeredNoise(float3 value,float noiseRoughness ,float noisePersistance)
{
float noise = 0;
float frequency = 1;
float factor = 1;
[unroll]
for (int i = 0; i < OCTAVES; i++)
{
noise = noise + perlinNoise(value * frequency + i * 0.72354) * factor;
factor *= noisePersistance;
frequency *= noiseRoughness;
}
return noise;
}
half4 frag(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
float4 color = SAMPLE_TEXTURE2D_X(_CameraOpaqueTexture, sampler_CameraOpaqueTexture, input.texcoord);
float depth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, input.texcoord).r;
depth = Linear01Depth(depth, _ZBufferParams);
//从深度重建世界空间位置
float2 p11_22 = float2(unity_CameraProjection._11, unity_CameraProjection._22);
float3 viewPos = float3((input.texcoord * 2 - 1) / p11_22, -1) * depth * _ProjectionParams.z;
float4 wposVP = mul(_InverseView, float4(viewPos, 1)); //_ViewToWorld
//3D分层Noise,增加风吹动的效果
float noise = sampleLayeredNoise(float3((wposVP.xyz + _NoiseSpeed * _Time.y) * _NoiseCellSize), _NoiseRoughness, _NoisePersistance);
if (depth < 1)
{
depth += depth * noise * _NoiseScale;
}
float fog = 1- saturate((_FogEnd - depth)/max(0,(_FogEnd-_FogStart)));
return lerp(color, _FogColor, fog);
}
ENDHLSL
}
}
} |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|