Rasagar/Library/PackageCache/com.unity.render-pipelines.high-definition/Runtime/Water/HDRenderPipeline.WaterSystem.cs

1577 lines
80 KiB
C#
Raw Normal View History

2024-08-26 13:07:20 -07:00
using System;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
#if UNITY_EDITOR
using UnityEditor.SceneManagement;
#endif
namespace UnityEngine.Rendering.HighDefinition
{
partial class WaterSystem
{
// Flag that allows us to track if the water system is currently active
bool m_ActiveWaterSystem = false;
internal bool m_EnableDecalWorkflow = false;
HDRenderPipeline m_RenderPipeline;
WaterSystemRuntimeResources m_RuntimeResources;
// Rendering kernels
ComputeShader m_WaterLightingCS;
int m_WaterClassifyTilesKernel;
int m_WaterPrepareSSRIndirectKernel;
int m_WaterClearIndirectKernel;
int[] m_WaterIndirectDeferredKernels = new int[WaterConsts.k_NumWaterVariants];
int m_WaterFogIndirectKernel, m_WaterFogTransmittanceIndirectKernel;
// Water evaluation
ComputeShader m_WaterEvaluationCS;
int m_FindVerticalDisplacementsKernel;
// The shader passes used to render the water
Material m_InternalWaterMaterial;
Mesh m_GridMesh, m_RingMesh, m_RingMeshLow;
GraphicsBuffer m_WaterIndirectDispatchBuffer;
GraphicsBuffer m_WaterPatchDataBuffer;
GraphicsBuffer m_WaterCameraFrustrumBuffer;
FrustumGPU[] m_WaterCameraFrustumCPU = new FrustumGPU[1];
// We can't name it simply GBuffer otherwise it's stripped in forward only
internal const string k_WaterGBufferPass = "WaterGBuffer";
internal const string k_WaterDebugPass = "WaterMask";
internal const string k_LowResGBufferPass = "LowRes";
internal const string k_TessellationPass = "Tessellation";
readonly static string[] k_PassesGBuffer = new string[] { k_WaterGBufferPass, k_LowResGBufferPass };
readonly static string[] k_PassesGBufferTessellation = new string[] { k_WaterGBufferPass + k_TessellationPass, k_LowResGBufferPass };
readonly static string[] k_PassesWaterDebug = new string[] { k_WaterDebugPass + k_TessellationPass, k_WaterDebugPass + k_LowResGBufferPass };
// Other internal rendering data
MaterialPropertyBlock m_WaterMaterialPropertyBlock;
const int k_MaxNumWaterSurfaceProfiles = 16;
// Water surface data CPU side
WaterSurfaceProfile[] m_WaterSurfaceProfileArray = new WaterSurfaceProfile[k_MaxNumWaterSurfaceProfiles];
WaterSurfaceGBufferData[] m_WaterGBufferDataArray = new WaterSurfaceGBufferData[k_MaxNumWaterSurfaceProfiles];
ShaderVariablesWaterPerCamera[] m_ShaderVariablesPerCameraArray = new ShaderVariablesWaterPerCamera[k_MaxNumWaterSurfaceProfiles];
ShaderVariablesWaterPerSurface[] m_ShaderVariablesPerSurfaceArray = new ShaderVariablesWaterPerSurface[k_MaxNumWaterSurfaceProfiles];
// Water surface data GPU side
GraphicsBuffer m_WaterProfileArrayGPU;
GraphicsBuffer m_ShaderVariablesWaterPerCamera;
internal GraphicsBuffer[] m_ShaderVariablesWaterPerSurface = new GraphicsBuffer[k_MaxNumWaterSurfaceProfiles];
// Caustics data
GraphicsBuffer m_CausticsGeometry;
bool m_CausticsBufferGeometryInitialized;
Material m_CausticsMaterial;
// Water line and under water
GraphicsBuffer m_WaterCameraHeightBuffer;
// Local Currents
Texture2D m_WaterSectorData;
#region Initialization
internal void Initialize(HDRenderPipeline hdPipeline)
{
m_RenderPipeline = hdPipeline;
m_ActiveWaterSystem = hdPipeline.asset.currentPlatformRenderPipelineSettings.supportWater;
m_EnableDecalWorkflow = GraphicsSettings.GetRenderPipelineSettings<WaterSystemGlobalSettings>().waterDecalMaskAndCurrent;
m_RuntimeResources = GraphicsSettings.GetRenderPipelineSettings<WaterSystemRuntimeResources>();
// These buffers are needed even when water is disabled
m_DefaultWaterLineBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 3, sizeof(uint));
m_DefaultWaterLineBuffer.SetData(new uint[] { 0xFFFFFFFF, 0, 2 });
m_WaterProfileArrayGPU = new GraphicsBuffer(GraphicsBuffer.Target.Structured, k_MaxNumWaterSurfaceProfiles, UnsafeUtility.SizeOf<WaterSurfaceProfile>());
// If the asset doesn't support water surfaces, nothing to do here
if (!m_ActiveWaterSystem)
return;
m_ShaderVariablesWaterPerCamera = new GraphicsBuffer(GraphicsBuffer.Target.Constant, 1, UnsafeUtility.SizeOf<ShaderVariablesWaterPerCamera>());
// Water simulation
InitializeWaterSimulation();
// Water rendering
m_WaterLightingCS = m_RuntimeResources.waterLightingCS;
m_WaterPrepareSSRIndirectKernel = m_WaterLightingCS.FindKernel("PrepareSSRIndirect");
m_WaterClearIndirectKernel = m_WaterLightingCS.FindKernel("WaterClearIndirect");
m_WaterClassifyTilesKernel = m_WaterLightingCS.FindKernel("WaterClassifyTiles");
m_WaterIndirectDeferredKernels[0] = m_WaterLightingCS.FindKernel("WaterDeferredLighting_Variant0");
m_WaterIndirectDeferredKernels[1] = m_WaterLightingCS.FindKernel("WaterDeferredLighting_Variant1");
m_WaterIndirectDeferredKernels[2] = m_WaterLightingCS.FindKernel("WaterDeferredLighting_Variant2");
m_WaterIndirectDeferredKernels[3] = m_WaterLightingCS.FindKernel("WaterDeferredLighting_Variant3");
m_WaterIndirectDeferredKernels[4] = m_WaterLightingCS.FindKernel("WaterDeferredLighting_Variant4");
m_WaterFogIndirectKernel = m_WaterLightingCS.FindKernel("WaterFogIndirect");
m_WaterFogTransmittanceIndirectKernel = m_WaterLightingCS.FindKernel("WaterFogTransmittanceIndirect");
// Water evaluation
m_WaterEvaluationCS = m_RuntimeResources.waterEvaluationCS;
m_FindVerticalDisplacementsKernel = m_WaterEvaluationCS.FindKernel("FindVerticalDisplacements");
// Allocate the additional rendering data
m_WaterMaterialPropertyBlock = new MaterialPropertyBlock();
m_InternalWaterMaterial = m_RuntimeResources.waterMaterial;
InitializeInstancingData();
// Create the caustics water geometry
m_CausticsGeometry = new GraphicsBuffer(GraphicsBuffer.Target.Raw | GraphicsBuffer.Target.Index, WaterConsts.k_WaterCausticsMeshNumQuads * 6, sizeof(int));
m_CausticsBufferGeometryInitialized = false;
m_CausticsMaterial = CoreUtils.CreateEngineMaterial(m_RuntimeResources.waterCausticsPS);
// Waterline / Underwater
// TODO: This should be entirely dynamic and depend on M_MaxViewCount
m_WaterCameraHeightBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 2 * 4, sizeof(float));
// Make sure the under water surface index is invalidated
m_UnderWaterSurfaceIndex = -1;
// Make sure the base mesh is built
BuildGridMeshes(ref m_GridMesh, ref m_RingMesh, ref m_RingMeshLow);
// Under water resources
InitializeUnderWaterResources();
// Faom resources
InitializeWaterDecals();
}
void InitializeInstancingData()
{
// Allocate the indirect instancing buffer
m_WaterIndirectDispatchBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 5, sizeof(int));
// Initialize the parts of the buffer with valid values
uint meshResolution = WaterConsts.k_WaterTessellatedMeshResolution;
uint quadCount = 2 * ((meshResolution - meshResolution / 4) * (meshResolution / 4 - 1) + meshResolution / 4);
uint triCount = quadCount + 3 * meshResolution / 2;
uint[] indirectBufferCPU = new uint[5];
indirectBufferCPU[0] = triCount * 3;
// Push the values to the GPU
m_WaterIndirectDispatchBuffer.SetData(indirectBufferCPU);
// Allocate the per instance data
m_WaterPatchDataBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 7 * 7, sizeof(float) * 2);
// Allocate the frustum buffer
m_WaterCameraFrustrumBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, System.Runtime.InteropServices.Marshal.SizeOf(typeof(FrustumGPU)));
}
void CheckWaterCurrentData()
{
if (m_WaterSectorData == null)
{
m_WaterSectorData = new Texture2D(16, 1, TextureFormat.RGBAFloat, -1, true);
m_WaterSectorData.SetPixelData(WaterConsts.k_SectorSwizzlePacked, 0, 0);
m_WaterSectorData.Apply();
}
}
internal void Cleanup()
{
// Grab all the water surfaces in the scene
var waterSurfaces = WaterSurface.instancesAsArray;
int numWaterSurfaces = WaterSurface.instanceCount;
// Loop through them and display them
for (int surfaceIdx = 0; surfaceIdx < numWaterSurfaces; ++surfaceIdx)
{
WaterSurface waterSurface = waterSurfaces[surfaceIdx];
waterSurface.ReleaseResources();
}
// Release the default water line array
CoreUtils.SafeRelease(m_DefaultWaterLineBuffer);
// Release the water profile array
CoreUtils.SafeRelease(m_WaterProfileArrayGPU);
// If the asset doesn't support water surfaces, nothing to do here
if (!m_ActiveWaterSystem)
return;
CoreUtils.SafeRelease(m_ShaderVariablesWaterPerCamera);
foreach (var cb in m_ShaderVariablesWaterPerSurface)
CoreUtils.SafeRelease(cb);
ReleaseWaterDecals();
ReleaseCPUWaterSimulation();
// Release the waterline underwater data
CoreUtils.SafeRelease(m_WaterCameraHeightBuffer);
// Release the caustics geometry
CoreUtils.Destroy(m_CausticsMaterial);
CoreUtils.SafeRelease(m_CausticsGeometry);
// Water rendering resources
CoreUtils.SafeRelease(m_WaterCameraFrustrumBuffer);
CoreUtils.SafeRelease(m_WaterPatchDataBuffer);
CoreUtils.SafeRelease(m_WaterIndirectDispatchBuffer);
// Simulation resources
ReleaseWaterSimulation();
// Free the meshes
m_GridMesh = m_RingMesh = m_RingMeshLow = null;
}
#endregion
#region Surface Update
/// <summary>
/// Computes the screen space size of an edge after camera projection
/// </summary>
/// <param name="hdCamera">camera</param>
/// <param name="cameraDistance">the distance between the edge and the camera</param>
/// <param name="sizeWS">the size of the edge in world space</param>
/// <returns>The screen space size</returns>
float ComputeScreenSpaceSize(HDCamera hdCamera, float cameraDistance, float sizeWS)
{
float4 positionWS = new float4(sizeWS, 0.0f, cameraDistance, 1.0f);
float4 positionCS = math.mul(hdCamera.mainViewConstants.nonJitteredProjMatrix, positionWS);
return math.abs(positionCS.x / positionCS.w) * 0.5f * hdCamera.actualWidth;
}
static void BindPerSurfaceConstantBuffer(CommandBuffer cmd, ComputeShader cs, GraphicsBuffer buffer)
{
cmd.SetComputeConstantBufferParam(cs, HDShaderIDs._ShaderVariablesWaterPerSurface, buffer, 0, buffer.stride);
}
void UpdatePerSurfaceConstantBuffer(WaterSurface currentWater)
{
ref var cb = ref m_ShaderVariablesPerSurfaceArray[currentWater.surfaceIndex];
cb._MaxWaterDeformation = m_MaxWaterDeformation;
cb._SimulationTime = currentWater.simulation.simulationTime;
cb._DeltaTime = currentWater.simulation.deltaTime;
if (currentWater.timeMultiplier == 0.0f)
cb._DeltaTime = 1.0f; // This is to be able to see the foam generators even when time is paused
// all the data below mostly don't change from frame to frame
// Resolution at which the simulation is evaluated
cb._BandResolution = (uint)m_WaterBandResolution;
// Per patch data
cb._PatchGroup = currentWater.simulation.spectrum.patchGroup;
cb._PatchOrientation = currentWater.simulation.spectrum.patchOrientation * Mathf.Deg2Rad;
cb._PatchWindSpeed = currentWater.simulation.spectrum.patchWindSpeed;
cb._PatchDirectionDampener = currentWater.simulation.spectrum.patchWindDirDampener;
void PackBandData(WaterSimulationResources simulation, int bandIdx, out Vector4 scaleOffsetAmplitude, out float2 fade)
{
float invPatchSize = 1.0f / simulation.spectrum.patchSizes[bandIdx];
float orientation = simulation.spectrum.patchOrientation[bandIdx] * Mathf.Deg2Rad;
float bandScale = simulation.rendering.patchCurrentSpeed[bandIdx] * simulation.simulationTime * invPatchSize;
scaleOffsetAmplitude = new Vector4(invPatchSize, Mathf.Cos(orientation) * bandScale, Mathf.Sin(orientation) * bandScale, simulation.rendering.patchAmplitudeMultiplier[bandIdx]);
fade = new float2(simulation.rendering.patchFadeA[bandIdx], simulation.rendering.patchFadeB[bandIdx]);
}
PackBandData(currentWater.simulation, 0, out cb._Band0_ScaleOffset_AmplitudeMultiplier, out cb._Band0_Fade);
PackBandData(currentWater.simulation, 1, out cb._Band1_ScaleOffset_AmplitudeMultiplier, out cb._Band1_Fade);
PackBandData(currentWater.simulation, 2, out cb._Band2_ScaleOffset_AmplitudeMultiplier, out cb._Band2_Fade);
cb._GroupOrientation = currentWater.simulation.spectrum.groupOrientation * Mathf.Deg2Rad;
// Max wave height for the system
float patchAmplitude = EvaluateMaxAmplitude(currentWater.simulation.spectrum.patchSizes.x, cb._PatchWindSpeed.x * WaterConsts.k_MeterPerSecondToKilometerPerHour);
cb._MaxWaveHeight = patchAmplitude;
cb._ScatteringWaveHeight = Mathf.Max(cb._MaxWaveHeight * WaterConsts.k_ScatteringRange, WaterConsts.k_MinScatteringAmplitude) + currentWater.maximumHeightOverride;
// Horizontal displacement due to each band
cb._MaxWaveDisplacement = cb._MaxWaveHeight * WaterConsts.k_WaterMaxChoppinessValue;
// Water smoothness
cb._WaterSmoothness = currentWater.startSmoothness;
// Foam Jacobian offset depends on the number of bands
if (currentWater.simulation.numActiveBands == 3)
cb._SimulationFoamAmount = 12.0f * Mathf.Pow(0.8f + currentWater.simulationFoamAmount * 0.28f, 0.25f);
else if (currentWater.simulation.numActiveBands == 2)
cb._SimulationFoamAmount = 8.0f * Mathf.Pow(0.72f + currentWater.simulationFoamAmount * 0.28f, 0.25f);
else
cb._SimulationFoamAmount = 4.0f * Mathf.Pow(0.72f + currentWater.simulationFoamAmount * 0.28f, 0.25f);
cb._FoamCurrentInfluence = currentWater.foamCurrentInfluence;
// Smoothness of the foam
cb._FoamPersistenceMultiplier = 1.0f / Mathf.Lerp(0.05f, 1f, currentWater.foamPersistenceMultiplier);
cb._WaterFoamSmoothness = currentWater.foamSmoothness;
cb._WaterFoamTiling = currentWater.foamTextureTiling;
// We currently only support properly up to 16 unique water surfaces
cb._SurfaceIndex = currentWater.surfaceIndex & 0xF;
cb._MaxRefractionDistance = Mathf.Min(currentWater.absorptionDistance, currentWater.maxRefractionDistance);
cb._WaterExtinction.xyz = currentWater.extinction;
cb._WaterAlbedo.Set(currentWater.scatteringColor.r, currentWater.scatteringColor.g, currentWater.scatteringColor.b, 0.0f);
cb._WaterUpDirection.xyz = currentWater.UpVector();
cb._AmbientScattering = currentWater.ambientScattering;
cb._HeightBasedScattering = currentWater.heightScattering;
cb._DisplacementScattering = currentWater.displacementScattering;
// Decal region
currentWater.GetDecalRegion(out var decalRegionCenter, out var decalRegionSize);
cb._DecalRegionOffset.Set(decalRegionCenter.x, decalRegionCenter.y);
cb._DecalRegionScale.Set(1.0f / decalRegionSize.x, 1.0f / decalRegionSize.y);
cb._DecalAtlasScale = 1.0f / m_DecalAtlasSize;
// Deformation
cb._DeformationRegionResolution = (int)currentWater.deformationRes;
// Foam
var simulationFoamWindAttenuation = Mathf.Clamp(currentWater.simulationFoamWindCurve.Evaluate(currentWater.simulation.spectrum.patchWindSpeed.x / WaterConsts.k_SwellMaximumWindSpeedMpS), 0.0f, 1.0f);
cb._SimulationFoamIntensity = currentWater.HasSimulationFoam() ? simulationFoamWindAttenuation : 0.0f;
cb._WaterFoamRegionResolution = (int)currentWater.foamResolution;
cb._SimulationFoamMaskScale.x = currentWater.supportSimulationFoamMask ? 1.0f : 0.0f;
if (!m_EnableDecalWorkflow)
{
// Foam Mask
cb._SimulationFoamMaskOffset = currentWater.simulationFoamMaskOffset;
cb._SimulationFoamMaskScale.Set(1.0f / currentWater.simulationFoamMaskExtent.x, 1.0f / currentWater.simulationFoamMaskExtent.y);
var localScale = currentWater.transform.localScale;
Vector2 invertScale = new Vector2(localScale.x < 0.0f ? -1.0f : 1.0f, localScale.z < 0.0f ? -1.0f : 1.0f);
// Water Mask
cb._WaterMaskOffset = Vector2.Scale(currentWater.waterMaskOffset, invertScale);
cb._WaterMaskScale.Set(1.0f / currentWater.waterMaskExtent.x, 1.0f / currentWater.waterMaskExtent.y);
cb._WaterMaskRemap.Set(currentWater.waterMaskRemap.x, currentWater.waterMaskRemap.y - currentWater.waterMaskRemap.x);
// Current maps
cb._Group0CurrentRegionScaleOffset.Set(invertScale.x / currentWater.largeCurrentRegionExtent.x, invertScale.y / currentWater.largeCurrentRegionExtent.y, currentWater.largeCurrentRegionOffset.x, -currentWater.largeCurrentRegionOffset.y);
if (currentWater.ripplesMotionMode == WaterPropertyOverrideMode.Inherit && currentWater.surfaceType != WaterSurfaceType.Pool)
{
cb._Group1CurrentRegionScaleOffset = cb._Group0CurrentRegionScaleOffset;
cb._CurrentMapInfluence.Set(currentWater.largeCurrentMapInfluence, currentWater.largeCurrentMapInfluence);
}
else
{
cb._Group1CurrentRegionScaleOffset.Set(invertScale.x / currentWater.ripplesCurrentRegionExtent.x, invertScale.y / currentWater.ripplesCurrentRegionExtent.y, currentWater.ripplesCurrentRegionOffset.x, -currentWater.ripplesCurrentRegionOffset.y);
cb._CurrentMapInfluence.Set(currentWater.largeCurrentMapInfluence, currentWater.ripplesCurrentMapInfluence);
}
}
// Caustics
cb._CausticsBandIndex = SanitizeCausticsBand(currentWater.causticsBand, currentWater.simulation.numActiveBands);
cb._CausticsRegionSize = currentWater.simulation.spectrum.patchSizes[cb._CausticsBandIndex];
// Cautics
cb._CausticsIntensity = currentWater.caustics ? currentWater.causticsIntensity : 0.0f;
cb._CausticsShadowIntensity = currentWater.causticsDirectionalShadow ? currentWater.causticsDirectionalShadowDimmer : 1.0f;
cb._CausticsPlaneBlendDistance = currentWater.causticsPlaneBlendDistance;
cb._CausticsMaxLOD = EvaluateCausticsMaxLOD(currentWater.causticsResolution);
cb._CausticsTilingFactor = 1.0f / currentWater.causticsTilingFactor;
// Tessellation
cb._WaterMaxTessellationFactor = currentWater.tessellation ? currentWater.maxTessellationFactor : 0.0f;
cb._WaterTessellationFadeStart = currentWater.tessellationFactorFadeStart;
cb._WaterTessellationFadeRange = currentWater.tessellationFactorFadeRange;
// Bind the rendering layer data for decal layers
cb._WaterRenderingLayer = (uint)currentWater.renderingLayerMask;
// Evaluate the matrices
cb._WaterSurfaceTransform = currentWater.simulation.rendering.waterToWorldMatrix;
cb._WaterSurfaceTransform_Inverse = currentWater.simulation.rendering.worldToWaterMatrix;
cb._WaterCustomTransform_Inverse = currentWater.simulation.rendering.worldToWaterMatrixCustom;
}
void UpdataPerCameraConstantBuffer(WaterSurface currentWater, HDCamera hdCamera, WaterRendering settings,
bool instancedQuads, bool infinite, bool customMesh, int surfaceIndex)
{
float3 waterPosition = currentWater.transform.position;
float2 extent = currentWater.IsProceduralGeometry() ? new Vector2(Mathf.Abs(currentWater.transform.lossyScale.x), Mathf.Abs(currentWater.transform.lossyScale.z)) : Vector2.one;
ref var cb = ref m_ShaderVariablesPerCameraArray[surfaceIndex];
ref var perSurfaceCB = ref m_ShaderVariablesPerSurfaceArray[surfaceIndex];
float maxWaveDisplacement = perSurfaceCB._MaxWaveDisplacement;
float maxWaveHeight = perSurfaceCB._MaxWaveHeight;
// Rotation, size and offsets (patch, water mask and foam mask)
if (instancedQuads)
{
Matrix4x4 worldToWater = currentWater.simulation.rendering.worldToWaterMatrix;
// Compute the grid size to maintain a constant triangle size in screen space
float distanceToCamera = maxWaveDisplacement + worldToWater.MultiplyPoint3x4(hdCamera.camera.transform.position).y;
float vertexSpace = Mathf.Min(ComputeScreenSpaceSize(hdCamera, distanceToCamera, 1.0f / WaterConsts.k_WaterTessellatedMeshResolution), 1.0f);
float gridSize = (1 << (int)(Mathf.Log(1.0f / vertexSpace, 2))) * settings.triangleSize.value;
cb._GridSize = gridSize;
// Move the patches with the camera in locksteps to reduce vertex wobbling
int stableLODCount = 5;
float cameraStep = (1 << stableLODCount) * gridSize / WaterConsts.k_WaterTessellatedMeshResolution;
float cameraDirOffset = maxWaveDisplacement * (1.0f - settings.triangleSize.value / 200.0f);
float3 patchOffset = worldToWater.MultiplyPoint3x4(hdCamera.camera.transform.position + hdCamera.camera.transform.forward * cameraDirOffset);
cb._PatchOffset = math.round((waterPosition.xz + patchOffset.xz) / cameraStep) * cameraStep - waterPosition.xz;
}
else
{
cb._GridSize = extent;
cb._PatchOffset = 0.0f;
}
// Used for non infinite surfaces
if (instancedQuads && !infinite)
{
cb._RegionExtent = extent * 0.5f;
// compute biggest lod that touches region
float distance = math.max(
math.abs(waterPosition.x - cb._PatchOffset.x) + cb._RegionExtent.x,
math.abs(waterPosition.z - cb._PatchOffset.y) + cb._RegionExtent.y);
int lod = (int)math.ceil(math.log2(math.max(distance * 2.0f / cb._GridSize.x, 1.0f)));
// determine vertex step for this lod level
float triangleSize = (1 << lod) * cb._GridSize.x / WaterConsts.k_WaterTessellatedMeshResolution;
// align grid size on region extent
float2 optimalTriangleSize = new float2(
extent.x / Mathf.Max(Mathf.Floor(extent.x / triangleSize), 1),
extent.y / Mathf.Max(Mathf.Floor(extent.y / triangleSize), 1));
cb._GridSize = optimalTriangleSize * cb._GridSize.x / triangleSize;
// align grid pos on one region corner
float2 corner = -(cb._PatchOffset + 0.5f * cb._GridSize) - cb._RegionExtent;
cb._PatchOffset = cb._PatchOffset + (corner - math.round(corner / optimalTriangleSize) * optimalTriangleSize);
}
else
cb._RegionExtent = new float2(float.MaxValue, float.MaxValue);
if (instancedQuads)
{
// Max offset for patch culling
float3 cameraPosition = hdCamera.camera.transform.position;
float maxOffsetRelative = math.max(math.abs(cb._PatchOffset.x - cameraPosition.x), math.abs(cb._PatchOffset.y - cameraPosition.z));
float maxPatchOffset = -maxWaveDisplacement - maxOffsetRelative;
// Compute last LOD with displacement
float maxFadeDistance = currentWater.simulation.rendering.maxFadeDistance;
if (maxFadeDistance != float.MaxValue)
{
float cameraHeight = cameraPosition.y - (waterPosition.y + maxWaveHeight);
maxFadeDistance = Mathf.Sqrt(maxFadeDistance * maxFadeDistance - cameraHeight * cameraHeight) - maxPatchOffset;
maxFadeDistance = Mathf.Max(maxFadeDistance * 2.0f / Mathf.Max(cb._GridSize.x, cb._GridSize.y), 1);
cb._MaxLOD = (uint)Mathf.Ceil(Mathf.Log(maxFadeDistance, 2)); // only keep highest bit
}
else
cb._MaxLOD = 8; // we could support a maximum of 12 LODs (max 49 patches, 12 * 4 = 48), but this is enough
cb._GridSizeMultiplier = (1 << (int)cb._MaxLOD) * 0.5f;
}
else
cb._GridSizeMultiplier = 1.0f;
}
void FillWaterSurfaceProfile(HDCamera hdCamera, WaterSurface waterSurface, int waterSurfaceIndex)
{
ref var cb = ref m_ShaderVariablesPerSurfaceArray[waterSurfaceIndex];
WaterSurfaceProfile profile = new WaterSurfaceProfile();
profile.maxRefractionDistance = cb._MaxRefractionDistance;
profile.renderingLayers = cb._WaterRenderingLayer;
profile.extinction = cb._WaterExtinction.xyz;
profile.extinctionMultiplier = 1.0f / waterSurface.absorptionDistanceMultiplier;
profile.albedo = cb._WaterAlbedo;
profile.upDirection = cb._WaterUpDirection.xyz;
// Precompute underwater lighting that includes ambient and directional lights
var lightList = m_RenderPipeline.gpuLightList;
float isotropicPhase = 1.0f / (4.0f * Mathf.PI);
profile.underwaterColor = m_RenderPipeline.GetShaderVariablesGlobalCB()._WaterAmbientProbe;
for (int i = 0; i < lightList.directionalLightCount; i++)
profile.underwaterColor += lightList.directionalLights[i].color * isotropicPhase;
profile.underwaterColor = Vector3.Scale(profile.underwaterColor, profile.albedo);
// Scattering parameters
profile.bodyScatteringHeight = waterSurface.directLightBodyScattering;
profile.tipScatteringHeight = waterSurface.directLightTipScattering;
profile.cameraUnderWater = waterSurfaceIndex == m_UnderWaterSurfaceIndex ? 1 : 0;
profile.envPerceptualRoughness = waterSurface.surfaceType == WaterSurfaceType.Pool ? 0.0f : Mathf.Lerp(0.0f, 0.15f, Mathf.Clamp(waterSurface.largeWindSpeed / WaterConsts.k_EnvRoughnessWindSpeed, 0.0f, 1.0f));
profile.disableIOR = waterSurface.underWaterRefraction ? 0 : 1;
// Smoothness fade
profile.smoothnessFadeStart = waterSurface.smoothnessFadeStart;
profile.smoothnessFadeDistance = waterSurface.smoothnessFadeDistance;
profile.roughnessEndValue = 1.0f - waterSurface.endSmoothness;
profile.foamColor.Set(waterSurface.foamColor.r, waterSurface.foamColor.g, waterSurface.foamColor.b);
// Profile has been filled, we're done
m_WaterSurfaceProfileArray[waterSurfaceIndex] = profile;
}
void UpdateWaterSurface(CommandBuffer cmd, WaterSurface currentWater, int surfaceIndex)
{
currentWater.surfaceIndex = surfaceIndex;
if (!m_ActiveWaterSimulationCPU && currentWater.scriptInteractions)
InitializeCPUWaterSimulation();
// Allocate necessary resources if they are not yet created
currentWater.CheckResources((int)m_WaterBandResolution, m_GPUReadbackMode);
// Update the simulation time (include timescale)
currentWater.simulation.Update(currentWater.timeMultiplier);
// Update the constant buffer
UpdatePerSurfaceConstantBuffer(currentWater);
// Upload to GPU
if (m_ShaderVariablesWaterPerSurface[surfaceIndex] == null)
m_ShaderVariablesWaterPerSurface[surfaceIndex] = new GraphicsBuffer(GraphicsBuffer.Target.Constant, 1, UnsafeUtility.SizeOf<ShaderVariablesWaterPerSurface>());
cmd.SetBufferData(m_ShaderVariablesWaterPerSurface[surfaceIndex], m_ShaderVariablesPerSurfaceArray, surfaceIndex, 0, 1);
// Update the GPU simulation for the water
UpdateGPUWaterSimulation(cmd, currentWater);
// Here we replicate the ocean simulation on the CPU (if requested)
UpdateCPUWaterSimulation(currentWater);
// Update the foam texture
UpdateWaterDecals(cmd, currentWater);
// Here we need to replicate the water CPU Buffers
UpdateCPUBuffers(cmd, currentWater);
// Render the caustics from the current simulation state if required
if (currentWater.caustics)
EvaluateWaterCaustics(cmd, currentWater);
else
currentWater.simulation.CheckCausticsResources(false, 0);
}
internal void UpdateWaterSurfaces(CommandBuffer cmd)
{
// Grab all the water surfaces in the scene
var waterSurfaces = WaterSurface.instancesAsArray;
int numWaterSurfaces = Mathf.Min(WaterSurface.instanceCount, k_MaxNumWaterSurfaceProfiles);
// If water surface simulation is disabled, skip.
if (!m_ActiveWaterSystem || numWaterSurfaces == 0)
return;
// We have to update that every frame cause changing global settings don't cause a pipeline reinit reload
m_EnableDecalWorkflow = GraphicsSettings.GetRenderPipelineSettings<WaterSystemGlobalSettings>().waterDecalMaskAndCurrent;
float ct = waterSurfaces[0].simulation != null ? waterSurfaces[0].simulation.simulationTime : 0.0f;
Vector4 _WaterDecalTimeParameters = new Vector4(ct, Mathf.Sin(ct), Mathf.Cos(ct), 0.0f);
Shader.SetGlobalVector(HDShaderIDs._WaterDecalTimeParameters, _WaterDecalTimeParameters);
// Cull decals and render them to the atlas
UpdateWaterDecalData(cmd);
// In case we had a scene switch, it is possible the resource became null
if (m_GridMesh == null)
BuildGridMeshes(ref m_GridMesh, ref m_RingMesh, ref m_RingMeshLow);
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.WaterSurfaceUpdate)))
{
// Update this frame data
for (int surfaceIdx = 0; surfaceIdx < numWaterSurfaces; ++surfaceIdx)
UpdateWaterSurface(cmd, waterSurfaces[surfaceIdx], surfaceIdx);
// Mark as not processed
for (int surfaceIdx = k_MaxNumWaterSurfaceProfiles; surfaceIdx < WaterSurface.instanceCount; ++surfaceIdx)
waterSurfaces[surfaceIdx].surfaceIndex = -1;
}
}
#endregion
#region GBuffer
internal struct WaterGBuffer
{
// Flag that defines if at least one water surface was rendered into the gbuffer
public bool valid;
// Flag that defines if at least one water surface will need to be rendered as debug
public bool debugRequired;
// GBuffer targets
public TextureHandle waterGBuffer0;
public TextureHandle waterGBuffer1;
public TextureHandle waterGBuffer2;
public TextureHandle waterGBuffer3;
// Indirect dispatch and tile data
public BufferHandle indirectBuffer;
public BufferHandle tileBuffer;
public BufferHandle cameraHeight;
}
struct WaterSurfaceGBufferData
{
public bool render;
public bool renderDebug;
public int surfaceIndex;
// Simulation & Deformation buffers
public RenderTargetIdentifier displacementTexture;
public RenderTargetIdentifier deformationBuffer;
// Geometry parameters
public bool drawInfiniteMesh;
public bool tessellation;
public int numActiveBands;
// Geometry properties
public bool instancedQuads;
public bool infinite;
public bool customMesh;
public List<MeshRenderer> meshRenderers;
public bool evaluateCameraPosition;
// Water Mask
public Texture waterMask;
// Current
public bool activeCurrent;
public Texture largeCurrentMap;
public Texture ripplesCurrentMap;
// Material data
public Material waterMaterial;
public MaterialPropertyBlock mpb;
// Matrices
public Matrix4x4 worldToWaterMatrixCustom;
}
internal void InitializeWaterPrepassOutput(RenderGraph renderGraph, ref HDRenderPipeline.TransparentPrepassOutput output)
{
var defaultBuffer = renderGraph.ImportBuffer(m_DefaultWaterLineBuffer);
var waterSurfaceProfiles = renderGraph.ImportBuffer(m_WaterProfileArrayGPU);
output.waterGBuffer = new WaterSystem.WaterGBuffer()
{
waterGBuffer0 = renderGraph.defaultResources.blackTextureXR,
waterGBuffer1 = renderGraph.defaultResources.blackTextureXR,
waterGBuffer2 = renderGraph.defaultResources.blackTextureXR,
waterGBuffer3 = renderGraph.defaultResources.blackTextureXR,
cameraHeight = defaultBuffer,
};
output.waterLine = defaultBuffer;
output.waterSurfaceProfiles = waterSurfaceProfiles;
}
void EvaluateWaterRenderingData(WaterSurface currentWater, out bool instancedQuads, out bool infinite, out bool customMesh, out List<MeshRenderer> meshRenderers)
{
instancedQuads = currentWater.IsInstancedQuads();
if (instancedQuads)
{
// See if the surface is infinite or clamped
infinite = currentWater.IsInfinite();
// We're not using custom meshes in this case
customMesh = false;
meshRenderers = null;
}
else
{
infinite = false;
if (currentWater.geometryType == WaterGeometryType.Quad || currentWater.meshRenderers.Count == 0)
{
// We're not using custom meshes in this case
customMesh = false;
meshRenderers = null;
}
else
{
customMesh = true;
meshRenderers = currentWater.meshRenderers;
}
}
}
void PrepareSurfaceGBufferData(HDCamera hdCamera, WaterRendering settings, WaterSurface currentWater, int surfaceIndex, ref WaterSurfaceGBufferData parameters)
{
parameters.surfaceIndex = surfaceIndex;
parameters.evaluateCameraPosition = surfaceIndex == m_UnderWaterSurfaceIndex;
bool supportDecals = hdCamera.frameSettings.IsEnabled(FrameSettingsField.WaterDecals);
// Geometry parameters
parameters.drawInfiniteMesh = currentWater.simulation.rendering.maxFadeDistance != float.MaxValue;
parameters.tessellation = currentWater.tessellation;
parameters.numActiveBands = currentWater.simulation.numActiveBands;
// Evaluate which mesh shall be used, etc
EvaluateWaterRenderingData(currentWater, out parameters.instancedQuads, out parameters.infinite, out parameters.customMesh, out parameters.meshRenderers);
// At the moment indirect buffer for instanced mesh draw with tessellation does not work on metal
if (parameters.instancedQuads && SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal)
parameters.tessellation = false;
// Water material
parameters.waterMaterial = currentWater.customMaterial != null ? currentWater.customMaterial : m_InternalWaterMaterial;
parameters.displacementTexture = currentWater.simulation.gpuBuffers.displacementBuffer;
parameters.deformationBuffer = currentWater.GetDeformationBuffer(this, supportDecals, Texture2D.blackTexture);
parameters.waterMask = currentWater.GetSimulationMaskBuffer(this, supportDecals, Texture2D.whiteTexture);
// Current
parameters.largeCurrentMap = currentWater.GetLargeCurrentBuffer(this, supportDecals, Texture2D.blackTexture);
parameters.ripplesCurrentMap = currentWater.GetRipplesCurrentBuffer(this, supportDecals, Texture2D.blackTexture);
parameters.activeCurrent = parameters.ripplesCurrentMap != Texture2D.blackTexture || parameters.largeCurrentMap != Texture2D.blackTexture;
// Property block used for binding the textures
currentWater.FillMaterialPropertyBlock(this, supportDecals);
parameters.mpb = currentWater.mpb;
// Setup the constant buffers
UpdataPerCameraConstantBuffer(currentWater, hdCamera, settings,
parameters.instancedQuads, parameters.infinite, parameters.customMesh, parameters.surfaceIndex);
parameters.worldToWaterMatrixCustom = currentWater.simulation.rendering.worldToWaterMatrixCustom;
}
class WaterRenderingData
{
public GraphicsBuffer patchDataBuffer;
public GraphicsBuffer indirectBuffer;
public GraphicsBuffer frustumBuffer;
public GraphicsBuffer heightBuffer;
public Texture2D surfaceFoamTexture;
public Texture2D sectorDataBuffer;
public int numSurfaces;
public WaterSurfaceGBufferData[] surfaces;
public ShaderVariablesWaterPerCamera[] sharedPerCameraDataArray;
public bool decalWorkflow;
public GraphicsBuffer surfaceProfiles;
public GraphicsBuffer[] perSurfaceCB;
public GraphicsBuffer perCameraCB;
// Meshes
public Mesh tessellableMesh, ringMesh, ringMeshLow;
// Shaders
public ComputeShader waterSimulation;
public int patchEvaluation, patchEvaluationInfinite;
public ComputeShader evaluationCS;
public int findVerticalDisplKernel;
// Camera parameters
public int viewCount;
public bool exclusion;
public void BindGlobal(CommandBuffer cmd)
{
cmd.SetGlobalBuffer(HDShaderIDs._WaterPatchData, patchDataBuffer);
cmd.SetGlobalBuffer(HDShaderIDs._WaterSurfaceProfiles, surfaceProfiles);
cmd.SetGlobalBuffer(HDShaderIDs._FrustumGPUBuffer, frustumBuffer);
cmd.SetGlobalTexture(HDShaderIDs._FoamTexture, surfaceFoamTexture);
cmd.SetGlobalTexture(HDShaderIDs._WaterSectorData, sectorDataBuffer);
}
}
void PrepareWaterRenderingData(WaterRenderingData passData, HDCamera hdCamera)
{
PropagateFrustumDataToGPU(hdCamera);
CheckWaterCurrentData();
passData.patchDataBuffer = m_WaterPatchDataBuffer;
passData.indirectBuffer = m_WaterIndirectDispatchBuffer;
passData.frustumBuffer = m_WaterCameraFrustrumBuffer;
passData.heightBuffer = m_WaterCameraHeightBuffer;
passData.surfaceFoamTexture = m_RuntimeResources.foamMask;
passData.sectorDataBuffer = m_WaterSectorData;
passData.numSurfaces = Mathf.Min(WaterSurface.instanceCount, k_MaxNumWaterSurfaceProfiles);
passData.surfaces = m_WaterGBufferDataArray;
passData.sharedPerCameraDataArray = m_ShaderVariablesPerCameraArray;
passData.decalWorkflow = m_EnableDecalWorkflow;
passData.surfaceProfiles = m_WaterProfileArrayGPU;
passData.perSurfaceCB = m_ShaderVariablesWaterPerSurface;
passData.perCameraCB = m_ShaderVariablesWaterPerCamera;
// Meshes
passData.tessellableMesh = m_GridMesh;
passData.ringMesh = m_RingMesh;
passData.ringMeshLow = m_RingMeshLow;
// Patch evaluation parameters
passData.waterSimulation = m_WaterSimulationCS;
passData.patchEvaluation = m_EvaluateInstanceDataKernel;
passData.patchEvaluationInfinite = m_EvaluateInstanceDataInfiniteKernel;
// Underwater
passData.evaluationCS = m_WaterEvaluationCS;
passData.findVerticalDisplKernel = m_FindVerticalDisplacementsKernel;
// Camera data
passData.viewCount = hdCamera.viewCount;
passData.exclusion = hdCamera.frameSettings.IsEnabled(FrameSettingsField.WaterExclusion);
}
class WaterGBufferData : WaterRenderingData
{
// Buffers
public bool decalsEnabled;
public BufferHandle layeredOffsetsBuffer;
public BufferHandle logBaseBuffer;
public TextureHandle normalBuffer;
public TextureHandle depthPyramid;
}
void PrepareWaterGBufferData(RenderGraphBuilder builder, HDCamera hdCamera, TextureHandle normalBuffer, TextureHandle depthPyramid,
in HDRenderPipeline.BuildGPULightListOutput lightLists, ref WaterGBuffer gbuffer, WaterGBufferData passData)
{
WaterRendering settings = hdCamera.volumeStack.GetComponent<WaterRendering>();
PrepareWaterRenderingData(passData, hdCamera);
// Buffers
passData.decalsEnabled = (hdCamera.frameSettings.IsEnabled(FrameSettingsField.Decals)) && (DecalSystem.m_DecalDatasCount > 0);
passData.layeredOffsetsBuffer = builder.ReadBuffer(lightLists.perVoxelOffset);
passData.logBaseBuffer = builder.ReadBuffer(lightLists.perTileLogBaseTweak);
passData.normalBuffer = builder.ReadTexture(normalBuffer);
passData.depthPyramid = builder.ReadTexture(depthPyramid);
builder.WriteBuffer(gbuffer.cameraHeight);
// Grab all the water surfaces in the scene
var waterSurfaces = WaterSurface.instancesAsArray;
for (int surfaceIdx = 0; surfaceIdx < passData.numSurfaces; ++surfaceIdx)
{
// Grab the current water surface
WaterSurface currentWater = waterSurfaces[surfaceIdx];
ref var surfaceData = ref passData.surfaces[surfaceIdx];
surfaceData.render = ShouldRenderSurface(hdCamera, currentWater, ref gbuffer.debugRequired);
if (!surfaceData.render) continue;
// GBuffer is valid as long as one surface is rendered
gbuffer.valid = true;
// Fill the water surface profile
FillWaterSurfaceProfile(hdCamera, currentWater, surfaceIdx);
// Prepare all the internal parameters
PrepareSurfaceGBufferData(hdCamera, settings, currentWater, surfaceIdx, ref surfaceData);
}
// Push the water profiles to the GPU for the deferred lighting pass
m_WaterProfileArrayGPU.SetData(m_WaterSurfaceProfileArray);
}
void PropagateFrustumDataToGPU(HDCamera hdCamera)
{
// Plane 0
FrustumGPU frustum;
frustum.normal0 = hdCamera.frustum.planes[0].normal;
frustum.dist0 = hdCamera.frustum.planes[0].distance;
// Plane 1
frustum.normal1 = hdCamera.frustum.planes[1].normal;
frustum.dist1 = hdCamera.frustum.planes[1].distance;
// Plane 2
frustum.normal2 = hdCamera.frustum.planes[2].normal;
frustum.dist2 = hdCamera.frustum.planes[2].distance;
// Plane 3
frustum.normal3 = hdCamera.frustum.planes[3].normal;
frustum.dist3 = hdCamera.frustum.planes[3].distance;
// Plane 4
frustum.normal4 = hdCamera.frustum.planes[4].normal;
frustum.dist4 = hdCamera.frustum.planes[4].distance;
// Plane 4
frustum.normal5 = hdCamera.frustum.planes[5].normal;
frustum.dist5 = hdCamera.frustum.planes[5].distance;
// Corners
frustum.corner0 = hdCamera.frustum.corners[0];
frustum.corner1 = hdCamera.frustum.corners[1];
frustum.corner2 = hdCamera.frustum.corners[2];
frustum.corner3 = hdCamera.frustum.corners[3];
frustum.corner4 = hdCamera.frustum.corners[4];
frustum.corner5 = hdCamera.frustum.corners[5];
frustum.corner6 = hdCamera.frustum.corners[6];
frustum.corner7 = hdCamera.frustum.corners[7];
// Copy the data to the GPU
m_WaterCameraFrustumCPU[0] = frustum;
m_WaterCameraFrustrumBuffer.SetData(m_WaterCameraFrustumCPU);
}
internal static bool ShouldRenderWater(HDCamera hdCamera)
{
WaterRendering settings = hdCamera.volumeStack.GetComponent<WaterRendering>();
return !(!settings.enable.value
|| !hdCamera.frameSettings.IsEnabled(FrameSettingsField.Water)
|| WaterSurface.instanceCount == 0);
}
bool ShouldRenderSurface(HDCamera hdCamera, WaterSurface currentWater, ref bool debugRequired)
{
// At least one surface will need to be rendered as a debug view.
if (m_RenderPipeline.NeedDebugDisplay() || currentWater.debugMode != WaterDebugMode.None)
{
debugRequired = true;
return false;
}
// Only render the water surface if it is included in the layers that the camera requires
int waterCullingMask = 1 << currentWater.gameObject.layer;
if (hdCamera.camera.cullingMask != 0 && (waterCullingMask & hdCamera.camera.cullingMask) == 0)
return false;
#if UNITY_EDITOR
var stage = PrefabStageUtility.GetCurrentPrefabStage();
if (!CoreUtils.IsSceneViewPrefabStageContextHidden() && stage != null && stage.mode == PrefabStage.Mode.InContext)
{
bool isInPrefabScene = stage.scene == currentWater.gameObject.scene;
if ((hdCamera.camera.sceneViewFilterMode == Camera.SceneViewFilterMode.Off && isInPrefabScene)
|| (hdCamera.camera.sceneViewFilterMode == Camera.SceneViewFilterMode.ShowFiltered && !isInPrefabScene))
return false;
}
if (currentWater.customMaterial != null && !WaterSurface.IsWaterMaterial(currentWater.customMaterial))
return false;
#endif
return true;
}
static void RenderWaterSurface(CommandBuffer cmd, WaterRenderingData parameters, ref WaterSurfaceGBufferData surfaceData)
{
cmd.SetBufferData(parameters.perCameraCB, parameters.sharedPerCameraDataArray, surfaceData.surfaceIndex, 0, 1);
// Raise the keywords for band count
SetupWaterShaderKeyword(cmd, parameters.decalWorkflow, surfaceData.numActiveBands, surfaceData.activeCurrent);
// First we need to evaluate if we are in the underwater region of this water surface if the camera
// is above of under water. This will need to be done on the CPU later
if (surfaceData.evaluateCameraPosition)
{
BindPerSurfaceConstantBuffer(cmd, parameters.evaluationCS, parameters.perSurfaceCB[surfaceData.surfaceIndex]);
cmd.SetComputeConstantBufferParam(parameters.evaluationCS, HDShaderIDs._ShaderVariablesWaterPerCamera, parameters.perCameraCB, 0, parameters.perCameraCB.stride);
cmd.SetComputeBufferParam(parameters.evaluationCS, parameters.findVerticalDisplKernel, HDShaderIDs._WaterCameraHeightBufferRW, parameters.heightBuffer);
cmd.SetComputeTextureParam(parameters.evaluationCS, parameters.findVerticalDisplKernel, HDShaderIDs._WaterDisplacementBuffer, surfaceData.displacementTexture);
cmd.SetComputeTextureParam(parameters.evaluationCS, parameters.findVerticalDisplKernel, HDShaderIDs._WaterDeformationBuffer, surfaceData.deformationBuffer);
cmd.SetComputeTextureParam(parameters.evaluationCS, parameters.findVerticalDisplKernel, HDShaderIDs._WaterMask, surfaceData.waterMask);
if (surfaceData.activeCurrent)
{
cmd.SetComputeTextureParam(parameters.evaluationCS, parameters.findVerticalDisplKernel, HDShaderIDs._WaterSectorData, parameters.sectorDataBuffer);
cmd.SetComputeTextureParam(parameters.evaluationCS, parameters.findVerticalDisplKernel, HDShaderIDs._Group0CurrentMap, surfaceData.largeCurrentMap);
cmd.SetComputeTextureParam(parameters.evaluationCS, parameters.findVerticalDisplKernel, HDShaderIDs._Group1CurrentMap, surfaceData.ripplesCurrentMap);
}
cmd.DispatchCompute(parameters.evaluationCS, parameters.findVerticalDisplKernel, 1, 1, parameters.viewCount);
}
// Raise the right stencil flags
cmd.SetGlobalFloat(HDShaderIDs._StencilWaterRefGBuffer, (int)(StencilUsage.WaterSurface | StencilUsage.TraceReflectionRay));
cmd.SetGlobalFloat(HDShaderIDs._StencilWaterWriteMaskGBuffer, (int)(StencilUsage.WaterSurface | StencilUsage.TraceReflectionRay));
cmd.SetGlobalFloat(HDShaderIDs._StencilWaterReadMaskGBuffer, parameters.exclusion ? (int)(StencilUsage.WaterExclusion) : 0);
cmd.SetGlobalFloat(HDShaderIDs._CullWaterMask, surfaceData.evaluateCameraPosition ? (int)CullMode.Off : (int)CullMode.Back);
var passNames = surfaceData.tessellation ? k_PassesGBufferTessellation : k_PassesGBuffer;
DrawWaterSurface(cmd, passNames, parameters, ref surfaceData);
// Reset the keywords
ResetWaterShaderKeyword(cmd);
}
internal WaterGBuffer RenderWaterGBuffer(RenderGraph renderGraph, CullingResults cull, HDCamera hdCamera,
TextureHandle depthBuffer, TextureHandle normalBuffer,
TextureHandle colorPyramid, TextureHandle depthPyramid,
in HDRenderPipeline.BuildGPULightListOutput lightLists)
{
// Tile sizes
int tileX = (hdCamera.actualWidth + 7) / 8;
int tileY = (hdCamera.actualHeight + 7) / 8;
int numTiles = tileX * tileY;
// We need to tag the stencil for water rejection
WaterRejectionTag(renderGraph, cull, hdCamera, depthBuffer);
// Request all the gbuffer textures we will need
WaterGBuffer outputGBuffer = new WaterGBuffer()
{
valid = false,
debugRequired = false,
cameraHeight = renderGraph.ImportBuffer(m_WaterCameraHeightBuffer),
waterGBuffer0 = renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true, name = "Water GBuffer 0", fallBackToBlackTexture = true }),
waterGBuffer1 = renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GraphicsFormat.R8G8B8A8_UNorm, enableRandomWrite = true, name = "Water GBuffer 1", fallBackToBlackTexture = true }),
waterGBuffer2 = renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GraphicsFormat.R8G8B8A8_UNorm, enableRandomWrite = true, name = "Water GBuffer 2", fallBackToBlackTexture = true }),
waterGBuffer3 = renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GraphicsFormat.R8G8B8A8_UNorm, enableRandomWrite = true, name = "Water GBuffer 3", fallBackToBlackTexture = true }),
indirectBuffer = renderGraph.CreateBuffer(new BufferDesc((WaterConsts.k_NumWaterVariants + 1) * 3, sizeof(uint), GraphicsBuffer.Target.IndirectArguments) { name = "Water Deferred Indirect" }),
tileBuffer = renderGraph.CreateBuffer(new BufferDesc((WaterConsts.k_NumWaterVariants + 1) * numTiles * hdCamera.viewCount, sizeof(uint)) { name = "Water Deferred Tiles" })
};
using (var builder = renderGraph.AddRenderPass<WaterGBufferData>("Render Water GBuffer", out var passData, ProfilingSampler.Get(HDProfileId.WaterGBuffer)))
{
// Prepare data
PrepareWaterGBufferData(builder, hdCamera, normalBuffer, depthPyramid, in lightLists, ref outputGBuffer, passData);
// Request the output textures
builder.UseColorBuffer(outputGBuffer.waterGBuffer0, 0);
builder.UseColorBuffer(outputGBuffer.waterGBuffer1, 1);
builder.UseColorBuffer(outputGBuffer.waterGBuffer2, 2);
builder.UseColorBuffer(outputGBuffer.waterGBuffer3, 3);
builder.UseDepthBuffer(depthBuffer, DepthAccess.ReadWrite);
builder.SetRenderFunc(
(WaterGBufferData data, RenderGraphContext ctx) =>
{
if (data.decalsEnabled)
DecalSystem.instance.SetAtlas(ctx.cmd);
if (data.layeredOffsetsBuffer.IsValid())
ctx.cmd.SetGlobalBuffer(HDShaderIDs.g_vLayeredOffsetsBuffer, data.layeredOffsetsBuffer);
if (data.logBaseBuffer.IsValid())
ctx.cmd.SetGlobalBuffer(HDShaderIDs.g_logBaseBuffer, data.logBaseBuffer);
ctx.cmd.SetGlobalTexture(HDShaderIDs._NormalBufferTexture, data.normalBuffer);
ctx.cmd.SetGlobalTexture(HDShaderIDs._CameraDepthTexture, data.depthPyramid);
data.BindGlobal(ctx.cmd);
for (int surfaceIdx = 0; surfaceIdx < data.numSurfaces; ++surfaceIdx)
{
ref var surfaceData = ref data.surfaces[surfaceIdx];
if (surfaceData.render)
RenderWaterSurface(ctx.cmd, data, ref surfaceData);
}
});
}
if (outputGBuffer.valid)
PrepareWaterLighting(renderGraph, hdCamera, depthBuffer, normalBuffer, lightLists, ref outputGBuffer);
return outputGBuffer;
}
internal void RenderWaterFromCamera(Camera camera, CommandBuffer cmd, int mode)
{
var hdCamera = HDCamera.GetOrCreate(camera, 0);
if (!ShouldRenderWater(hdCamera))
return;
// Backup frustum as we are rendering from another point of view
var frustum = m_WaterCameraFrustumCPU[0];
var globalCB = m_RenderPipeline.GetShaderVariablesGlobalCB();
// Upload mode
if (mode != 0)
{
globalCB._CustomOutputForCustomPass = mode;
ConstantBuffer.PushGlobal(cmd, globalCB, HDShaderIDs._ShaderVariablesGlobal);
}
WaterRenderingData passData = new();
PrepareWaterRenderingData(passData, hdCamera);
int numWaterSurfaces = Mathf.Min(WaterSurface.instanceCount, k_MaxNumWaterSurfaceProfiles);
for (int surfaceIdx = 0; surfaceIdx < numWaterSurfaces; ++surfaceIdx)
{
ref var surfaceData = ref passData.surfaces[surfaceIdx];
if (surfaceData.render)
RenderWaterSurface(cmd, passData, ref surfaceData);
}
if (mode != 0)
{
globalCB._CustomOutputForCustomPass = 0;
ConstantBuffer.PushGlobal(cmd, globalCB, HDShaderIDs._ShaderVariablesGlobal);
}
// Restore camera frustum
m_WaterCameraFrustumCPU[0] = frustum;
m_WaterCameraFrustrumBuffer.SetData(m_WaterCameraFrustumCPU);
}
#endregion
#region Prepare Lighting
class WaterPrepareLightingData
{
// Camera data
public int width;
public int height;
public int viewCount;
public int tileX;
public int tileY;
public int numTiles;
public bool transparentSSR;
// Shader data
public ComputeShader waterLighting;
public int clearIndirectKernel;
public int classifyTilesKernel;
public int prepareSSRKernel;
// Input textures
public TextureHandle depthBuffer;
public TextureHandle gbuffer1;
public TextureHandle gbuffer3;
public BufferHandle perVoxelOffset;
public BufferHandle perTileLogBaseTweak;
// Output texture
public TextureHandle normalBuffer;
public BufferHandle indirectBuffer;
public BufferHandle tileBuffer;
}
void PrepareWaterLighting(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle depthBuffer, TextureHandle normalBuffer, in HDRenderPipeline.BuildGPULightListOutput lightLists, ref WaterGBuffer gbuffer)
{
using (var builder = renderGraph.AddRenderPass<WaterPrepareLightingData>("Prepare water for lighting", out var passData, ProfilingSampler.Get(HDProfileId.WaterPrepareLighting)))
{
// Camera parameters
passData.width = hdCamera.actualWidth;
passData.height = hdCamera.actualHeight;
passData.viewCount = hdCamera.viewCount;
passData.tileX = (passData.width + 7) / 8;
passData.tileY = (passData.height + 7) / 8;
passData.numTiles = passData.tileX * passData.tileY;
passData.transparentSSR = hdCamera.IsSSREnabled(true);
// CS and kernels
passData.waterLighting = m_WaterLightingCS;
passData.prepareSSRKernel = m_WaterPrepareSSRIndirectKernel;
passData.clearIndirectKernel = m_WaterClearIndirectKernel;
passData.classifyTilesKernel = m_WaterClassifyTilesKernel;
// Input resources
passData.gbuffer1 = builder.ReadTexture(gbuffer.waterGBuffer1);
passData.gbuffer3 = builder.ReadTexture(gbuffer.waterGBuffer3);
passData.depthBuffer = builder.UseDepthBuffer(depthBuffer, DepthAccess.Read);
passData.perVoxelOffset = builder.ReadBuffer(lightLists.perVoxelOffset);
passData.perTileLogBaseTweak = builder.ReadBuffer(lightLists.perTileLogBaseTweak);
// Output resources
passData.normalBuffer = builder.WriteTexture(normalBuffer);
passData.indirectBuffer = builder.WriteBuffer(gbuffer.indirectBuffer);
passData.tileBuffer = builder.WriteBuffer(gbuffer.tileBuffer);
builder.SetRenderFunc(
(WaterPrepareLightingData data, RenderGraphContext ctx) =>
{
// Clear indirect args
ctx.cmd.SetComputeBufferParam(data.waterLighting, data.clearIndirectKernel, HDShaderIDs._WaterDispatchIndirectBuffer, data.indirectBuffer);
ctx.cmd.DispatchCompute(data.waterLighting, data.clearIndirectKernel, 1, 1, 1);
// Bind the input gbuffer data
int kernel = data.classifyTilesKernel;
ctx.cmd.SetComputeIntParam(data.waterLighting, HDShaderIDs._WaterNumTiles, data.numTiles);
ctx.cmd.SetComputeBufferParam(data.waterLighting, kernel, HDShaderIDs._WaterDispatchIndirectBuffer, data.indirectBuffer);
ctx.cmd.SetComputeBufferParam(data.waterLighting, kernel, HDShaderIDs._WaterTileBufferRW, data.tileBuffer);
ctx.cmd.SetComputeTextureParam(data.waterLighting, kernel, HDShaderIDs._DepthTexture, data.depthBuffer);
ctx.cmd.SetComputeTextureParam(data.waterLighting, kernel, HDShaderIDs._StencilTexture, data.depthBuffer, 0, RenderTextureSubElement.Stencil);
ctx.cmd.SetComputeBufferParam(data.waterLighting, kernel, HDShaderIDs.g_vLayeredOffsetsBuffer, data.perVoxelOffset);
ctx.cmd.SetComputeBufferParam(data.waterLighting, kernel, HDShaderIDs.g_logBaseBuffer, data.perTileLogBaseTweak);
ctx.cmd.DispatchCompute(data.waterLighting, kernel, data.tileX, data.tileY, data.viewCount);
if (data.transparentSSR)
{
// Prepare the normal buffer for SSR
ctx.cmd.SetComputeTextureParam(data.waterLighting, data.prepareSSRKernel, HDShaderIDs._WaterGBufferTexture1, data.gbuffer1);
ctx.cmd.SetComputeTextureParam(data.waterLighting, data.prepareSSRKernel, HDShaderIDs._WaterGBufferTexture3, data.gbuffer3);
ctx.cmd.SetComputeTextureParam(data.waterLighting, data.prepareSSRKernel, HDShaderIDs._StencilTexture, data.depthBuffer, 0, RenderTextureSubElement.Stencil);
ctx.cmd.SetComputeTextureParam(data.waterLighting, data.prepareSSRKernel, HDShaderIDs._NormalBufferRW, data.normalBuffer);
ctx.cmd.SetComputeBufferParam(data.waterLighting, data.prepareSSRKernel, HDShaderIDs._WaterTileBuffer, data.tileBuffer);
ctx.cmd.DispatchCompute(data.waterLighting, data.prepareSSRKernel, data.indirectBuffer, (uint)WaterConsts.k_NumWaterVariants * 3 * sizeof(uint));
}
});
}
}
#endregion
#region Deferred Lighting
struct WaterRenderingDeferredParameters
{
// Deferred Lighting
public ComputeShader waterLighting;
public int[] indirectLightingKernels;
public int lightingKernel;
public int numVariants;
public int waterFogKernel;
}
WaterRenderingDeferredParameters PrepareWaterRenderingDeferredParameters(HDCamera hdCamera, bool needFogTransmittance)
{
WaterRenderingDeferredParameters parameters = new WaterRenderingDeferredParameters();
// Deferred Lighting
parameters.waterLighting = m_WaterLightingCS;
parameters.indirectLightingKernels = m_WaterIndirectDeferredKernels;
parameters.numVariants = WaterConsts.k_NumWaterVariants;
parameters.waterFogKernel = needFogTransmittance ? m_WaterFogTransmittanceIndirectKernel : m_WaterFogIndirectKernel;
return parameters;
}
class WaterRenderingDeferredData
{
// All the parameters required to simulate and render the water
public WaterRenderingDeferredParameters parameters;
public bool pbrSkyActive;
// GBuffer Data
public BufferHandle indirectBuffer;
public BufferHandle tileBuffer;
public TextureHandle gbuffer0;
public TextureHandle gbuffer1;
public TextureHandle gbuffer2;
public TextureHandle gbuffer3;
public TextureHandle depthBuffer;
public TextureHandle depthPyramid;
// Lighting textures/buffers
public TextureHandle volumetricLightingTexture;
public TextureHandle transparentSSRLighting;
public BufferHandle perVoxelOffset;
public BufferHandle perTileLogBaseTweak;
public BufferHandle cameraHeightBuffer;
public BufferHandle waterLine;
// Temporary buffers
public TextureHandle waterLightingBuffer;
// Water rendered to this buffer
public TextureHandle colorBuffer;
public TextureHandle transmittanceBuffer;
}
internal void RenderWaterLighting(RenderGraph renderGraph, HDCamera hdCamera,
TextureHandle colorBuffer, TextureHandle depthBuffer, TextureHandle depthPyramid,
TextureHandle volumetricLightingTexture, TextureHandle ssrLighting,
in HDRenderPipeline.TransparentPrepassOutput prepassOutput, in HDRenderPipeline.BuildGPULightListOutput lightLists, ref TextureHandle opticalFogTransmittance)
{
// We do not render the deferred lighting if:
// - Water rendering is disabled.
// - The water gbuffer was never written.
if (!ShouldRenderWater(hdCamera) || !prepassOutput.waterGBuffer.valid)
return;
// Execute the unique lighting pass
using (var builder = renderGraph.AddRenderPass<WaterRenderingDeferredData>("Water Deferred Lighting", out var passData, ProfilingSampler.Get(HDProfileId.WaterDeferredLighting)))
{
bool needFogTransmittance = LensFlareCommonSRP.IsCloudLayerOpacityNeeded(hdCamera.camera) || Fog.IsMultipleScatteringEnabled(hdCamera, out _);
if (needFogTransmittance)
{
if (!opticalFogTransmittance.IsValid())
opticalFogTransmittance = renderGraph.CreateTexture(HDRenderPipeline.GetOpticalFogTransmittanceDesc(hdCamera));
passData.transmittanceBuffer = builder.ReadWriteTexture(opticalFogTransmittance);
}
// Prepare all the internal parameters
passData.parameters = PrepareWaterRenderingDeferredParameters(hdCamera, needFogTransmittance);
passData.pbrSkyActive = hdCamera.volumeStack.GetComponent<VisualEnvironment>().skyType.value == (int)SkyType.PhysicallyBased;
// GBuffer data
passData.indirectBuffer = builder.ReadBuffer(prepassOutput.waterGBuffer.indirectBuffer);
passData.tileBuffer = builder.ReadBuffer(prepassOutput.waterGBuffer.tileBuffer);
passData.gbuffer0 = builder.ReadTexture(prepassOutput.waterGBuffer.waterGBuffer0);
passData.gbuffer1 = builder.ReadTexture(prepassOutput.waterGBuffer.waterGBuffer1);
passData.gbuffer2 = builder.ReadTexture(prepassOutput.waterGBuffer.waterGBuffer2);
passData.gbuffer3 = builder.ReadTexture(prepassOutput.waterGBuffer.waterGBuffer3);
passData.depthBuffer = builder.UseDepthBuffer(depthBuffer, DepthAccess.Read);
passData.depthPyramid = builder.ReadTexture(depthPyramid);
passData.volumetricLightingTexture = builder.ReadTexture(volumetricLightingTexture);
passData.transparentSSRLighting = builder.ReadTexture(ssrLighting);
passData.perVoxelOffset = builder.ReadBuffer(lightLists.perVoxelOffset);
passData.perTileLogBaseTweak = builder.ReadBuffer(lightLists.perTileLogBaseTweak);
passData.cameraHeightBuffer = builder.ReadBuffer(prepassOutput.waterGBuffer.cameraHeight);
passData.waterLine = builder.ReadBuffer(prepassOutput.waterLine);
// Request the output textures
passData.waterLightingBuffer = builder.CreateTransientTexture(colorBuffer);
passData.colorBuffer = builder.WriteTexture(colorBuffer);
// Run the deferred lighting
builder.SetRenderFunc(
(WaterRenderingDeferredData data, RenderGraphContext ctx) =>
{
for (int variantIdx = 0; variantIdx < data.parameters.numVariants; ++variantIdx)
{
// Kernel to be used for the target variant
int kernel = data.parameters.indirectLightingKernels[variantIdx];
// Bind the input gbuffer data
ctx.cmd.SetComputeBufferParam(data.parameters.waterLighting, kernel, HDShaderIDs._WaterTileBuffer, data.tileBuffer);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._WaterGBufferTexture0, data.gbuffer0);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._WaterGBufferTexture1, data.gbuffer1);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._WaterGBufferTexture2, data.gbuffer2);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._WaterGBufferTexture3, data.gbuffer3);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._DepthTexture, data.depthBuffer);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._StencilTexture, data.depthBuffer, 0, RenderTextureSubElement.Stencil);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._SsrLightingTexture, data.transparentSSRLighting);
ctx.cmd.SetComputeBufferParam(data.parameters.waterLighting, kernel, HDShaderIDs.g_vLayeredOffsetsBuffer, data.perVoxelOffset);
ctx.cmd.SetComputeBufferParam(data.parameters.waterLighting, kernel, HDShaderIDs.g_logBaseBuffer, data.perTileLogBaseTweak);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._CameraDepthTexture, data.depthPyramid);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._ColorPyramidTexture, data.colorBuffer); // caution, this is not a pyramid, we can't access LODs
// Bind the output texture
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, kernel, HDShaderIDs._CameraColorTextureRW, data.waterLightingBuffer);
// Run the lighting
ctx.cmd.DispatchCompute(data.parameters.waterLighting, kernel, data.indirectBuffer, (uint)variantIdx * 3 * sizeof(uint));
}
if (!data.pbrSkyActive)
PhysicallyBasedSkyRenderer.SetDefaultGlobalSkyData(ctx.cmd);
// Evaluate the fog
int fogKernel = data.parameters.waterFogKernel;
ctx.cmd.SetComputeBufferParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._WaterLineBuffer, data.waterLine);
ctx.cmd.SetComputeBufferParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._WaterTileBuffer, data.tileBuffer);
ctx.cmd.SetComputeBufferParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._WaterCameraHeightBuffer, data.cameraHeightBuffer);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._WaterGBufferTexture3, data.gbuffer3);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._VBufferLighting, data.volumetricLightingTexture);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._DepthTexture, data.depthBuffer);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._StencilTexture, data.depthBuffer, 0, RenderTextureSubElement.Stencil);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._CameraColorTexture, data.waterLightingBuffer);
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._CameraColorTextureRW, data.colorBuffer);
if (data.transmittanceBuffer.IsValid())
ctx.cmd.SetComputeTextureParam(data.parameters.waterLighting, fogKernel, HDShaderIDs._TransmittanceBufferRW, data.transmittanceBuffer);
ctx.cmd.DispatchCompute(data.parameters.waterLighting, fogKernel, data.indirectBuffer, (uint)WaterConsts.k_NumWaterVariants * 3 * sizeof(uint));
});
}
}
#endregion
#region Exclusion
class WaterExclusionPassData
{
public FrameSettings frameSettings;
public TextureHandle depthBuffer;
public RendererListHandle opaqueRenderList;
}
void WaterRejectionTag(RenderGraph renderGraph, CullingResults cull, HDCamera hdCamera, TextureHandle depthBuffer)
{
if (!hdCamera.frameSettings.IsEnabled(FrameSettingsField.WaterExclusion))
return;
using (var builder = renderGraph.AddRenderPass<WaterExclusionPassData>("Water Exclusion", out var passData, ProfilingSampler.Get(HDProfileId.WaterExclusion)))
{
var depthStateNoWrite = new RenderStateBlock
{
depthState = new DepthState(false, CompareFunction.LessEqual),
mask = RenderStateMask.Depth
};
passData.frameSettings = hdCamera.frameSettings;
passData.depthBuffer = builder.UseDepthBuffer(depthBuffer, DepthAccess.ReadWrite);
passData.opaqueRenderList = builder.UseRendererList(renderGraph.CreateRendererList(HDRenderPipeline.CreateOpaqueRendererListDesc(cull, hdCamera.camera, HDShaderPassNames.s_WaterStencilTagName, stateBlock: depthStateNoWrite)));
builder.SetRenderFunc(
(WaterExclusionPassData data, RenderGraphContext ctx) =>
{
ctx.cmd.SetGlobalInteger(HDShaderIDs._StencilWriteMaskStencilTag, (int)StencilUsage.WaterExclusion);
ctx.cmd.SetGlobalInteger(HDShaderIDs._StencilRefMaskStencilTag, (int)StencilUsage.WaterExclusion);
CoreUtils.DrawRendererList(ctx.renderContext, ctx.cmd, data.opaqueRenderList);
});
}
}
#endregion
}
[Serializable]
[SupportedOnRenderPipeline(typeof(HDRenderPipelineAsset))]
[Categorization.CategoryInfo(Name = "Water System", Order = 20)]
class WaterSystemGlobalSettings : IRenderPipelineGraphicsSettings
{
[SerializeField, HideInInspector]
int m_Version = 1;
[SerializeField, Tooltip("Enable mask and current outputs in water decals.")]
bool m_EnableMaskAndCurrentWaterDecals = false;
public int version { get => m_Version; }
public bool isAvailableInPlayerBuild { get => true; }
public bool waterDecalMaskAndCurrent
{
get => m_EnableMaskAndCurrentWaterDecals;
set => this.SetValueAndNotify(ref m_EnableMaskAndCurrentWaterDecals, value, nameof(m_EnableMaskAndCurrentWaterDecals));
}
}
[Serializable]
[SupportedOnRenderPipeline(typeof(HDRenderPipelineAsset))]
[Categorization.CategoryInfo(Name = "R: Water System", Order = 1000), HideInInspector]
class WaterSystemRuntimeResources : IRenderPipelineResources
{
public int version => 0;
#region Materials
[Header("Materials")]
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/ShaderGraph/Water.shadergraph")]
private Material m_WaterMaterial;
public Material waterMaterial
{
get => m_WaterMaterial;
set => this.SetValueAndNotify(ref m_WaterMaterial, value);
}
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/Material/MaterialWaterExclusion.mat")]
private Material m_WaterExclusionMaterial;
public Material waterExclusionMaterial
{
get => m_WaterExclusionMaterial;
set => this.SetValueAndNotify(ref m_WaterExclusionMaterial, value);
}
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/ShaderGraph/Water Decal.shadergraph")]
private Material m_WaterDecalMaterial;
public Material waterDecalMaterial
{
get => m_WaterDecalMaterial;
set => this.SetValueAndNotify(ref m_WaterDecalMaterial, value);
}
#endregion
#region Shaders
[Header("Shaders")]
[SerializeField, ResourcePath("Runtime/Water/Shaders/WaterSimulation.compute")]
private ComputeShader m_WaterSimulationCS;
public ComputeShader waterSimulationCS
{
get => m_WaterSimulationCS;
set => this.SetValueAndNotify(ref m_WaterSimulationCS, value);
}
[SerializeField, ResourcePath("Runtime/Water/Shaders/FourierTransform.compute")]
private ComputeShader m_FourierTransformCS;
public ComputeShader fourierTransformCS
{
get => m_FourierTransformCS;
set => this.SetValueAndNotify(ref m_FourierTransformCS, value);
}
[SerializeField, ResourcePath("Runtime/Water/Shaders/WaterEvaluation.compute")]
private ComputeShader m_WaterEvaluationCS;
public ComputeShader waterEvaluationCS
{
get => m_WaterEvaluationCS;
set => this.SetValueAndNotify(ref m_WaterEvaluationCS, value);
}
[SerializeField, ResourcePath("Runtime/RenderPipelineResources/ShaderGraph/Water.shadergraph")]
private Shader m_WaterPS;
public Shader waterPS
{
get => m_WaterPS;
set => this.SetValueAndNotify(ref m_WaterPS, value);
}
[SerializeField, ResourcePath("Runtime/Water/Shaders/WaterLighting.compute")]
private ComputeShader m_WaterLightingCS;
public ComputeShader waterLightingCS
{
get => m_WaterLightingCS;
set => this.SetValueAndNotify(ref m_WaterLightingCS, value);
}
[SerializeField, ResourcePath("Runtime/Water/Shaders/WaterLine.compute")]
private ComputeShader m_WaterLineCS;
public ComputeShader waterLineCS
{
get => m_WaterLineCS;
set => this.SetValueAndNotify(ref m_WaterLineCS, value);
}
[SerializeField, ResourcePath("Runtime/Water/Shaders/WaterCaustics.shader")]
private Shader m_WaterCausticsPS;
public Shader waterCausticsPS
{
get => m_WaterCausticsPS;
set => this.SetValueAndNotify(ref m_WaterCausticsPS, value);
}
[SerializeField, ResourcePath("Runtime/Water/Shaders/WaterDecal.shader")]
private Shader m_WaterDecalPS;
public Shader waterDecalPS
{
get => m_WaterDecalPS;
set => this.SetValueAndNotify(ref m_WaterDecalPS, value);
}
[SerializeField, ResourcePath("Runtime/Water/Shaders/WaterDeformation.compute")]
private ComputeShader m_WaterDeformationCS;
public ComputeShader waterDeformationCS
{
get => m_WaterDeformationCS;
set => this.SetValueAndNotify(ref m_WaterDeformationCS, value);
}
[SerializeField, ResourcePath("Runtime/Water/Shaders/WaterFoam.compute")]
private ComputeShader m_WaterFoamCS;
public ComputeShader waterFoamCS
{
get => m_WaterFoamCS;
set => this.SetValueAndNotify(ref m_WaterFoamCS, value);
}
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/ShaderGraph/Sample Water Decal.shadergraph")]
private Shader m_WaterDecalMigrationShader;
public Shader waterDecalMigrationShader
{
get => m_WaterDecalMigrationShader;
set => this.SetValueAndNotify(ref m_WaterDecalMigrationShader, value);
}
#endregion
#region Textures
[Header("Textures")]
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/Texture/Water/FoamMask.png")]
private Texture2D m_FoamMask;
public Texture2D foamMask
{
get => m_FoamMask;
set => this.SetValueAndNotify(ref m_FoamMask, value);
}
#endregion
}
}