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.
 
 
 
 
 

301 lines
15 KiB

using System;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine.Experimental.Rendering;
using static Unity.Mathematics.math;
namespace UnityEngine.Rendering.HighDefinition
{
public partial class HDRenderPipeline
{
// Controls the maximum number of deformers that are supported in one frame
int m_MaxDeformerCount;
// Flag that allows us to track if the system currently supports foam.
bool m_ActiveWaterDeformation = false;
// Buffer used to hold all the water deformers on the CPU
NativeArray<WaterDeformerData> m_WaterDeformersDataCPU;
// The number of deformers for the current frame
int m_ActiveWaterDeformers = 0;
// The maximal deformation that is going to be applied this frame
float m_MaxWaterDeformation = 0.0f;
// Buffer used to hold all the water deformers on the GPU
ComputeBuffer m_WaterDeformersData = null;
// The material used to render the deformers
Material m_DeformerMaterial = null;
// Filtering and normal kernels
ComputeShader m_WaterDeformationCS;
int m_FilterDeformationKernel;
int m_EvaluateDeformationSurfaceGradientKernel;
// Atlas used to hold the custom deformers' textures.
PowerOfTwoTextureAtlas m_DeformerAtlas;
void InitializeWaterDeformers()
{
m_ActiveWaterDeformation = m_Asset.currentPlatformRenderPipelineSettings.supportWaterDeformation;
if (!m_ActiveWaterDeformation)
return;
m_MaxDeformerCount = m_Asset.currentPlatformRenderPipelineSettings.maximumDeformerCount;
m_WaterDeformersData = new ComputeBuffer(m_MaxDeformerCount, System.Runtime.InteropServices.Marshal.SizeOf<WaterDeformerData>());
m_WaterDeformersDataCPU = new NativeArray<WaterDeformerData>(m_MaxDeformerCount, Allocator.Persistent);
m_DeformerMaterial = CoreUtils.CreateEngineMaterial(runtimeShaders.waterDeformationPS);
m_WaterDeformationCS = runtimeShaders.waterDeformationCS;
m_FilterDeformationKernel = m_WaterDeformationCS.FindKernel("FilterDeformation");
m_EvaluateDeformationSurfaceGradientKernel = m_WaterDeformationCS.FindKernel("EvaluateDeformationSurfaceGradient");
m_DeformerAtlas = new PowerOfTwoTextureAtlas((int)m_Asset.currentPlatformRenderPipelineSettings.deformationAtlasSize, 0, GraphicsFormat.R16_UNorm, name: "Water Deformation Atlas", useMipMap: false);
}
void ReleaseWaterDeformers()
{
if (!m_ActiveWaterDeformation)
return;
m_WaterDeformersDataCPU.Dispose();
m_DeformerAtlas.ResetRequestedTexture();
CoreUtils.Destroy(m_DeformerMaterial);
CoreUtils.SafeRelease(m_WaterDeformersData);
}
void ProcessWaterDeformers(CommandBuffer cmd)
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
if (WaterDeformer.instanceCount >= m_MaxDeformerCount)
Debug.LogWarning("Maximum amount of Water Deformer reached. Adjust the maximum amount supported in the HDRP asset.");
#endif
// Reset the requested textures
m_DeformerAtlas.ResetRequestedTexture();
// Grab all the deformers in the scene
var deformerArray = WaterDeformer.instancesAsArray;
int numDeformers = Mathf.Min(WaterDeformer.instanceCount, m_MaxDeformerCount);
// Loop through the deformers and reserve space
bool needRelayout = false;
for (int deformerIdx = 0; deformerIdx < numDeformers; ++deformerIdx)
{
// Grab the current deformer to process
WaterDeformer deformer = deformerArray[deformerIdx];
if (deformer.type == WaterDeformerType.Texture && deformer.texture != null)
{
if (!m_DeformerAtlas.ReserveSpace(deformer.texture))
{
needRelayout = true;
}
}
else if (deformer.type == WaterDeformerType.Material && deformer.IsValidMaterial())
{
if (!m_DeformerAtlas.ReserveSpace(deformer.GetMaterialAtlasingId(), deformer.resolution.x, deformer.resolution.y))
{
needRelayout = true;
}
}
}
// Ask for a relayout
bool outOfSpace = false;
if (needRelayout && !m_DeformerAtlas.RelayoutEntries())
{
outOfSpace = true;
}
// Loop through the deformers
WaterDeformerData data = new WaterDeformerData();
for (int deformerIdx = 0; deformerIdx < numDeformers; ++deformerIdx)
{
// Grab the current deformer to process
WaterDeformer currentDeformer = deformerArray[deformerIdx];
// If this is a texture deformer without a texture skip it
if (currentDeformer.type == WaterDeformerType.Texture && currentDeformer.texture == null)
continue;
if (currentDeformer.type == WaterDeformerType.Material && !currentDeformer.IsValidMaterial())
continue;
Vector3 scale = currentDeformer.scaleMode == DecalScaleMode.InheritFromHierarchy ? currentDeformer.transform.lossyScale : Vector3.one;
// General
data.position = currentDeformer.transform.position;
data.type = (int)currentDeformer.type;
data.amplitude = currentDeformer.amplitude * scale.y;
data.rotation = -currentDeformer.transform.eulerAngles.y * Mathf.Deg2Rad;
data.regionSize = Vector2.Scale(currentDeformer.regionSize, new Vector2(scale.x, scale.z));
data.deepFoamDimmer = currentDeformer.deepFoamDimmer;
data.surfaceFoamDimmer = currentDeformer.surfaceFoamDimmer;
m_MaxWaterDeformation = Mathf.Max(m_MaxWaterDeformation, Mathf.Abs(data.amplitude));
switch (currentDeformer.type)
{
case WaterDeformerType.Sphere:
{
// We do not want any blend for the sphere
data.blendRegion = Vector2.zero;
data.cubicBlend = 0;
}
break;
case WaterDeformerType.Box:
{
data.blendRegion = currentDeformer.boxBlend;
data.cubicBlend = currentDeformer.cubicBlend ? 1 : 0;
}
break;
case WaterDeformerType.BowWave:
{
data.bowWaveElevation = currentDeformer.bowWaveElevation;
data.cubicBlend = 0;
data.blendRegion = Vector2.zero;
}
break;
case WaterDeformerType.ShoreWave:
{
data.amplitude *= 0.5f;
data.waveLength = currentDeformer.waveLength;
data.waveRepetition = currentDeformer.waveRepetition;
data.breakingRange = currentDeformer.breakingRange;
data.waveSpeed = currentDeformer.waveSpeed * WaterConsts.k_KilometerPerHourToMeterPerSecond;
data.waveOffset = currentDeformer.waveOffset;
data.blendRegion = currentDeformer.waveBlend;
data.deepFoamRange = currentDeformer.deepFoamRange;
}
break;
case WaterDeformerType.Texture:
{
Texture tex = currentDeformer.texture;
if (!m_DeformerAtlas.IsCached(out var scaleBias, m_DeformerAtlas.GetTextureID(tex)) && outOfSpace)
Debug.LogError($"No more space in the 2D Water Deformer Altas to store the texture {tex}. To solve this issue, increase the resolution of the Deformation Atlas Size in the current HDRP asset.");
if (m_DeformerAtlas.NeedsUpdate(tex, false))
m_DeformerAtlas.BlitTexture(cmd, scaleBias, tex, new Vector4(1, 1, 0, 0), blitMips: false, overrideInstanceID: m_DeformerAtlas.GetTextureID(tex));
// General
data.scaleOffset = scaleBias;
data.blendRegion = currentDeformer.range;
}
break;
case WaterDeformerType.Material:
{
Material mat = currentDeformer.material;
if (!m_DeformerAtlas.IsCached(out var scaleBias, currentDeformer.GetMaterialAtlasingId()) && outOfSpace)
Debug.LogError($"No more space in the 2D Water Deformer Altas to store the material {mat}. To solve this issue, decrease the deformer resolution or increase the resolution of the Deformation Atlas Size in the current HDRP asset.");
if (currentDeformer.updateMode == CustomRenderTextureUpdateMode.Realtime || currentDeformer.shouldUpdate)
{
var size = (int)m_Asset.currentPlatformRenderPipelineSettings.deformationAtlasSize;
cmd.SetRenderTarget(m_DeformerAtlas.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.Deformer, MeshTopology.Triangles, 3, 1, currentDeformer.mpb);
currentDeformer.shouldUpdate = false;
}
// General
data.scaleOffset = scaleBias;
data.blendRegion = currentDeformer.range;
}
break;
}
// Validate it and push it to the buffer
m_WaterDeformersDataCPU[m_ActiveWaterDeformers] = data;
m_ActiveWaterDeformers++;
}
}
void UpdateWaterDeformersData(CommandBuffer cmd)
{
// Reset the water deformer count
m_ActiveWaterDeformers = 0;
// Reset the max deformation amplitude
m_MaxWaterDeformation = 0.0f;
// If deformation is not supported, nothing to do beyond this point
if (!m_ActiveWaterDeformation)
return;
// Do a pass on the deformers
ProcessWaterDeformers(cmd);
// Push the deformers to the GPU
m_WaterDeformersData.SetData(m_WaterDeformersDataCPU);
// The global deformer buffer data needs to be bound once
cmd.SetGlobalBuffer(HDShaderIDs._WaterDeformerData, m_WaterDeformersData);
// Bind the deformer atlas texture
cmd.SetGlobalTexture(HDShaderIDs._WaterDeformerTextureAtlas, m_DeformerAtlas.AtlasTexture);
}
bool WaterHasDeformation(WaterSurface currentWater)
{
return (m_ActiveWaterDeformers > 0 && currentWater.deformation);
}
void UpdateWaterDeformation(CommandBuffer cmd, WaterSurface currentWater)
{
// First we must ensure, that the texture is there (if it should be) and at the right resolution
currentWater.CheckDeformationResources();
// If deformation will not be read, nothing to do
if (!currentWater.deformation || !m_ActiveWaterDeformation)
return;
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.WaterSurfaceDeformation)))
{
if (m_ActiveWaterDeformers > 0)
{
// Bind the constant buffers
var perSurfaceCB = m_ShaderVariablesWaterPerSurface[currentWater.surfaceIndex];
currentWater.mpb.SetConstantBuffer(HDShaderIDs._ShaderVariablesWaterPerSurface, perSurfaceCB, 0, perSurfaceCB.stride);
BindPerSurfaceConstantBuffer(cmd, m_WaterDeformationCS, perSurfaceCB);
// Disable wireframe for next drawcall
bool wireframe = GL.wireframe;
if (wireframe)
cmd.SetWireframe(false);
// Clear the render target to black and draw all the deformers to the texture
CoreUtils.SetRenderTarget(cmd, currentWater.deformationSGBuffer, clearFlag: ClearFlag.Color, Color.black);
cmd.DrawProcedural(Matrix4x4.identity, m_DeformerMaterial, 0, MeshTopology.Triangles, 6, m_ActiveWaterDeformers, currentWater.mpb);
// Reenable wireframe if needed
if (wireframe)
cmd.SetWireframe(true);
// Evaluate the normals
int numTiles = HDUtils.DivRoundUp((int)currentWater.deformationRes, 8);
// First we need to clear the edge pixel and blur the deformation a bit
cmd.SetComputeTextureParam(m_WaterDeformationCS, m_FilterDeformationKernel, HDShaderIDs._WaterDeformationBuffer, currentWater.deformationSGBuffer);
cmd.SetComputeTextureParam(m_WaterDeformationCS, m_FilterDeformationKernel, HDShaderIDs._WaterDeformationBufferRW, currentWater.deformationBuffer);
cmd.DispatchCompute(m_WaterDeformationCS, m_FilterDeformationKernel, numTiles, numTiles, 1);
// For the CPU Simulation
currentWater.deformationBuffer.rt.IncrementUpdateCount();
cmd.SetComputeTextureParam(m_WaterDeformationCS, m_EvaluateDeformationSurfaceGradientKernel, HDShaderIDs._WaterDeformationBuffer, currentWater.deformationBuffer);
cmd.SetComputeTextureParam(m_WaterDeformationCS, m_EvaluateDeformationSurfaceGradientKernel, HDShaderIDs._WaterDeformationSGBufferRW, currentWater.deformationSGBuffer);
cmd.DispatchCompute(m_WaterDeformationCS, m_EvaluateDeformationSurfaceGradientKernel, numTiles, numTiles, 1);
}
else
{
CoreUtils.SetRenderTarget(cmd, currentWater.deformationBuffer, clearFlag: ClearFlag.Color, Color.black);
CoreUtils.SetRenderTarget(cmd, currentWater.deformationSGBuffer, clearFlag: ClearFlag.Color, Color.black);
}
}
}
// Function that returns the number of active water deformers
internal int NumActiveWaterDeformers()
{
return m_ActiveWaterDeformers;
}
}
}