You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
260 lines
13 KiB
260 lines
13 KiB
using Unity.Collections;
|
|
using Unity.Mathematics;
|
|
using UnityEngine.Experimental.Rendering;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
public partial class HDRenderPipeline
|
|
{
|
|
// Controls the maximum number of foam generators that are supported in one frame
|
|
const int k_MaxNumWaterFoamGenerators = 64;
|
|
const float k_FoamIntensityThreshold = 0.015f;
|
|
|
|
// Flag that allows us to track if the system currently supports foam.
|
|
bool m_ActiveWaterFoam = false;
|
|
|
|
// Buffer used to hold all the water foam generators on the CPU
|
|
NativeArray<WaterGeneratorData> m_WaterFoamGeneratorDataCPU;
|
|
|
|
// The number of foam generators for the current frame
|
|
int m_ActiveWaterFoamGenerators = 0;
|
|
|
|
// Buffer used to hold all the water foam generators on the GPU
|
|
ComputeBuffer m_WaterFoamGeneratorData = null;
|
|
|
|
// Materials and Compute shaders
|
|
Material m_FoamMaterial;
|
|
|
|
// The pass used in the WaterFoam.shader
|
|
int m_ShoreWaveFoamGenerationPass;
|
|
int m_OtherFoamGenerationPass;
|
|
int m_AttenuationPass;
|
|
|
|
ComputeShader m_WaterFoamCS;
|
|
int m_ReprojectFoamKernel;
|
|
int m_AttenuateFoamKernel;
|
|
|
|
// Keeps track of maximum possible foam intensity in to estimate when there is no more foam
|
|
float m_MaxInjectedFoamIntensity = 0.0f;
|
|
|
|
// Atlas used to hold the custom foam generators' textures.
|
|
PowerOfTwoTextureAtlas m_FoamTextureAtlas;
|
|
|
|
void InitializeWaterFoam()
|
|
{
|
|
m_ActiveWaterFoam = m_Asset.currentPlatformRenderPipelineSettings.supportWaterFoam;
|
|
if (!m_ActiveWaterFoam)
|
|
return;
|
|
|
|
m_FoamMaterial = CoreUtils.CreateEngineMaterial(runtimeShaders.waterFoamPS);
|
|
m_WaterFoamGeneratorDataCPU = new NativeArray<WaterGeneratorData>(k_MaxNumWaterFoamGenerators, Allocator.Persistent);
|
|
m_WaterFoamGeneratorData = new ComputeBuffer(k_MaxNumWaterFoamGenerators, System.Runtime.InteropServices.Marshal.SizeOf<WaterGeneratorData>());
|
|
m_FoamTextureAtlas = new PowerOfTwoTextureAtlas((int)m_Asset.currentPlatformRenderPipelineSettings.foamAtlasSize, 0, GraphicsFormat.R16G16_UNorm, name: "Water Foam Atlas", useMipMap: false);
|
|
m_WaterFoamCS = runtimeShaders.waterFoamCS;
|
|
m_ReprojectFoamKernel = m_WaterFoamCS.FindKernel("ReprojectFoam");
|
|
m_AttenuateFoamKernel = m_WaterFoamCS.FindKernel("AttenuateFoam");
|
|
|
|
m_ShoreWaveFoamGenerationPass = m_FoamMaterial.FindPass("ShoreWaveFoamGeneration");
|
|
m_OtherFoamGenerationPass = m_FoamMaterial.FindPass("OtherFoamGeneration");
|
|
m_AttenuationPass = m_FoamMaterial.FindPass("Attenuation");
|
|
}
|
|
|
|
void ReleaseWaterFoam()
|
|
{
|
|
if (!m_ActiveWaterFoam)
|
|
return;
|
|
|
|
CoreUtils.Destroy(m_FoamMaterial);
|
|
m_WaterFoamGeneratorDataCPU.Dispose();
|
|
CoreUtils.SafeRelease(m_WaterFoamGeneratorData);
|
|
m_FoamTextureAtlas.ResetRequestedTexture();
|
|
}
|
|
|
|
void ProcessWaterFoamGenerators(CommandBuffer cmd)
|
|
{
|
|
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
|
if (WaterFoamGenerator.instanceCount >= k_MaxNumWaterFoamGenerators)
|
|
Debug.LogWarning("Maximum amount of Foam Generator reached. Some of them will be ignored.");
|
|
#endif
|
|
|
|
// Grab all the procedural generators in the scene
|
|
var foamGenerators = WaterFoamGenerator.instancesAsArray;
|
|
int numWaterGenerators = Mathf.Min(WaterFoamGenerator.instanceCount, k_MaxNumWaterFoamGenerators);
|
|
|
|
// Reset the atlas
|
|
m_FoamTextureAtlas.ResetRequestedTexture();
|
|
|
|
// Loop through the custom generators and reserve space
|
|
bool needRelayout = false;
|
|
for (int generatorIdx = 0; generatorIdx < numWaterGenerators; ++generatorIdx)
|
|
{
|
|
WaterFoamGenerator foamGenerator = foamGenerators[generatorIdx];
|
|
if (foamGenerator.type == WaterFoamGeneratorType.Texture && foamGenerator.texture != null)
|
|
{
|
|
if (!m_FoamTextureAtlas.ReserveSpace(foamGenerator.texture))
|
|
needRelayout = true;
|
|
}
|
|
else if (foamGenerator.type == WaterFoamGeneratorType.Material && foamGenerator.IsValidMaterial())
|
|
{
|
|
if (!m_FoamTextureAtlas.ReserveSpace(foamGenerator.GetMaterialAtlasingId(), foamGenerator.resolution.x, foamGenerator.resolution.y))
|
|
needRelayout = true;
|
|
}
|
|
}
|
|
|
|
// Ask for a relayout
|
|
bool outOfSpace = false;
|
|
if (needRelayout && !m_FoamTextureAtlas.RelayoutEntries())
|
|
{
|
|
outOfSpace = true;
|
|
}
|
|
|
|
// Loop through the procedural generators first
|
|
WaterGeneratorData data = new WaterGeneratorData();
|
|
for (int generatorIdx = 0; generatorIdx < numWaterGenerators; ++generatorIdx)
|
|
{
|
|
// Grab the current generator to process
|
|
WaterFoamGenerator currentGenerator = foamGenerators[generatorIdx];
|
|
|
|
// If this is a texture deformer without a texture skip it
|
|
if (currentGenerator.type == WaterFoamGeneratorType.Texture && currentGenerator.texture == null)
|
|
continue;
|
|
if (currentGenerator.type == WaterFoamGeneratorType.Material && !currentGenerator.IsValidMaterial())
|
|
continue;
|
|
|
|
// Generator properties
|
|
data.position = currentGenerator.transform.position;
|
|
data.type = (int)currentGenerator.type;
|
|
data.rotation = -currentGenerator.transform.eulerAngles.y * Mathf.Deg2Rad;
|
|
data.regionSize = Vector2.Scale(currentGenerator.regionSize, currentGenerator.scale);
|
|
data.deepFoamDimmer = currentGenerator.deepFoamDimmer;
|
|
data.surfaceFoamDimmer = currentGenerator.surfaceFoamDimmer;
|
|
|
|
if (currentGenerator.type == WaterFoamGeneratorType.Texture)
|
|
{
|
|
Texture tex = currentGenerator.texture;
|
|
if (!m_FoamTextureAtlas.IsCached(out var scaleBias, m_FoamTextureAtlas.GetTextureID(tex)) && outOfSpace)
|
|
Debug.LogError($"No more space in the 2D Water Foam Altas to store the texture {tex}. To solve this issue, increase the resolution of the Foam Atlas Size in the current HDRP asset.");
|
|
|
|
if (m_FoamTextureAtlas.NeedsUpdate(tex, false))
|
|
m_FoamTextureAtlas.BlitTexture(cmd, scaleBias, tex, new Vector4(1, 1, 0, 0), blitMips: false, overrideInstanceID: m_FoamTextureAtlas.GetTextureID(tex));
|
|
data.scaleOffset = scaleBias;
|
|
}
|
|
else if (currentGenerator.type == WaterFoamGeneratorType.Material)
|
|
{
|
|
Material mat = currentGenerator.material;
|
|
if (!m_FoamTextureAtlas.IsCached(out var scaleBias, currentGenerator.GetMaterialAtlasingId()) && outOfSpace)
|
|
Debug.LogError($"No more space in the 2D Water Foam Altas to store the material {mat}. To solve this issue, increase the resolution of the Foam Atlas Size in the current HDRP asset.");
|
|
|
|
if (currentGenerator.updateMode == CustomRenderTextureUpdateMode.Realtime || currentGenerator.shouldUpdate)
|
|
{
|
|
var size = (int)m_Asset.currentPlatformRenderPipelineSettings.foamAtlasSize;
|
|
cmd.SetRenderTarget(m_FoamTextureAtlas.AtlasTexture);
|
|
cmd.SetViewport(new Rect(scaleBias.z * size, scaleBias.w * size, scaleBias.x * size, scaleBias.y * size));
|
|
cmd.DrawProcedural(Matrix4x4.identity, mat, (int)WaterDeformer.PassType.FoamGenerator, MeshTopology.Triangles, 3, 1, currentGenerator.mpb);
|
|
|
|
currentGenerator.shouldUpdate = false;
|
|
}
|
|
|
|
data.scaleOffset = scaleBias;
|
|
}
|
|
|
|
// Enqueue the generator
|
|
m_WaterFoamGeneratorDataCPU[m_ActiveWaterFoamGenerators] = data;
|
|
m_ActiveWaterFoamGenerators++;
|
|
}
|
|
}
|
|
|
|
void UpdateWaterGeneratorsData(CommandBuffer cmd)
|
|
{
|
|
// Reset the water generators count
|
|
m_ActiveWaterFoamGenerators = 0;
|
|
|
|
// If foam is not supported, nothing to do beyond this point
|
|
if (!m_ActiveWaterFoam)
|
|
return;
|
|
|
|
// Do a pass on the foam generators
|
|
ProcessWaterFoamGenerators(cmd);
|
|
|
|
// Push the generators to the GPU
|
|
m_WaterFoamGeneratorData.SetData(m_WaterFoamGeneratorDataCPU);
|
|
|
|
// The global generators buffer data needs to be bound once
|
|
cmd.SetGlobalBuffer(HDShaderIDs._WaterGeneratorData, m_WaterFoamGeneratorData);
|
|
|
|
// Bind the deformer atlas texture
|
|
cmd.SetGlobalTexture(HDShaderIDs._WaterGeneratorTextureAtlas, m_FoamTextureAtlas.AtlasTexture);
|
|
}
|
|
|
|
void UpdateWaterFoamSimulation(CommandBuffer cmd, WaterSurface currentWater)
|
|
{
|
|
// If foam is not supported, nothing to do beyond this point
|
|
if (!m_ActiveWaterFoam)
|
|
return;
|
|
|
|
// First we must ensure, that the texture is there (if it should be) and at the right resolution
|
|
currentWater.CheckFoamResources();
|
|
|
|
// Skip if there are is foam to render
|
|
if (!currentWater.foam)
|
|
return;
|
|
|
|
// What are the type of foam injectors?
|
|
ref var cb = ref m_ShaderVariablesPerSurfaceArray[currentWater.surfaceIndex];
|
|
bool foamGenerators = m_ActiveWaterFoamGenerators > 0;
|
|
bool waterDeformers = WaterHasDeformation(currentWater);
|
|
if (!foamGenerators && !waterDeformers)
|
|
{
|
|
if (m_MaxInjectedFoamIntensity <= k_FoamIntensityThreshold)
|
|
return;
|
|
|
|
// Attenuation formula must be in sync with WaterFoam.shader
|
|
m_MaxInjectedFoamIntensity *= Mathf.Exp(-cb._DeltaTime * cb._FoamPersistenceMultiplier * 0.5f);
|
|
}
|
|
else
|
|
m_MaxInjectedFoamIntensity = 1.0f;
|
|
|
|
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.WaterSurfaceFoam)))
|
|
{
|
|
RTHandle currentFoamBuffer = currentWater.FoamBuffer();
|
|
BindPerSurfaceConstantBuffer(cmd, m_WaterFoamCS, m_ShaderVariablesWaterPerSurface[currentWater.surfaceIndex]);
|
|
|
|
// Check if we need to reproj
|
|
if (currentWater.previousFoamRegionScaleOffset.x != cb._FoamRegionScale.x ||
|
|
currentWater.previousFoamRegionScaleOffset.y != cb._FoamRegionScale.y ||
|
|
currentWater.previousFoamRegionScaleOffset.z != cb._FoamRegionOffset.x ||
|
|
currentWater.previousFoamRegionScaleOffset.w != cb._FoamRegionOffset.y)
|
|
{
|
|
RTHandle tmpFoamBuffer = currentWater.TmpFoamBuffer();
|
|
|
|
// Reproject the previous frame's foam buffer
|
|
int tileC = HDUtils.DivRoundUp((int)currentWater.foamResolution, 8);
|
|
cmd.SetComputeVectorParam(m_WaterFoamCS, HDShaderIDs._PreviousFoamRegionScaleOffset, currentWater.previousFoamRegionScaleOffset);
|
|
cmd.SetComputeTextureParam(m_WaterFoamCS, m_ReprojectFoamKernel, HDShaderIDs._WaterFoamBuffer, currentFoamBuffer);
|
|
cmd.SetComputeTextureParam(m_WaterFoamCS, m_ReprojectFoamKernel, HDShaderIDs._WaterFoamBufferRW, tmpFoamBuffer);
|
|
cmd.DispatchCompute(m_WaterFoamCS, m_ReprojectFoamKernel, tileC, tileC, 1);
|
|
|
|
// Attenuate the foam buffer
|
|
cmd.SetComputeTextureParam(m_WaterFoamCS, m_AttenuateFoamKernel, HDShaderIDs._WaterFoamBuffer, tmpFoamBuffer);
|
|
cmd.SetComputeTextureParam(m_WaterFoamCS, m_AttenuateFoamKernel, HDShaderIDs._WaterFoamBufferRW, currentFoamBuffer);
|
|
cmd.DispatchCompute(m_WaterFoamCS, m_AttenuateFoamKernel, tileC, tileC, 1);
|
|
|
|
// Update the foam data for the next frame
|
|
currentWater.previousFoamRegionScaleOffset = new float4(cb._FoamRegionScale, cb._FoamRegionOffset);
|
|
}
|
|
else
|
|
{
|
|
// Attenuate the foam buffer
|
|
CoreUtils.SetRenderTarget(cmd, currentFoamBuffer);
|
|
cmd.DrawProcedural(Matrix4x4.identity, m_FoamMaterial, m_AttenuationPass, MeshTopology.Triangles, 3, 1, currentWater.mpb);
|
|
}
|
|
|
|
// Then we render the deformers and the generators
|
|
if (waterDeformers)
|
|
cmd.DrawProcedural(Matrix4x4.identity, m_FoamMaterial, m_ShoreWaveFoamGenerationPass, MeshTopology.Triangles, 6, m_ActiveWaterDeformers, currentWater.mpb);
|
|
if (foamGenerators)
|
|
cmd.DrawProcedural(Matrix4x4.identity, m_FoamMaterial, m_OtherFoamGenerationPass, MeshTopology.Triangles, 6, m_ActiveWaterFoamGenerators, currentWater.mpb);
|
|
}
|
|
}
|
|
}
|
|
}
|