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.
 
 
 
 

237 lines
9.9 KiB

#ifndef UNDER_WATER_UTILITIES_H
#define UNDER_WATER_UTILITIES_H
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStencilUsage.cs.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Water/WaterSystemDef.cs.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/NormalBuffer.hlsl"
// This file is included in various part of HDRP
// We don't want water specific global shader variables to leak into the global scope
#if defined(WATER_FOG_PASS) || defined(WATER_SURFACE_GBUFFER) || defined(WATER_ONE_BAND) || defined(WATER_TWO_BANDS) || defined(WATER_THREE_BANDS)
#define IS_HDRP_WATER_SYSTEM_PASS
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Water/ShaderVariablesWater.cs.hlsl"
#endif
#if defined(_SURFACE_TYPE_TRANSPARENT) && defined(_ENABLE_FOG_ON_TRANSPARENT)
#define SUPPORT_WATER_ABSORPTION
#endif
// Buffers used for refraction sorting
#if defined(SUPPORT_WATER_ABSORPTION) || defined (_TRANSPARENT_REFRACTIVE_SORT)
TYPED_TEXTURE2D_X(uint2, _StencilTexture);
TEXTURE2D_X(_RefractiveDepthBuffer);
#endif
StructuredBuffer<float> _WaterCameraHeightBuffer;
float GetWaterCameraHeight()
{
return _WaterCameraHeightBuffer[4 * unity_StereoEyeIndex];
}
StructuredBuffer<uint> _WaterLine;
float GetUnderWaterDistance(uint2 coord)
{
float2 upVector = float2(_UpDirectionX, _UpDirectionY);
float2 rightVector = float2(_UpDirectionY, -_UpDirectionX);
// Find index to sample in 1D buffer
uint xr = unity_StereoEyeIndex * _BufferStride;
uint2 boundsX = uint2(0xFFFFFFFF - _WaterLine[0 + xr], _WaterLine[1 + xr]);
uint posX = round(dot((float2)coord.xy, rightVector) - _BoundsSS.x);
posX = clamp(posX, min(boundsX.y, boundsX.x), max(boundsX.x, boundsX.y));
// Decompress water line height
float posY = dot((float2)coord.xy, upVector) - _BoundsSS.z;
uint packedValue = _WaterLine[posX + 2 + xr] & 0xFFFF;
float waterLine = packedValue - 1;
// Normalize distance to water line
float maxHeight = (_BoundsSS.w - _BoundsSS.z);
float distanceToWaterLine = (floor(posY) - waterLine) / maxHeight;
return distanceToWaterLine;
}
bool IsUnderWater(uint2 coord)
{
return GetUnderWaterDistance(coord) < 0.0f;
}
TEXTURE2D_X(_WaterGBufferTexture0);
TEXTURE2D_X(_WaterGBufferTexture1);
TEXTURE2D_X(_WaterGBufferTexture2);
TEXTURE2D_X_FLOAT(_WaterGBufferTexture3); // force high-precision else UnpackSurfaceIndex gives incorrect values on builds defaulting to mediump
StructuredBuffer<WaterSurfaceProfile> _WaterSurfaceProfiles;
uint UnpackSurfaceIndex(uint2 positionSS)
{
float4 inGBuffer3 = LOAD_TEXTURE2D_X(_WaterGBufferTexture3, positionSS);
return ((uint)(inGBuffer3.w * 255.0f)) & 0xf;
}
uint GetWaterSurfaceIndex(uint2 positionSS)
{
return IsUnderWater(positionSS) ? _UnderWaterSurfaceIndex : UnpackSurfaceIndex(positionSS);
}
Texture2D<float4> _WaterCausticsDataBuffer;
#if defined(IS_HDRP_WATER_SYSTEM_PASS) || defined(SUPPORT_WATER_ABSORPTION)
float EvaluateSimulationCaustics(float3 refractedWaterPosRWS, float refractedWaterDepth, float2 distortedWaterNDC)
{
// We cannot have same variable names in different constant buffers, use some defines to select the correct ones
#ifdef SUPPORT_WATER_ABSORPTION
#define _CausticsIntensity _UnderWaterCausticsIntensity
#define _CausticsPlaneBlendDistance _UnderWaterCausticsPlaneBlendDistance
#define _CausticsTilingFactor _UnderWaterCausticsTilingFactor
#define _CausticsRegionSize _UnderWaterCausticsRegionSize
#define _CausticsMaxLOD _UnderWaterCausticsMaxLOD
#define _WaterSurfaceTransform_Inverse _UnderWaterSurfaceTransform_Inverse
#endif
float caustics = 0.0f;
// TODO: Is this worth a multicompile?
if (_CausticsIntensity != 0.0f)
{
// Evaluate the caustics weight
float causticWeight = saturate(refractedWaterDepth / _CausticsPlaneBlendDistance);
// Evaluate the normal of the surface (using partial derivatives of the absolute world pos is not possible as it is not stable enough)
NormalData normalData;
float4 normalBuffer = LOAD_TEXTURE2D_X_LOD(_NormalBufferTexture, distortedWaterNDC * _ScreenSize.xy, 0);
DecodeFromNormalBuffer(normalBuffer, normalData);
// Evaluate the triplanar weights
float3 normalOS = mul((float3x3)_WaterSurfaceTransform_Inverse, normalData.normalWS);
float3 triplanarW = ComputeTriplanarWeights(normalOS);
// Convert the position to absolute world space and move the position to the water local space
float3 causticPosAWS = GetAbsolutePositionWS(refractedWaterPosRWS) * _CausticsTilingFactor;
float3 causticPosOS = mul(_WaterSurfaceTransform_Inverse, float4(causticPosAWS, 1.0f)).xyz;
// Evaluate the triplanar coodinates
float3 sampleCoord = causticPosOS / (_CausticsRegionSize * 0.5) + 0.5;
float2 uv0, uv1, uv2;
GetTriplanarCoordinate(sampleCoord, uv0, uv1, uv2);
// Evaluate the sharpness of the caustics based on the depth
float sharpness = (1.0 - causticWeight) * _CausticsMaxLOD;
// sample the caustics texture
float3 causticsValues;
#if defined(SHADER_STAGE_COMPUTE)
causticsValues.x = SAMPLE_TEXTURE2D_LOD(_WaterCausticsDataBuffer, s_linear_repeat_sampler, uv0, sharpness).x;
causticsValues.y = SAMPLE_TEXTURE2D_LOD(_WaterCausticsDataBuffer, s_linear_repeat_sampler, uv1, sharpness).x;
causticsValues.z = SAMPLE_TEXTURE2D_LOD(_WaterCausticsDataBuffer, s_linear_repeat_sampler, uv2, sharpness).x;
#else
causticsValues.x = SAMPLE_TEXTURE2D_BIAS(_WaterCausticsDataBuffer, s_linear_repeat_sampler, uv0, sharpness).x;
causticsValues.y = SAMPLE_TEXTURE2D_BIAS(_WaterCausticsDataBuffer, s_linear_repeat_sampler, uv1, sharpness).x;
causticsValues.z = SAMPLE_TEXTURE2D_BIAS(_WaterCausticsDataBuffer, s_linear_repeat_sampler, uv2, sharpness).x;
#endif
caustics = dot(causticsValues, triplanarW.yzx) * causticWeight * _CausticsIntensity;
}
#ifdef SUPPORT_WATER_ABSORPTION
#undef _CausticsIntensity
#undef _CausticsPlaneBlendDistance
#undef _CausticsTilingFactor
#undef _CausticsRegionSize
#undef _CausticsMaxLOD
#undef _WaterSurfaceTransform_Inverse
#endif
// Evaluate the triplanar weights and blend the samples togheter
return 1.0 + caustics;
}
#endif
#ifdef SUPPORT_WATER_ABSORPTION
bool EvaluateUnderwaterAbsorption(PositionInputs posInput, out bool underWater, inout float3 opacity)
{
#ifdef _ENABLE_FOG_ON_TRANSPARENT
// Disable underwater on low res transparents
if (_OffScreenRendering == 1)
{
underWater = false;
return false;
}
#endif
uint surfaceIndex = -1;
bool hasWater = false, hasExcluder = false;
float waterDepth = UNITY_RAW_FAR_CLIP_VALUE;
underWater = IsUnderWater(posInput.positionSS.xy);
bool skipFog = false;
#ifdef _ENABLE_FOG_ON_TRANSPARENT
[branch]
if (_PreRefractionPass != 0)
#endif
{
waterDepth = LOAD_TEXTURE2D_X(_RefractiveDepthBuffer, posInput.positionSS.xy).r;
uint stencil = GetStencilValue(LOAD_TEXTURE2D_X(_StencilTexture, posInput.positionSS.xy));
hasExcluder = (stencil & STENCILUSAGE_WATER_EXCLUSION) != 0;
hasWater = (stencil & STENCILUSAGE_WATER_SURFACE) != 0;
float cameraToWater = underWater ? FLT_MAX : (hasWater ? waterDepth : 0.0f);
float cameraToSurface = (underWater && hasWater && waterDepth >= posInput.deviceDepth) ? FLT_MAX : posInput.deviceDepth;
surfaceIndex = cameraToSurface < cameraToWater ? GetWaterSurfaceIndex(posInput.positionSS.xy) : -1;
#ifdef SUPPORT_WATER_ABSORPTION
// Don't apply underwater fog on excluders
// Still apply on transparents even if an excluder is behind though
if (hasExcluder)
surfaceIndex = -1;
#endif
if (surfaceIndex == -1)
underWater = false;
// Don't apply volumetric fog when viewing surface from above
else if (!underWater)
{
WaterSurfaceProfile prof = _WaterSurfaceProfiles[surfaceIndex];
PositionInputs waterPosInput = GetPositionInput(posInput.positionSS.xy, _ScreenSize.zw, waterDepth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
float3 opticalDepth = 2 * length(posInput.positionWS - waterPosInput.positionWS) * prof.extinction;
opacity = 1 - TransmittanceFromOpticalDepth(opticalDepth);
skipFog = true;
}
}
return skipFog;
}
#endif
#ifdef _TRANSPARENT_REFRACTIVE_SORT
// This function is used only by pre refraction transparent objects (which includes volumetric clouds)
// It checks wether it's in front or behind a refractive object to output to the correct buffer for sorting
void ComputeRefractionSplitColor(PositionInputs posInput, inout float4 outColor, inout float4 outBeforeRefractionColor, inout float4 outBeforeRefractionAlpha)
{
const uint refractiveMask = STENCILUSAGE_REFRACTIVE | STENCILUSAGE_WATER_SURFACE;
bool hasRefractive = (GetStencilValue(LOAD_TEXTURE2D_X(_StencilTexture, posInput.positionSS.xy)) & refractiveMask) != 0;
float refractiveDepth = LOAD_TEXTURE2D_X(_RefractiveDepthBuffer, posInput.positionSS.xy).r;
bool beforeRefraction = hasRefractive && posInput.deviceDepth >= refractiveDepth; // pixels between refractive object and camera
// Perform per pixel sorting
if (beforeRefraction)
{
outBeforeRefractionColor = outColor;
outBeforeRefractionAlpha = float4(0, 0, 0, outColor.a);
outColor = 0;
// Because we don't have control over shader blend mode (to be compatible with VT and MV), we handle blending manually
if (_BlendMode == BLENDINGMODE_ADDITIVE)
outBeforeRefractionColor.a = outBeforeRefractionAlpha.a = 0;
}
else
{
outBeforeRefractionColor = 0;
outBeforeRefractionAlpha = 0;
}
}
#endif
#endif // UNDER_WATER_UTILITIES_H