627 lines
30 KiB
C#
627 lines
30 KiB
C#
//------------------------------------------------------------------------------------------------------------------
|
|
// Volumetric Fog & Mist 2
|
|
// Created by Kronnect
|
|
//------------------------------------------------------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
#if UNITY_2023_3_OR_NEWER
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
#endif
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
namespace VolumetricFogAndMist2 {
|
|
|
|
public class VolumetricFogRenderFeature : ScriptableRendererFeature {
|
|
|
|
public static class ShaderParams {
|
|
public const string LightBufferName = "_LightBuffer";
|
|
public static int LightBuffer = Shader.PropertyToID(LightBufferName);
|
|
public static int LightBufferSize = Shader.PropertyToID("_VFRTSize");
|
|
public static int MainTex = Shader.PropertyToID("_MainTex");
|
|
public static int BlurRT = Shader.PropertyToID("_BlurTex");
|
|
public static int BlurRT2 = Shader.PropertyToID("_BlurTex2");
|
|
public static int MiscData = Shader.PropertyToID("_MiscData");
|
|
public static int ForcedInvisible = Shader.PropertyToID("_ForcedInvisible");
|
|
public static int DownsampledDepth = Shader.PropertyToID("_DownsampledDepth");
|
|
public static int BlueNoiseTexture = Shader.PropertyToID("_BlueNoise");
|
|
public static int BlurScale = Shader.PropertyToID("_BlurScale");
|
|
public static int Downscaling = Shader.PropertyToID("_Downscaling");
|
|
public static int ScatteringData = Shader.PropertyToID("_ScatteringData");
|
|
public static int ScatteringTint = Shader.PropertyToID("_ScatteringTint");
|
|
|
|
public static int BlurredTex = Shader.PropertyToID("_BlurredTex");
|
|
|
|
public const string SKW_DITHER = "DITHER";
|
|
public const string SKW_EDGE_PRESERVE = "EDGE_PRESERVE";
|
|
public const string SKW_EDGE_PRESERVE_UPSCALING = "EDGE_PRESERVE_UPSCALING";
|
|
public const string SKW_SCATTERING_HQ = "SCATTERING_HQ";
|
|
public const string SKW_DEPTH_PEELING = "VF2_DEPTH_PEELING";
|
|
public const string SKW_DEPTH_PREPASS = "VF2_DEPTH_PREPASS";
|
|
}
|
|
|
|
public static int GetScaledSize (int size, float factor) {
|
|
size = (int)(size / factor);
|
|
size /= 2;
|
|
if (size < 1)
|
|
size = 1;
|
|
return size * 2;
|
|
}
|
|
|
|
class VolumetricFogRenderPass : ScriptableRenderPass {
|
|
|
|
const string m_ProfilerTag = "Volumetric Fog Buffer Rendering";
|
|
|
|
static FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.transparent, -1);
|
|
static readonly List<ShaderTagId> shaderTagIdList = new List<ShaderTagId>();
|
|
RTHandle m_LightBuffer;
|
|
VolumetricFogRenderFeature settings;
|
|
|
|
public VolumetricFogRenderPass () {
|
|
shaderTagIdList.Clear();
|
|
shaderTagIdList.Add(new ShaderTagId("UniversalForward"));
|
|
shaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
|
|
RenderTargetIdentifier lightBuffer = new RenderTargetIdentifier(ShaderParams.LightBuffer, 0, CubemapFace.Unknown, -1);
|
|
m_LightBuffer = RTHandles.Alloc(lightBuffer, name: ShaderParams.LightBufferName);
|
|
}
|
|
|
|
public void CleanUp () {
|
|
RTHandles.Release(m_LightBuffer);
|
|
}
|
|
|
|
public void Setup (VolumetricFogRenderFeature settings, RenderPassEvent renderPassEvent) {
|
|
this.settings = settings;
|
|
this.renderPassEvent = renderPassEvent;
|
|
}
|
|
|
|
#if UNITY_2023_3_OR_NEWER
|
|
[Obsolete]
|
|
#endif
|
|
public override void Configure (CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) {
|
|
RenderTextureDescriptor lightBufferDesc = cameraTextureDescriptor;
|
|
VolumetricFogManager manager = VolumetricFogManager.GetManagerIfExists();
|
|
if (manager != null) {
|
|
if (manager.downscaling > 1f) {
|
|
int size = GetScaledSize(cameraTextureDescriptor.width, manager.downscaling);
|
|
lightBufferDesc.width = size;
|
|
lightBufferDesc.height = size;
|
|
}
|
|
lightBufferDesc.colorFormat = manager.blurHDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
|
|
cmd.SetGlobalVector(ShaderParams.LightBufferSize, new Vector4(lightBufferDesc.width, lightBufferDesc.height, manager.downscaling > 1f ? 1f : 0, 0));
|
|
}
|
|
lightBufferDesc.depthBufferBits = 0;
|
|
lightBufferDesc.msaaSamples = 1;
|
|
lightBufferDesc.useMipMap = false;
|
|
|
|
cmd.GetTemporaryRT(ShaderParams.LightBuffer, lightBufferDesc, FilterMode.Bilinear);
|
|
ConfigureTarget(m_LightBuffer);
|
|
ConfigureClear(ClearFlag.Color, new Color(0, 0, 0, 0));
|
|
ConfigureInput(ScriptableRenderPassInput.Depth);
|
|
}
|
|
|
|
#if UNITY_2023_3_OR_NEWER
|
|
[Obsolete]
|
|
#endif
|
|
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
|
|
|
|
VolumetricFogManager manager = VolumetricFogManager.GetManagerIfExists();
|
|
|
|
CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
|
|
cmd.SetGlobalInt(ShaderParams.ForcedInvisible, 0);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
|
|
if (manager == null || (manager.downscaling <= 1f && manager.blurPasses < 1 && manager.scattering <= 0 && !isUsingDepthPeeling)) {
|
|
CommandBufferPool.Release(cmd);
|
|
return;
|
|
}
|
|
|
|
cmd.Clear();
|
|
|
|
foreach (VolumetricFog vg in VolumetricFog.volumetricFogs) {
|
|
if (vg != null) {
|
|
vg.meshRenderer.renderingLayerMask |= VolumetricFogManager.FOG_VOLUMES_RENDERING_LAYER;
|
|
if (isUsingDepthPeeling && renderPassEvent < RenderPassEvent.AfterRenderingTransparents) {
|
|
vg.RenderDistantFog(cmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isUsingDepthPeeling) {
|
|
if (renderPassEvent < RenderPassEvent.AfterRenderingTransparents) {
|
|
cmd.DisableShaderKeyword(ShaderParams.SKW_DEPTH_PREPASS);
|
|
cmd.EnableShaderKeyword(ShaderParams.SKW_DEPTH_PEELING);
|
|
} else {
|
|
cmd.DisableShaderKeyword(ShaderParams.SKW_DEPTH_PEELING);
|
|
cmd.EnableShaderKeyword(ShaderParams.SKW_DEPTH_PREPASS);
|
|
}
|
|
context.ExecuteCommandBuffer(cmd);
|
|
}
|
|
var sortFlags = SortingCriteria.CommonTransparent;
|
|
var drawSettings = CreateDrawingSettings(shaderTagIdList, ref renderingData, sortFlags);
|
|
var filterSettings = filteringSettings;
|
|
filterSettings.layerMask = settings.fogLayerMask;
|
|
filterSettings.renderingLayerMask = VolumetricFogManager.FOG_VOLUMES_RENDERING_LAYER;
|
|
|
|
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSettings);
|
|
|
|
CommandBufferPool.Release(cmd);
|
|
|
|
}
|
|
|
|
#if UNITY_2023_3_OR_NEWER
|
|
|
|
class PassData {
|
|
public RendererListHandle rendererListHandle;
|
|
public UniversalCameraData cameraData;
|
|
public RenderPassEvent renderPassEvent;
|
|
}
|
|
|
|
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) {
|
|
|
|
using (var builder = renderGraph.AddUnsafePass<PassData>(m_ProfilerTag, out var passData)) {
|
|
builder.AllowPassCulling(false);
|
|
|
|
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
|
|
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
|
|
UniversalLightData lightData = frameData.Get<UniversalLightData>();
|
|
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
|
|
passData.cameraData = cameraData;
|
|
passData.renderPassEvent = renderPassEvent;
|
|
|
|
builder.UseTexture(resourceData.activeDepthTexture, AccessFlags.Read);
|
|
ConfigureInput(ScriptableRenderPassInput.Depth);
|
|
|
|
SortingCriteria sortingCriteria = SortingCriteria.CommonTransparent;
|
|
var drawingSettings = CreateDrawingSettings(shaderTagIdList, renderingData, cameraData, lightData, sortingCriteria);
|
|
var filterSettings = filteringSettings;
|
|
filterSettings.layerMask = settings.fogLayerMask;
|
|
filterSettings.renderingLayerMask = VolumetricFogManager.FOG_VOLUMES_RENDERING_LAYER;
|
|
RendererListParams listParams = new RendererListParams(renderingData.cullResults, drawingSettings, filterSettings);
|
|
passData.rendererListHandle = renderGraph.CreateRendererList(listParams);
|
|
builder.UseRendererList(passData.rendererListHandle);
|
|
|
|
builder.SetRenderFunc(static (PassData passData, UnsafeGraphContext context) => {
|
|
|
|
CommandBuffer cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
|
|
|
RenderTextureDescriptor lightBufferDesc = passData.cameraData.cameraTargetDescriptor;
|
|
VolumetricFogManager manager = VolumetricFogManager.GetManagerIfExists();
|
|
if (manager != null) {
|
|
if (manager.downscaling > 1f) {
|
|
int size = GetScaledSize(lightBufferDesc.width, manager.downscaling);
|
|
lightBufferDesc.width = size;
|
|
lightBufferDesc.height = size;
|
|
}
|
|
lightBufferDesc.colorFormat = manager.blurHDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
|
|
cmd.SetGlobalVector(ShaderParams.LightBufferSize, new Vector4(lightBufferDesc.width, lightBufferDesc.height, manager.downscaling > 1f ? 1f : 0, 0));
|
|
}
|
|
lightBufferDesc.depthBufferBits = 0;
|
|
lightBufferDesc.msaaSamples = 1;
|
|
lightBufferDesc.useMipMap = false;
|
|
|
|
cmd.GetTemporaryRT(ShaderParams.LightBuffer, lightBufferDesc, FilterMode.Bilinear);
|
|
RenderTargetIdentifier rti = new RenderTargetIdentifier(ShaderParams.LightBuffer, 0, CubemapFace.Unknown, -1);
|
|
cmd.SetRenderTarget(rti);
|
|
cmd.ClearRenderTarget(false, true, new Color(0, 0, 0, 0));
|
|
|
|
cmd.SetGlobalInt(ShaderParams.ForcedInvisible, 0);
|
|
if (manager == null || (manager.downscaling <= 1f && manager.blurPasses < 1 && manager.scattering <= 0 && !isUsingDepthPeeling)) {
|
|
return;
|
|
}
|
|
|
|
int vgCount = VolumetricFog.volumetricFogs.Count;
|
|
for (int i = 0; i < vgCount; i++) {
|
|
VolumetricFog vg = VolumetricFog.volumetricFogs[i];
|
|
if (vg != null) {
|
|
vg.meshRenderer.renderingLayerMask |= VolumetricFogManager.FOG_VOLUMES_RENDERING_LAYER;
|
|
if (isUsingDepthPeeling && passData.renderPassEvent < RenderPassEvent.AfterRenderingTransparents) {
|
|
vg.RenderDistantFog(cmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isUsingDepthPeeling) {
|
|
if (passData.renderPassEvent < RenderPassEvent.AfterRenderingTransparents) {
|
|
cmd.DisableShaderKeyword(ShaderParams.SKW_DEPTH_PREPASS);
|
|
cmd.EnableShaderKeyword(ShaderParams.SKW_DEPTH_PEELING);
|
|
} else {
|
|
cmd.DisableShaderKeyword(ShaderParams.SKW_DEPTH_PEELING);
|
|
cmd.EnableShaderKeyword(ShaderParams.SKW_DEPTH_PREPASS);
|
|
}
|
|
}
|
|
|
|
context.cmd.DrawRendererList(passData.rendererListHandle);
|
|
});
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
class BlurRenderPass : ScriptableRenderPass {
|
|
|
|
enum Pass {
|
|
BlurHorizontal = 0,
|
|
BlurVertical = 1,
|
|
BlurVerticalAndBlend = 2,
|
|
UpscalingBlend = 3,
|
|
DownscaleDepth = 4,
|
|
BlurVerticalFinal = 5,
|
|
Resample = 6,
|
|
ResampleAndCombine = 7,
|
|
ScatteringPrefilter = 8,
|
|
ScatteringBlend = 9,
|
|
Blend = 10
|
|
}
|
|
|
|
class PassData {
|
|
#if UNITY_2022_3_OR_NEWER
|
|
public RTHandle source;
|
|
#else
|
|
public RenderTargetIdentifier source;
|
|
#endif
|
|
#if UNITY_2023_3_OR_NEWER
|
|
public TextureHandle colorTexture;
|
|
public UniversalCameraData cameraData;
|
|
#endif
|
|
public RenderPassEvent renderPassEvent;
|
|
}
|
|
|
|
|
|
const string m_ProfilerTag = "Volumetric Fog Render Feature";
|
|
ScriptableRenderer renderer;
|
|
static Material mat;
|
|
static RenderTextureDescriptor sourceDesc;
|
|
static VolumetricFogManager manager;
|
|
static readonly PassData passData = new PassData();
|
|
|
|
public void Setup (Shader shader, ScriptableRenderer renderer, RenderPassEvent renderPassEvent) {
|
|
this.renderPassEvent = renderPassEvent;
|
|
this.renderer = renderer;
|
|
manager = VolumetricFogManager.GetManagerIfExists();
|
|
if (mat == null) {
|
|
mat = CoreUtils.CreateEngineMaterial(shader);
|
|
Texture2D noiseTex = Resources.Load<Texture2D>("Textures/blueNoiseVF128");
|
|
mat.SetTexture(ShaderParams.BlueNoiseTexture, noiseTex);
|
|
}
|
|
}
|
|
|
|
#if UNITY_2023_3_OR_NEWER
|
|
[Obsolete]
|
|
#endif
|
|
public override void Configure (CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) {
|
|
sourceDesc = cameraTextureDescriptor;
|
|
ConfigureInput(ScriptableRenderPassInput.Depth);
|
|
}
|
|
|
|
#if UNITY_2023_3_OR_NEWER
|
|
[Obsolete]
|
|
#endif
|
|
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
|
|
|
|
#if UNITY_2022_1_OR_NEWER
|
|
passData.source = renderer.cameraColorTargetHandle;
|
|
#else
|
|
passData.source = renderer.cameraColorTarget;
|
|
#endif
|
|
passData.renderPassEvent = renderPassEvent;
|
|
CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
|
|
ExecutePass(passData, cmd);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
|
|
CommandBufferPool.Release(cmd);
|
|
|
|
}
|
|
|
|
#if UNITY_2023_3_OR_NEWER
|
|
|
|
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) {
|
|
|
|
using (var builder = renderGraph.AddUnsafePass<PassData>(m_ProfilerTag, out var passData)) {
|
|
builder.AllowPassCulling(false);
|
|
|
|
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
|
|
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
|
|
UniversalLightData lightData = frameData.Get<UniversalLightData>();
|
|
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
|
|
passData.cameraData = cameraData;
|
|
passData.colorTexture = resourceData.activeColorTexture;
|
|
builder.UseTexture(resourceData.activeColorTexture, AccessFlags.ReadWrite);
|
|
builder.UseTexture(resourceData.activeDepthTexture, AccessFlags.Read);
|
|
|
|
ConfigureInput(ScriptableRenderPassInput.Depth);
|
|
passData.renderPassEvent = renderPassEvent;
|
|
|
|
sourceDesc = cameraData.cameraTargetDescriptor;
|
|
|
|
builder.SetRenderFunc(static (PassData passData, UnsafeGraphContext context) => {
|
|
CommandBuffer cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
|
passData.source = passData.colorTexture;
|
|
ExecutePass(passData, cmd);
|
|
});
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void ExecutePass (PassData passData, CommandBuffer cmd) {
|
|
|
|
if (manager == null || (manager.downscaling <= 1f && manager.blurPasses < 1 && manager.scattering <= 0 && !isUsingDepthPeeling)) {
|
|
Cleanup();
|
|
return;
|
|
}
|
|
|
|
mat.SetVector(ShaderParams.MiscData, new Vector4(manager.ditherStrength * 0.1f, 0, manager.blurEdgeDepthThreshold, manager.downscalingEdgeDepthThreshold * 0.001f));
|
|
if (manager.ditherStrength > 0) {
|
|
mat.EnableKeyword(ShaderParams.SKW_DITHER);
|
|
} else {
|
|
mat.DisableKeyword(ShaderParams.SKW_DITHER);
|
|
}
|
|
mat.DisableKeyword(ShaderParams.SKW_EDGE_PRESERVE);
|
|
mat.DisableKeyword(ShaderParams.SKW_EDGE_PRESERVE_UPSCALING);
|
|
if (manager.blurPasses > 0 && manager.blurEdgePreserve) {
|
|
mat.EnableKeyword(manager.downscaling > 1f ? ShaderParams.SKW_EDGE_PRESERVE_UPSCALING : ShaderParams.SKW_EDGE_PRESERVE);
|
|
}
|
|
|
|
#if UNITY_2022_3_OR_NEWER
|
|
RTHandle source = passData.source;
|
|
#else
|
|
RenderTargetIdentifier source = passData.source;
|
|
#endif
|
|
|
|
cmd.SetGlobalInt(ShaderParams.ForcedInvisible, 1);
|
|
|
|
RenderTextureDescriptor rtBlurDesc = sourceDesc;
|
|
rtBlurDesc.width = GetScaledSize(sourceDesc.width, manager.downscaling);
|
|
rtBlurDesc.height = GetScaledSize(sourceDesc.height, manager.downscaling);
|
|
rtBlurDesc.useMipMap = false;
|
|
rtBlurDesc.colorFormat = manager.blurHDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
|
|
rtBlurDesc.msaaSamples = 1;
|
|
rtBlurDesc.depthBufferBits = 0;
|
|
|
|
bool usingDownscaling = manager.downscaling > 1f;
|
|
bool isFrontDepthPeeling = isUsingDepthPeeling && passData.renderPassEvent >= RenderPassEvent.AfterRenderingTransparents;
|
|
if (usingDownscaling && !isFrontDepthPeeling) {
|
|
RenderTextureDescriptor rtDownscaledDepth = rtBlurDesc;
|
|
rtDownscaledDepth.colorFormat = RenderTextureFormat.RFloat;
|
|
cmd.GetTemporaryRT(ShaderParams.DownsampledDepth, rtDownscaledDepth, FilterMode.Bilinear);
|
|
FullScreenBlit(cmd, source, ShaderParams.DownsampledDepth, mat, (int)Pass.DownscaleDepth);
|
|
}
|
|
|
|
if (isUsingDepthPeeling) {
|
|
mat.EnableKeyword(ShaderParams.SKW_DEPTH_PEELING);
|
|
} else {
|
|
mat.DisableKeyword(ShaderParams.SKW_DEPTH_PEELING);
|
|
}
|
|
|
|
if (manager.blurPasses < 1) {
|
|
// no blur but downscaling
|
|
FullScreenBlit(cmd, ShaderParams.LightBuffer, source, mat, usingDownscaling ? (int)Pass.UpscalingBlend : (int)Pass.Blend);
|
|
} else {
|
|
// blur (with or without downscaling)
|
|
rtBlurDesc.width = GetScaledSize(sourceDesc.width, manager.blurDownscaling);
|
|
rtBlurDesc.height = GetScaledSize(sourceDesc.height, manager.blurDownscaling);
|
|
cmd.GetTemporaryRT(ShaderParams.BlurRT, rtBlurDesc, FilterMode.Bilinear);
|
|
cmd.GetTemporaryRT(ShaderParams.BlurRT2, rtBlurDesc, FilterMode.Bilinear);
|
|
cmd.SetGlobalFloat(ShaderParams.BlurScale, manager.blurSpread * manager.blurDownscaling);
|
|
FullScreenBlit(cmd, ShaderParams.LightBuffer, ShaderParams.BlurRT, mat, (int)Pass.BlurHorizontal);
|
|
cmd.SetGlobalFloat(ShaderParams.BlurScale, manager.blurSpread);
|
|
for (int k = 0; k < manager.blurPasses - 1; k++) {
|
|
FullScreenBlit(cmd, ShaderParams.BlurRT, ShaderParams.BlurRT2, mat, (int)Pass.BlurVertical);
|
|
FullScreenBlit(cmd, ShaderParams.BlurRT2, ShaderParams.BlurRT, mat, (int)Pass.BlurHorizontal);
|
|
}
|
|
if (usingDownscaling) {
|
|
FullScreenBlit(cmd, ShaderParams.BlurRT, ShaderParams.BlurRT2, mat, (int)Pass.BlurVerticalFinal);
|
|
FullScreenBlit(cmd, ShaderParams.BlurRT2, source, mat, (int)Pass.UpscalingBlend);
|
|
} else {
|
|
FullScreenBlit(cmd, ShaderParams.BlurRT, source, mat, (int)Pass.BlurVerticalAndBlend);
|
|
}
|
|
|
|
cmd.ReleaseTemporaryRT(ShaderParams.BlurRT2);
|
|
cmd.ReleaseTemporaryRT(ShaderParams.BlurRT);
|
|
}
|
|
|
|
if (manager.scattering > 0 && (!isUsingDepthPeeling || isFrontDepthPeeling)) {
|
|
ComputeScattering(cmd, source, mat);
|
|
}
|
|
|
|
cmd.ReleaseTemporaryRT(ShaderParams.LightBuffer);
|
|
if (usingDownscaling) {
|
|
cmd.ReleaseTemporaryRT(ShaderParams.DownsampledDepth);
|
|
}
|
|
}
|
|
|
|
|
|
struct ScatteringMipData {
|
|
public int rtDown, rtUp, width, height;
|
|
}
|
|
static ScatteringMipData[] rt;
|
|
const int PYRAMID_MAX_LEVELS = 5;
|
|
|
|
|
|
#if UNITY_2022_1_OR_NEWER
|
|
static void ComputeScattering(CommandBuffer cmd, RTHandle source, Material mat) {
|
|
#else
|
|
static void ComputeScattering (CommandBuffer cmd, RenderTargetIdentifier source, Material mat) {
|
|
#endif
|
|
|
|
mat.SetVector(ShaderParams.ScatteringData, new Vector4(manager.scatteringThreshold, manager.scatteringIntensity, 1f - manager.scatteringAbsorption, manager.scattering));
|
|
mat.SetColor(ShaderParams.ScatteringTint, manager.scatteringTint);
|
|
float downscaling = manager.downscaling;
|
|
|
|
// Initialize buffers descriptors
|
|
if (rt == null || rt.Length != PYRAMID_MAX_LEVELS + 1) {
|
|
rt = new ScatteringMipData[PYRAMID_MAX_LEVELS + 1];
|
|
for (int k = 0; k < rt.Length; k++) {
|
|
rt[k].rtDown = Shader.PropertyToID("_VFogDownMip" + k);
|
|
rt[k].rtUp = Shader.PropertyToID("_VFogUpMip" + k);
|
|
}
|
|
}
|
|
|
|
int width = GetScaledSize(sourceDesc.width, downscaling);
|
|
int height = GetScaledSize(sourceDesc.height, downscaling);
|
|
if (downscaling > 1 && manager.scatteringHighQuality) {
|
|
mat.EnableKeyword(ShaderParams.SKW_SCATTERING_HQ);
|
|
} else {
|
|
mat.DisableKeyword(ShaderParams.SKW_SCATTERING_HQ);
|
|
}
|
|
if (!manager.scatteringHighQuality) {
|
|
width /= 2;
|
|
height /= 2;
|
|
}
|
|
int mipCount = manager.scatteringHighQuality ? 5 : 4;
|
|
RenderTextureDescriptor scatterDesc = sourceDesc;
|
|
scatterDesc.colorFormat = RenderTextureFormat.ARGBHalf;
|
|
scatterDesc.msaaSamples = 1;
|
|
scatterDesc.depthBufferBits = 0;
|
|
for (int k = 0; k <= mipCount; k++) {
|
|
if (width < 2) width = 2;
|
|
if (height < 2) height = 2;
|
|
scatterDesc.width = rt[k].width = width;
|
|
scatterDesc.height = rt[k].height = height;
|
|
cmd.GetTemporaryRT(rt[k].rtDown, scatterDesc, FilterMode.Bilinear);
|
|
cmd.GetTemporaryRT(rt[k].rtUp, scatterDesc, FilterMode.Bilinear);
|
|
width /= 2;
|
|
height /= 2;
|
|
}
|
|
|
|
RenderTargetIdentifier sourceMip = rt[0].rtDown;
|
|
|
|
FullScreenBlit(cmd, source, sourceMip, mat, (int)Pass.ScatteringPrefilter);
|
|
|
|
// Blitting down...
|
|
cmd.SetGlobalFloat(ShaderParams.BlurScale, 1f);
|
|
for (int k = 1; k <= mipCount; k++) {
|
|
FullScreenBlit(cmd, sourceMip, rt[k].rtDown, mat, (int)Pass.Resample);
|
|
sourceMip = rt[k].rtDown;
|
|
}
|
|
|
|
// Blitting up...
|
|
cmd.SetGlobalFloat(ShaderParams.BlurScale, 1.5f);
|
|
for (int k = mipCount; k > 0; k--) {
|
|
cmd.SetGlobalTexture(ShaderParams.BlurredTex, rt[k - 1].rtDown);
|
|
FullScreenBlit(cmd, sourceMip, rt[k - 1].rtUp, mat, (int)Pass.ResampleAndCombine);
|
|
sourceMip = rt[k - 1].rtUp;
|
|
}
|
|
|
|
FullScreenBlit(cmd, sourceMip, source, mat, (int)Pass.ScatteringBlend);
|
|
}
|
|
|
|
static void FullScreenBlit (CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material, int passIndex) {
|
|
destination = new RenderTargetIdentifier(destination, 0, CubemapFace.Unknown, -1);
|
|
cmd.SetRenderTarget(destination);
|
|
cmd.SetGlobalTexture(ShaderParams.MainTex, source);
|
|
cmd.DrawMesh(Tools.fullscreenMesh, Matrix4x4.identity, material, 0, passIndex);
|
|
}
|
|
|
|
static public void Cleanup () {
|
|
Shader.SetGlobalInt(ShaderParams.ForcedInvisible, 0);
|
|
}
|
|
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
Shader blurShader;
|
|
VolumetricFogRenderPass fogRenderPass, fogRenderBackTranspPass;
|
|
BlurRenderPass blurRenderPass, blurRenderBackTranspPass;
|
|
public static bool installed;
|
|
public static bool isRenderingBeforeTransparents;
|
|
public static bool isUsingDepthPeeling;
|
|
|
|
public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
|
|
|
|
[Tooltip("Specify which fog volumes will be rendered by this feature.")]
|
|
public LayerMask fogLayerMask = -1;
|
|
|
|
[Tooltip("Specify which cameras can execute this render feature. If you have several cameras in your scene, make sure only the correct cameras use this feature in order to optimize performance.")]
|
|
public LayerMask cameraLayerMask = -1;
|
|
|
|
[Tooltip("Ignores reflection probes from executing this render feature")]
|
|
public bool ignoreReflectionProbes = true;
|
|
|
|
void OnDisable () {
|
|
installed = false;
|
|
isRenderingBeforeTransparents = false;
|
|
isUsingDepthPeeling = false;
|
|
BlurRenderPass.Cleanup();
|
|
}
|
|
|
|
private void OnDestroy () {
|
|
if (fogRenderPass != null) {
|
|
fogRenderPass.CleanUp();
|
|
}
|
|
if (fogRenderBackTranspPass != null) {
|
|
fogRenderBackTranspPass.CleanUp();
|
|
}
|
|
}
|
|
|
|
public override void Create () {
|
|
name = "Volumetric Fog 2";
|
|
fogRenderPass = new VolumetricFogRenderPass();
|
|
blurRenderPass = new BlurRenderPass();
|
|
fogRenderBackTranspPass = new VolumetricFogRenderPass();
|
|
blurRenderBackTranspPass = new BlurRenderPass();
|
|
blurShader = Shader.Find("Hidden/VolumetricFog2/Blur");
|
|
if (blurShader == null) {
|
|
Debug.LogWarning("Could not load Volumetric Fog composition shader.");
|
|
}
|
|
}
|
|
|
|
// This method is called when setting up the renderer once per-camera.
|
|
public override void AddRenderPasses (ScriptableRenderer renderer, ref RenderingData renderingData) {
|
|
|
|
installed = true;
|
|
|
|
if (VolumetricFog.volumetricFogs.Count == 0) return;
|
|
|
|
VolumetricFogManager manager = VolumetricFogManager.GetManagerIfExists();
|
|
if (manager == null) {
|
|
Shader.SetGlobalInt(ShaderParams.ForcedInvisible, 0);
|
|
return;
|
|
}
|
|
|
|
isUsingDepthPeeling = manager.includeTransparent != 0 && manager.depthPeeling;
|
|
if (manager.downscaling <= 1f && manager.blurPasses < 1 && manager.scattering <= 0 && !isUsingDepthPeeling) {
|
|
Shader.SetGlobalInt(ShaderParams.ForcedInvisible, 0);
|
|
Shader.DisableKeyword(ShaderParams.SKW_DEPTH_PREPASS);
|
|
Shader.DisableKeyword(ShaderParams.SKW_DEPTH_PEELING);
|
|
return;
|
|
}
|
|
|
|
Camera cam = renderingData.cameraData.camera;
|
|
|
|
CameraType camType = cam.cameraType;
|
|
if (camType == CameraType.Preview) return;
|
|
if (ignoreReflectionProbes && camType == CameraType.Reflection) return;
|
|
|
|
if ((fogLayerMask & cam.cullingMask) == 0) return;
|
|
|
|
if ((cameraLayerMask & (1 << cam.gameObject.layer)) == 0) return;
|
|
|
|
if (cam.targetTexture != null && cam.targetTexture.format == RenderTextureFormat.Depth) return; // ignore occlusion cams!
|
|
|
|
RenderPassEvent injectionPoint = renderPassEvent;
|
|
|
|
if (isUsingDepthPeeling) {
|
|
fogRenderBackTranspPass.Setup(this, RenderPassEvent.AfterRenderingSkybox);
|
|
renderer.EnqueuePass(fogRenderBackTranspPass);
|
|
|
|
blurRenderBackTranspPass.Setup(blurShader, renderer, RenderPassEvent.AfterRenderingSkybox);
|
|
renderer.EnqueuePass(blurRenderBackTranspPass);
|
|
|
|
injectionPoint = (RenderPassEvent)Mathf.Max((int)injectionPoint, (int)RenderPassEvent.AfterRenderingTransparents);
|
|
}
|
|
|
|
isRenderingBeforeTransparents = injectionPoint < RenderPassEvent.AfterRenderingTransparents;
|
|
|
|
fogRenderPass.Setup(this, injectionPoint);
|
|
renderer.EnqueuePass(fogRenderPass);
|
|
|
|
blurRenderPass.Setup(blurShader, renderer, injectionPoint);
|
|
renderer.EnqueuePass(blurRenderPass);
|
|
}
|
|
}
|
|
}
|