#ifndef KWS_WATER_PASS_HELPERS #define KWS_WATER_PASS_HELPERS #ifndef KWS_WATER_VARIABLES #include "KWS_WaterVariables.cginc" #endif #ifndef KWS_COMMON_HELPERS #include "../Common/KWS_CommonHelpers.cginc" #endif #ifdef KWS_BUILTIN #ifndef KWS_PLATFORM_SPECIFIC_HELPERS #include "../PlatformSpecific/KWS_PlatformSpecificHelpers_Builtin.cginc" #endif #endif #ifdef KWS_URP #ifndef KWS_PLATFORM_SPECIFIC_HELPERS #include "../PlatformSpecific/KWS_PlatformSpecificHelpers_URP.cginc" #endif #endif #ifdef KWS_HDRP #ifndef KWS_PLATFORM_SPECIFIC_HELPERS #include "../PlatformSpecific/KWS_PlatformSpecificHelpers_HDRP.cginc" #endif #endif float CalcMipLevel(float2 uv) { float2 dx = ddx(uv); float2 dy = ddy(uv); float delta = max(dot(dx, dx), dot(dy, dy)); return max(0.0, 0.5 * log2(delta)); } ////////////////////////////////////////////// FFT_Waves_Pass ////////////////////////////////////////////// #define MAX_FFT_WAVES_MAX_CASCADES 4 float KWS_WavesDomainSizes[MAX_FFT_WAVES_MAX_CASCADES]; float KWS_WavesDomainVisiableArea[MAX_FFT_WAVES_MAX_CASCADES]; float KWS_WindSpeed; float KWS_WavesAreaScale; float KWS_WavesCascades; Texture2DArray KWS_FftWavesDisplace; Texture2DArray KWS_FftWavesNormal; SamplerState sampler_KWS_FftWavesNormal; float4 KWS_FftWavesDisplace_TexelSize; float4 KWS_FftWavesNormal_TexelSize; inline float GetDomainSize(uint idx) { return KWS_WavesDomainSizes[idx] * KWS_WavesAreaScale; } inline float GetDomainSize(uint idx, uint waterID) { return KWS_WavesDomainSizes[idx] * KWS_WavesAreaScale; } inline float GetDomainVisibleArea(uint idx) { return KWS_WavesDomainVisiableArea[idx] * KWS_WavesAreaScale; } float3 GetFftWavesDisplacementSlice(float3 worldPos, uint slice) { worldPos += KWS_WaterWorldPosOffset; return KWS_FftWavesDisplace.SampleLevel(sampler_linear_repeat, float3(worldPos.xz / GetDomainSize(slice), slice), 0).xyz * KWS_WavesAreaScale; } float3 GetFftWavesDisplacementSliceBicubic(float3 worldPos, uint slice) { worldPos += KWS_WaterWorldPosOffset; return Texture2DArraySampleLevelBicubic(KWS_FftWavesDisplace, sampler_linear_repeat, worldPos.xz / GetDomainSize(slice), KWS_FftWavesDisplace_TexelSize, slice, 0).xyz; } inline float GetFftFade(float distanceToCamera, int lodIdx, float farDistanceMinFade = 0.0) { if (lodIdx == KWS_WavesCascades - 1) { float farDist = max(500, KW_WaterFarDistance * 0.5); return saturate(1.0 + farDistanceMinFade - saturate(distanceToCamera / farDist)); } else { float fadeLod = saturate(distanceToCamera / GetDomainVisibleArea(lodIdx)); fadeLod = 1 - fadeLod * fadeLod * fadeLod; return fadeLod; } } float3 GetFftWavesDisplacementLast(float3 worldPos) { worldPos += KWS_WaterWorldPosOffset; int lastCascadeIdx = max(0, KWS_WavesCascades - 1); return KWS_FftWavesDisplace.SampleLevel(sampler_linear_repeat, float3(worldPos.xz / GetDomainSize(lastCascadeIdx), lastCascadeIdx), 0).xyz * KWS_WavesAreaScale; } float3 GetFftWavesDisplacementDetailsHQ(float3 worldPos) { worldPos += KWS_WaterWorldPosOffset; float3 disp = Texture2DArraySampleLevelBicubic(KWS_FftWavesDisplace, sampler_linear_repeat, worldPos.xz / GetDomainSize(0), KWS_FftWavesDisplace_TexelSize, 0, 0).xyz; if (KWS_WavesCascades > 1) disp += Texture2DArraySampleLevelBicubic(KWS_FftWavesDisplace, sampler_linear_repeat, worldPos.xz / GetDomainSize(1), KWS_FftWavesDisplace_TexelSize, 1, 0).xyz; return disp * KWS_WavesAreaScale; } float3 GetFftWavesDisplacementDetails(float3 worldPos) { worldPos += KWS_WaterWorldPosOffset; float3 disp = KWS_FftWavesDisplace.SampleLevel(sampler_linear_repeat, float3(worldPos.xz / GetDomainSize(0), 0), 0).xyz; if (KWS_WavesCascades > 1) disp += KWS_FftWavesDisplace.SampleLevel(sampler_linear_repeat, float3(worldPos.xz / GetDomainSize(1), 1), 0).xyz; return disp * KWS_WavesAreaScale; } float3 GetFftWavesDisplacementBuoyancy(float3 worldPos) { worldPos += KWS_WaterWorldPosOffset; float3 finalData = 0; for (int idx = KWS_WavesCascades - 1; idx > 0; idx--) { finalData += KWS_FftWavesDisplace.SampleLevel(sampler_linear_repeat, float3(worldPos.xz / GetDomainSize(idx), idx), 0).xyz; } return finalData * KWS_WavesAreaScale; } float3 GetFftWavesDisplacementDynamicWaves(float3 worldPos) { worldPos += KWS_WaterWorldPosOffset; int maxCascadeIdx = KWS_WavesCascades - 1; float3 finalData = 0; for (int idx = maxCascadeIdx; idx > 1; idx--) { finalData += KWS_FftWavesDisplace.SampleLevel(sampler_linear_repeat, float3(worldPos.xz / GetDomainSize(idx), idx), 0).xyz * saturate((idx + 0.25) / maxCascadeIdx); } return finalData * KWS_WavesAreaScale; } float3 GetFftWavesDisplacement(float3 worldPos) { float distanceToCamera = GetWorldToCameraDistance(worldPos); float3 finalData = 0; worldPos += KWS_WaterWorldPosOffset; UNITY_LOOP for (int idx = KWS_WavesCascades - 1; idx > 0; idx--) { float fade = GetFftFade(distanceToCamera, idx); if (fade < 0.01) continue; finalData += fade * KWS_FftWavesDisplace.SampleLevel(sampler_linear_repeat, float3(worldPos.xz / GetDomainSize(idx), idx), 0).xyz; } return finalData * KWS_WavesAreaScale; } float3 GetFftWavesDisplacementWithAttenuation(float3 worldPos, float attenuation) { float distanceToCamera = GetWorldToCameraDistance(worldPos); float3 finalData = 0; worldPos += KWS_WaterWorldPosOffset; UNITY_LOOP for (int idx = KWS_WavesCascades - 1; idx > 0; idx--) { float fade = GetFftFade(distanceToCamera, idx); if (fade < 0.01) continue; float windAttenuation = lerp(attenuation * saturate(1.2 - 0.2 * idx), 1, attenuation); finalData += windAttenuation * fade * KWS_FftWavesDisplace.SampleLevel(sampler_linear_repeat, float3(worldPos.xz / GetDomainSize(idx), idx), 0).xyz; } return finalData * KWS_WavesAreaScale; } float3 GetFftWavesNormalLod(float3 worldPos, float lod) { float distanceToCamera = GetWorldToCameraDistance(worldPos); float3 finalData = float3(0, 1, 0); worldPos += KWS_WaterWorldPosOffset; UNITY_LOOP for (int idx = KWS_WavesCascades - 1; idx > 0; idx--) { float fade = GetFftFade(distanceToCamera, idx, 0.25); if (fade < 0.01) continue; float3 data = fade * KWS_FftWavesNormal.SampleLevel(sampler_trilinear_repeat, float3(worldPos.xz / GetDomainSize(idx), idx), lod).xyz; data.y = 1; finalData = KWS_BlendNormals(finalData, data); } return float3(finalData.x, 1, finalData.z); } float3 GetFftWavesNormalFoam(float3 worldPos, float attenuation) { float distanceToCamera = GetWorldToCameraDistance(worldPos); float3 finalData = float3(0, 1, 0); float foam = 0; int idx = KWS_WavesCascades; worldPos += KWS_WaterWorldPosOffset; float2 noise1 = float2(SimpleNoise1(worldPos.xz * 0.005 + KWS_ScaledTime * 0.15), SimpleNoise1(worldPos.xz * 0.005 - (KWS_ScaledTime * 0.15 + 40))); float2 noise2 = float2(SimpleNoise1(worldPos.xz * 0.026 + KWS_ScaledTime * 0.06), SimpleNoise1(worldPos.xz * 0.024 - (KWS_ScaledTime * 0.07 + 40))); float animNoise1 = saturate(0.05 + KWS_Pow10(1 - saturate(max(noise1.x, noise1.y) * 0.2))); float animNoise2 = saturate(0.1 + KWS_Pow5(1 - saturate(max(noise2.x, noise2.y) * 0.75))); UNITY_UNROLL for (int i = 0; i <= MAX_FFT_WAVES_MAX_CASCADES; i++) { idx--; if (idx < 0) break; float fade = GetFftFade(distanceToCamera, idx, 0.25); float windAttenuation = lerp(attenuation * saturate(1.2 - 0.2 * idx), 1, attenuation); fade *= windAttenuation; //if (fade < 0.01) continue; float3 data = float3(fade, animNoise1 * animNoise2, fade); float3 normal = 0; if (idx == 0 || idx == 3) normal = Texture2DArraySampleBicubic(KWS_FftWavesNormal, sampler_linear_repeat, worldPos.xz / GetDomainSize(idx), KWS_FftWavesNormal_TexelSize, idx).xyz; else normal = KWS_FftWavesNormal.Sample(sampler_KWS_FftWavesNormal, float3(worldPos.xz / GetDomainSize(idx), idx)).xyz; data *= normal; foam += data.y; data.y = 1; finalData = KWS_BlendNormals(finalData, data); } //return finalData; return float3(finalData.x, foam, finalData.z); } float3 GetFftWavesNormalDomain(float3 worldPos, uint idx) { return KWS_FftWavesNormal.Sample(sampler_KWS_FftWavesNormal, float3(worldPos.xz / GetDomainSize(idx), idx)).xyz; } float GetFftWavesHeight(float3 worldPos, uint iterations) { float3 invertedDisplacedPosition = worldPos; for (uint i = 0; i < iterations; i++) { float3 displacement = GetFftWavesDisplacementBuoyancy(invertedDisplacedPosition); float3 error = (invertedDisplacedPosition + displacement) - worldPos; invertedDisplacedPosition -= error; } float3 disp = GetFftWavesDisplacement(invertedDisplacedPosition); return disp.y; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// PrePass (Mask/Normal/SSS/VolumeMask/Depth) ////////////////////////////////////////////// //#define WATER_MASK_PASS_UNDERWATER_THRESHOLD 0.35 #define WATER_VOLUME_PRE_PASS_MAX_VALUE 1000000 #define WATER_LINE_HEIGHT_ENCODE_VALUE 10000 DECLARE_TEXTURE(KWS_WaterPrePassRT0); DECLARE_TEXTURE(KWS_WaterPrePassRT1); DECLARE_TEXTURE(KWS_WaterDepthRT); DECLARE_TEXTURE(KWS_WaterIntersectionHalfLineTensionMaskRT); float4 KWS_WaterPrePassRT0_TexelSize; float4 KWS_WaterPrePass_RTHandleScale; DECLARE_TEXTURE(KWS_WaterBackfacePrePassRT0); DECLARE_TEXTURE(KWS_WaterBackfacePrePassRT1); DECLARE_TEXTURE(KWS_WaterBackfaceDepthRT); float4 KWS_WaterBackfacePrePassRT0_TexelSize; float4 KWS_WaterBackfacePrePass_RTHandleScale; inline float2 GetWaterPrePassUV(float2 uv) { uv = GetRTHandleUV(uv, KWS_WaterPrePassRT0_TexelSize.xy, 1.0, KWS_WaterPrePass_RTHandleScale.xy); return uv; } inline float2 GetWaterBackfacePrePassUV(float2 uv) { uv = GetRTHandleUV(uv, KWS_WaterBackfacePrePassRT0_TexelSize.xy, 1.0, KWS_WaterBackfacePrePass_RTHandleScale.xy); return uv; } inline float GetWaterSSS(float2 uv) { return SAMPLE_TEXTURE_LOD(KWS_WaterPrePassRT0, sampler_linear_clamp, GetWaterPrePassUV(uv), 0).z; } inline float3 GetWaterNormals(float2 uv) { float2 rawNormal = SAMPLE_TEXTURE_LOD(KWS_WaterPrePassRT1, sampler_linear_clamp, GetWaterPrePassUV(uv), 0).xy; #ifdef KWS_USE_AQUARIUM_RENDERING float2 rawNormalBackface = SAMPLE_TEXTURE_LOD(KWS_WaterBackfacePrePassRT1, sampler_point_clamp, GetWaterBackfacePrePassUV(uv), 0).xy; rawNormal += rawNormalBackface; #endif return float3(rawNormal.x, 1, rawNormal.y); } inline float GetWaterAquariumBackfaceMask(float2 uv) { return SAMPLE_TEXTURE_LOD(KWS_WaterBackfacePrePassRT0, sampler_point_clamp, GetWaterBackfacePrePassUV(uv), 0).y; } //inside = 1, surface outside = 0.5, box fringe = 0.1 inline float GetWaterMaskFast(float2 uv, float2 offset = float2(0, 0)) { return SAMPLE_TEXTURE_LOD(KWS_WaterPrePassRT0, sampler_linear_clamp, GetWaterPrePassUV(uv), 0).y; } //inside = 1, surface outside = 0.5, box fringe = 0.1 inline float GetWaterMask(float2 uv, float2 offset = float2(0, 0)) { #ifdef KWS_SHARED_API_INCLUDED return GetWaterMaskFast(uv); #else float4 mask = SAMPLE_TEXTURE_GATHER_GREEN(KWS_WaterPrePassRT0, sampler_linear_clamp, GetWaterPrePassUV(uv) + KWS_WaterPrePassRT0_TexelSize.xy * offset); return max(mask.x, max(mask.y, max(mask.z, mask.w))); #endif //float mask = SAMPLE_TEXTURE_LOD(KWS_WaterPrePassRT0, sampler_point_clamp, GetWaterPrePassUV(uv), 0).y; //float center = SAMPLE_TEXTURE_LOD(KW_WaterMaskScatterNormals, sampler_point_clamp, GetWaterPrePassUV(uv), 0).x; //float up = SAMPLE_TEXTURE_LOD_OFFSET(KW_WaterMaskScatterNormals, sampler_point_clamp, GetWaterPrePassUV(uv), 0, int2(0, 1)).x; //float down = SAMPLE_TEXTURE_LOD_OFFSET(KW_WaterMaskScatterNormals, sampler_point_clamp, GetWaterPrePassUV(uv), 0, int2(0, -1)).x; //float diff = (up + down) * 0.5 - center; //if((center == 0.0 || center == 1.0) && down == up) return down; //return mask; } inline float GetUnderwaterMask(float waterMask) { return waterMask > 0.5; } inline bool GetSurfaceMask(float waterMask) { return abs(waterMask - 0.25) < 0.01; } inline bool GetUnderwaterSurfaceMask(float waterMask) { return abs(waterMask - 0.75) < 0.01; } inline float GetWaterHalfLineTensionMask(float2 uv) { float intersectionMask = 0; float aquariumMask = 0; float2 scaledUV = GetWaterPrePassUV(uv); #ifdef KWS_CAMERA_UNDERWATER intersectionMask = SAMPLE_TEXTURE_LOD(KWS_WaterIntersectionHalfLineTensionMaskRT, sampler_linear_clamp, scaledUV, 0).x; intersectionMask *= 1.4; if (intersectionMask >= 0.99) intersectionMask = saturate((1.2 - intersectionMask) * 5); #endif #ifdef KWS_USE_AQUARIUM_RENDERING aquariumMask = SAMPLE_TEXTURE_LOD(KWS_WaterPrePassRT0, sampler_linear_clamp, scaledUV, 0).w; intersectionMask = max(intersectionMask, aquariumMask); #endif return intersectionMask; } inline float GetWaterDepth(float2 uv) { return SAMPLE_TEXTURE_LOD(KWS_WaterDepthRT, sampler_point_clamp, GetWaterPrePassUV(uv), 0).x; } inline float GetWaterBackfaceDepth(float2 uv) { return SAMPLE_TEXTURE_LOD(KWS_WaterBackfaceDepthRT, sampler_point_clamp, GetWaterBackfacePrePassUV(uv), 0).x; } inline uint GetWaterLocalZonesTransparent(float2 uv) { return SAMPLE_TEXTURE_LOD(KWS_WaterPrePassRT0, sampler_point_clamp, GetWaterPrePassUV(uv), 0).x * 100.0; } //Front depth (x), Back depth (y) inline float2 GetWaterVolumeDepth(float2 uv, float surfaceZ, float sceneZ, float waterMask) { float2 volumeDepth = 0; volumeDepth.x = surfaceZ; volumeDepth.y = sceneZ; #if KWS_USE_AQUARIUM_RENDERING volumeDepth.y = max(volumeDepth.y, GetWaterBackfaceDepth(uv)); #endif bool underwaterMask = GetUnderwaterMask(waterMask); volumeDepth.y = lerp(volumeDepth.y, max(volumeDepth.x, sceneZ), underwaterMask); volumeDepth.x = lerp(volumeDepth.x, 1, underwaterMask); if (volumeDepth.x < sceneZ) volumeDepth.x = 0; return volumeDepth; } //Front depth (x), Back depth (y) inline float2 GetWaterVolumeDepth(float2 uv, float sceneZ, float waterMask) { float2 volumeDepth = 0; volumeDepth.x = GetWaterDepth(uv).x; volumeDepth.y = sceneZ; #if KWS_USE_AQUARIUM_RENDERING volumeDepth.y = max(volumeDepth.y, GetWaterBackfaceDepth(uv)); #endif bool underwaterMask = GetUnderwaterMask(waterMask); volumeDepth.y = lerp(volumeDepth.y, max(volumeDepth.x, sceneZ), underwaterMask); volumeDepth.x = lerp(volumeDepth.x, 1, underwaterMask); if (volumeDepth.x < sceneZ) volumeDepth.x = 0; return volumeDepth; } inline float GetBoxExtrude(float3 pos, float3x3 rotationMatrix, float3 size) { float3 rotatedPos = mul(rotationMatrix, pos).xyz; float3 d = abs(rotatedPos) - size; return length(max(d, 0)) + KWS_MAX(min(d, 0)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// VolumetricLighting_Pass ////////////////////////////////////////////// DECLARE_TEXTURE(KWS_VolumetricLightRT); DECLARE_TEXTURE(KWS_VolumetricLightAdditionalDataRT); DECLARE_TEXTURE(KWS_VolumetricLightSurfaceRT); float4 KWS_VolumetricLightRT_TexelSize; float4 KWS_VolumetricLight_RTHandleScale; DECLARE_TEXTURE(KWS_VolumetricLightRT_Last); float4 KWS_VolumetricLightRT_Last_TexelSize; float4 KWS_VolumetricLightRT_Last_RTHandleScale; struct VolumetricLightAdditionalData { half SurfaceDirShadow; half SceneDirShadow; half AdditionalLightsAttenuation; }; inline float GetMaxRayDistanceRelativeToTransparent(float transparent) { return min(KWS_MAX_TRANSPARENT, transparent * 1.5); } inline half4 GetVolumetricLight(float2 uv) { return SAMPLE_TEXTURE_LOD(KWS_VolumetricLightRT, sampler_linear_clamp, saturate(uv), 0); } //(R) surface dir shadow, (G) scene dir shadow, (B) additional lights attenuation inline VolumetricLightAdditionalData GetVolumetricLightAdditionalData(float2 uv) { float3 rawData = SAMPLE_TEXTURE_LOD(KWS_VolumetricLightAdditionalDataRT, sampler_linear_clamp, saturate(uv), 0).xyz; #if defined(KWS_USE_DYNAMIC_WAVES) float4 shadowFix = SAMPLE_TEXTURE_GATHER(KWS_VolumetricLightAdditionalDataRT, sampler_linear_clamp, saturate(uv)); rawData.x = rawData.x * shadowFix.x * shadowFix.y * shadowFix.z * shadowFix.w; #endif VolumetricLightAdditionalData volumeData; volumeData.SurfaceDirShadow = rawData.x; volumeData.SceneDirShadow = rawData.y; volumeData.AdditionalLightsAttenuation = rawData.z; return volumeData; } inline float3 GetVolumetricSurfaceLight(float2 uv) { float3 surfaceLight = SAMPLE_TEXTURE_LOD(KWS_VolumetricLightSurfaceRT, sampler_linear_clamp, saturate(uv), 0).xyz; if(surfaceLight.r < 0.00001) return GetAmbientColor(GetExposure()); else return surfaceLight; } inline float GetVolumeLightInDepthTransmitance(float waterHeight, float currentHeight, float transparent) { //at far distance currentHeight has some float precission error (water depth -> world pos) and random lines. In this case I can just add small heigh offset relative to water surface <---> camera //float distanceToCamera = saturate((_WorldSpaceCameraPos.y - waterHeight) * 0.01) * 10; float distanceToWaterSurface = waterHeight - _WorldSpaceCameraPos.y - 1; float lightInDepthTransmitance = saturate(exp2(-0.5 * distanceToWaterSurface / max(2, transparent)) * 1.1 - 0.1); return lightInDepthTransmitance; } inline float GetVolumeLightSunAngleAttenuation(float3 lightDir, float3 normal = float3(0, 1, 0)) { float dotVal = dot(lightDir, normal); return smoothstep(-0.25, 1.0, dotVal); //return saturate(dot(lightDir, normal)); } inline half3 GetVolumetricSurfaceLight(float4 volumeScattering, float3 normal, float exposure) { float3 ambient = GetAmbientColor(exposure); float3 dirLight = GetVolumeLightSunAngleAttenuation(GetMainLightDir(), normal) * GetMainLightColor(exposure); return (ambient + dirLight * volumeScattering.a + volumeScattering.rgb); } float4 ComputeAbsorbtion(float transparent, float3 dyeColor, float rayLength) { float dyeOverrideFactor = dot(dyeColor, 0.33); float3 absorbtionColor = lerp(1 - dyeColor, float3(1.0, 0.12, 0.02), dyeOverrideFactor); float absorbtionMultiplier = lerp(0.2, 0.1, saturate((transparent - KWS_MAX_TRANSPARENT) / KWS_MAX_TRANSPARENT)); float3 finalAbsorbtion = max(float3(0.0005, 0.001, 0.025), exp2(-KWS_VOLUME_LIGHT_ABSORBTION_FACTOR * pow(rayLength, 1.5) * absorbtionColor * absorbtionMultiplier)); rayLength = min(rayLength, min(KWS_MAX_TRANSPARENT, transparent)); float extintionIntegralApproximationCoeff = KWS_Pow2(transparent * 0.2); float extinction = 1 - saturate(exp2(-rayLength / extintionIntegralApproximationCoeff + KWS_VOLUME_LIGHT_TRANSMITANCE_NEAR_OFFSET_FACTOR)); return saturate(float4(finalAbsorbtion, extinction)); } inline half4 GetVolumetricLightWithAbsorbtion(float2 uv, float2 refractionUV, float transparent, float3 tubidityColor, float3 dyeColor, float3 sceneColor, float2 volumeDepth, float exposure, float3 sceneColorAdditiveAlbedo) { float3 frontPos = GetWorldSpacePositionFromDepth(refractionUV, volumeDepth.x); float3 backPos = GetWorldSpacePositionFromDepth(refractionUV, volumeDepth.y); float rayLength = length(backPos - frontPos); if (volumeDepth.x == 0) rayLength = 0.1; float4 absorbtion = ComputeAbsorbtion(transparent, dyeColor, rayLength); float waterSurfaceShadow = 1; half sceneAttenuation = GetVolumeLightInDepthTransmitance(KWS_WaterPosition.y, backPos.y, transparent); #if KWS_USE_VOLUMETRIC_LIGHT float4 scattering = GetVolumetricLight(refractionUV); //float4 scatteringWithOffset = GetVolumetricLight(uv); //if ((scattering.x + scattering.y + scattering.z) <= 0.1) scattering = scatteringWithOffset; VolumetricLightAdditionalData volumeLightData = GetVolumetricLightAdditionalData(uv); sceneAttenuation = max(sceneAttenuation, volumeLightData.AdditionalLightsAttenuation); waterSurfaceShadow = volumeLightData.SurfaceDirShadow; //absorbtion.a = scattering.a; #else float phaseG = GetVolumeLightSunAngleAttenuation(GetMainLightDir()); float3 sceneLight = 0.5 * GetAmbientColor(exposure) + GetMainLightColor(exposure) * phaseG; float4 scattering = half4(0.5 * tubidityColor * saturate(sceneLight), 1.0); #endif sceneColor *= sceneAttenuation; #ifdef KWS_SHARED_API_INCLUDED float3 albedo = sceneColorAdditiveAlbedo * dot(scattering.xyz, 0.33); float3 finalColor = lerp(absorbtion.rgb * sceneColor + clamp(albedo, 0, 2), scattering.xyz, absorbtion.a); #else float3 finalColor = lerp(absorbtion.rgb * sceneColor, scattering.xyz, absorbtion.a); #endif return float4(finalColor, waterSurfaceShadow); } inline half4 GetVolumetricLightWithAbsorbtionByDistance(float2 uv, float2 refractionUV, float transparent, float3 tubidityColor, float3 dyeColor, float3 sceneColor, float rayLength, float exposure, float3 sceneColorAdditiveAlbedo) { float4 absorbtion = ComputeAbsorbtion(transparent, dyeColor, rayLength); #if KWS_USE_VOLUMETRIC_LIGHT float4 scattering = GetVolumetricLight(refractionUV); //float4 scatteringWithOffset = GetVolumetricLight(uv); //if ((scattering.x + scattering.y + scattering.z) <= 0.1) scattering = scatteringWithOffset; //absorbtion.a = scattering.a; #else float phaseG = GetVolumeLightSunAngleAttenuation(GetMainLightDir()); float3 sceneLight = 0.5 * GetAmbientColor(exposure) + GetMainLightColor(exposure) * phaseG; float4 scattering = half4(0.5 * tubidityColor * saturate(sceneLight), 1.0); #endif #ifdef KWS_SHARED_API_INCLUDED float3 albedo = sceneColorAdditiveAlbedo * dot(scattering.xyz, 0.33); float3 finalColor = lerp(absorbtion.rgb * sceneColor + clamp(albedo, 0, 2), scattering.xyz, absorbtion.a); #else float3 finalColor = lerp(absorbtion.rgb * sceneColor, scattering.xyz, absorbtion.a); #endif return float4(finalColor, absorbtion.a); } inline half3 GetSurfaceLightWithAbsorbtion(float2 uv, float transparent, float3 tubidityColor, float3 dyeColor, float exposure) { float dyeOverrideFactor = dot(dyeColor, 0.33); float3 absorbtionColor = lerp(1 - dyeColor, float3(1.0, 0.13, 0.02), dyeOverrideFactor); float absorbtionMultiplier = lerp(1, 0.0, saturate((transparent) / KWS_MAX_TRANSPARENT)); float extinction = 0; float3 finalAbsorbtion = absorbtionColor; float phaseG = GetVolumeLightSunAngleAttenuation(GetMainLightDir()); float3 sceneLight = 0.5 * GetAmbientColor(exposure) + GetMainLightColor(exposure) * phaseG; float4 scattering = half4(0.5 * tubidityColor * saturate(sceneLight), 1.0); float3 finalColor = lerp(finalAbsorbtion * absorbtionMultiplier, scattering.xyz, 0.5); return finalColor; } inline half3 GetSurfaceLightWithAbsorbtionByDistance(float2 uv, float transparent, float3 tubidityColor, float3 dyeColor, float rayLength, float exposure) { float dyeOverrideFactor = dot(dyeColor, 0.33); float3 absorbtionColor = lerp(1 - dyeColor, float3(1.0, 0.13, 0.02), dyeOverrideFactor); float absorbtionMultiplier = lerp(1, 0.0, saturate((transparent) / KWS_MAX_TRANSPARENT)); float extinction = 1 - saturate(exp2(-rayLength)); float3 finalAbsorbtion = max(float3(0.0005, 0.001, 0.025), exp2(-rayLength * absorbtionColor)); float phaseG = GetVolumeLightSunAngleAttenuation(GetMainLightDir()); float3 sceneLight = 0.5 * GetAmbientColor(exposure) + GetMainLightColor(exposure) * phaseG; float4 scattering = half4(0.5 * tubidityColor * saturate(sceneLight), 1.0); float3 finalColor = lerp(finalAbsorbtion * absorbtionMultiplier, scattering.xyz, extinction); return finalColor; } inline half4 GetVolumetricLightLastFrame(float2 uv) { //float2 scaledUV = GetRTHandleUV(uv, KWS_VolumetricLightRT_TexelSize.xy, 1.0, KWS_VolumetricLight_RTHandleScale.xy); //scaledUV += KWS_VolumetricLightRT_TexelSize.xy * 0.5; return SAMPLE_TEXTURE_LOD(KWS_VolumetricLightRT_Last, sampler_point_clamp, uv, 0); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// ScreenSpaceReflection_Pass ////////////////////////////////////////////// DECLARE_TEXTURE(KWS_ScreenSpaceReflectionRT); float4 KWS_ScreenSpaceReflectionRT_TexelSize; float4 KWS_ScreenSpaceReflection_RTHandleScale; #define KWS_ScreenSpaceReflectionMaxMip 4 inline float2 GetScreenSpaceReflectionNormalizedUV(float2 uv) { uv = GetRTHandleUV(uv, KWS_ScreenSpaceReflectionRT_TexelSize.xy, 0.5, KWS_ScreenSpaceReflection_RTHandleScale.xy); //uv -= KWS_ScreenSpaceReflectionRT_TexelSize.xy * 0.5; return uv; //return clamp(uv, 0.001, 0.999) * KWS_ScreenSpaceReflection_RTHandleScale.xy; } inline half4 GetScreenSpaceReflection(float2 uv, float3 worldPos) { float2 ssrUV = GetScreenSpaceReflectionNormalizedUV(uv); float mipLevel = CalcMipLevel(ssrUV * KWS_ScreenSpaceReflectionRT_TexelSize.zw); float distance = length(worldPos.xz - GetCameraAbsolutePosition().xz); float anisoScaleRelativeToDistance = saturate(distance * 0.05); float lod = min(mipLevel, KWS_ScreenSpaceReflectionMaxMip); //lod = anisoScaleRelativeToDistance * 2; float4 res = SAMPLE_TEXTURE_LOD(KWS_ScreenSpaceReflectionRT, sampler_trilinear_clamp, ssrUV, lod); //res.a = saturate(res.a); //I use negative alpha to minimize edge bilinear interpolation artifacts. return res; } float KWS_ScreenSpaceBordersStretching; inline half4 GetScreenSpaceReflectionWithStretchingMask(float2 uv, float3 worldPos) { #if defined(STEREO_INSTANCING_ON) uv -= mul((float2x2)UNITY_MATRIX_V, float2(0, KWS_ReflectionClipOffset)).xy; #else uv.y -= KWS_ReflectionClipOffset; #endif float AngleStretch = max(0.25, saturate(-KWS_CameraForward.y * 1.5)); float ScreenStretch = saturate(abs(uv.x * 2 - 1) - 0.8); float uvOffset = -AngleStretch * ScreenStretch * KWS_ScreenSpaceBordersStretching * 10; uv.x = uv.x * (1 + uvOffset * 2) - uvOffset; //float stretchingMask = 1 - abs(refl_uv.x * 2 - 1); //refl_uv.x = lerp(refl_uv.x * (1 - Test4.x * 2) + Test4.x, refl_uv.x, AngleStretch * ScreenStretch); return GetScreenSpaceReflection(uv, worldPos); } float GetAnisoScaleRelativeToWind(float windSpeed) { float normalizedWind = saturate((windSpeed - 0.1) / 10.0); return (normalizedWind) * KWS_AnisoReflectionsScale; } float2 GetScreenSpaceReflectionUV(float3 reflDir, float2 orthoUV) { UNITY_BRANCH if (unity_OrthoParams.w == 1.0) return orthoUV; else { reflDir.y = -reflDir.y; float4 projected = mul(UNITY_MATRIX_VP, float4(reflDir, 0)); float2 uv = (projected.xy / projected.w) * 0.5f + 0.5f; #ifdef UNITY_UV_STARTS_AT_TOP uv.y = 1 - uv.y; #endif return uv; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Planar Reflection ////////////////////////////////////////////////////////// DECLARE_TEXTURE(KWS_PlanarReflectionRT); int KWS_PlanarReflectionInstanceID; float4 KWS_PlanarReflectionRT_TexelSize; TextureCube KWS_CubemapReflectionRT; #define KWS_PlanarReflectionMaxMip 4 inline half3 GetPlanarReflection(float2 refl_uv) { float mipLevel = CalcMipLevel(refl_uv * KWS_PlanarReflectionRT_TexelSize.zw); float lod = min(mipLevel, KWS_PlanarReflectionMaxMip); return SAMPLE_TEXTURE_LOD(KWS_PlanarReflectionRT, sampler_trilinear_clamp, refl_uv, lod).xyz; } inline half3 GetPlanarReflectionRaw(float2 refl_uv) { return SAMPLE_TEXTURE_LOD(KWS_PlanarReflectionRT, sampler_trilinear_clamp, refl_uv, 0).xyz; } inline half3 GetPlanarReflectionWithClipOffset(float2 refl_uv) { #if defined(STEREO_INSTANCING_ON) refl_uv -= mul((float2x2)UNITY_MATRIX_V, float2(0, KWS_ReflectionClipOffset)).xy; #else refl_uv.y -= KWS_ReflectionClipOffset; #endif return GetPlanarReflection(refl_uv); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Reflection Effects Pass ////////////////////////////////////////////////////////// float Fresnel_IOR(float3 viewDir, float3 normal, float ior) { float cosi = clamp(-1, 1, dot(viewDir, normal)); float etai = 1, etat = ior; if (cosi > 0) { float temp = etat; etat = etai; etai = temp; } float sint = etai / etat * sqrt(max(0.f, 1 - cosi * cosi)); if (sint >= 1) { return 1; } else { float cost = sqrt(max(0.f, 1 - sint * sint)); cosi = abs(cosi); float Rs = ((etat * cosi) - (etai * cost)) / ((etat * cosi) + (etai * cost)); float Rp = ((etai * cosi) - (etat * cost)) / ((etai * cosi) + (etat * cost)); return (Rs * Rs + Rp * Rp) / 2; } // As a consequence of the conservation of energy, transmittance is given by: // kt = 1 - kr; } inline half SelfSmithJointGGXVisibilityTerm(half NdotL, half NdotV, half roughness) { half a = roughness; half lambdaV = NdotL * (NdotV * (1 - a) + a); half lambdaL = NdotV * (NdotL * (1 - a) + a); return 0.5f / (lambdaV + lambdaL + 1e-5f); } inline half SelfGGXTerm(half NdotH, half roughness) { half a2 = roughness * roughness; half d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad return 0.31830988618f * a2 / (d * d + 1e-7f); // This function is not intended to be running on Mobile, // therefore epsilon is smaller than what can be represented by half } inline half ComputeSpecular(half nl, half nv, half nh, half viewDistNormalized, half smoothness) { half V = SelfSmithJointGGXVisibilityTerm(nl, nv, smoothness); half D = SelfGGXTerm(nh, viewDistNormalized * 0.1 + smoothness); half specularTerm = V * D; specularTerm = max(0, specularTerm * nl * KWS_SunStrength); return specularTerm; } float ComputeWaterFresnel(float3 normal, float3 viewDir, float minValue = 0.05) { float x = 1 - saturate(dot(normal, viewDir)); return minValue + (1 - minValue) * x * x * x * x ; //fresnel aproximation http://wiki.nuaj.net/images/thumb/1/16/Fresnel.jpg/800px-Fresnel.jpg } half3 ComputeSSS(float2 screenUV, float sssMask, half3 underwaterColor, half shadowMask, half transparent) { float3 sssColor = underwaterColor; return sssMask * shadowMask * sssColor * (saturate(transparent * 0.05) + 0.1); } half3 ComputeSunlight(half3 normal, half3 viewDir, float3 lightDir, float3 lightColor, half shadowMask, float viewDist, float waterFarDistance, half transparent) { half3 halfDir = normalize(lightDir + viewDir); half nh = saturate(dot(normal, halfDir)); half nl = saturate(dot(normal, lightDir)); half lh = saturate(dot(lightDir, halfDir)); half fresnel = saturate(dot(normal, viewDir)); float viewDistNormalized = saturate(viewDist / (waterFarDistance * 2)); half3 specular = ComputeSpecular(nl, fresnel, nh, viewDistNormalized, KWS_SunCloudiness); specular = clamp(specular * 10 - 2.5 * saturate(1 - KWS_SunCloudiness * 10), 0, KWS_SunMaxValue); //half sunset = saturate(0.01 + dot(lightDir, float3(0, 1, 0))) * 30; return shadowMask * specular * lightColor; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Caustic_Pass ////////////////////////////////////////////////////////// Texture2DArray KWS_CausticRTArray; float4 KWS_CausticRTArray_TexelSize; float KWS_CaustisDispersionStrength; inline float3 GetCausticSlice(float2 uv, uint causticSlice) { float3 caustic = 1; #ifdef USE_DISPERSION float2 offset = KWS_CausticRTArray_TexelSize.x * KWS_CaustisDispersionStrength; caustic.x = KWS_CausticRTArray.Sample(sampler_linear_repeat, float3(uv - offset, causticSlice)).x; caustic.y = KWS_CausticRTArray.Sample(sampler_linear_repeat, float3(uv, causticSlice)).x; caustic.z = KWS_CausticRTArray.Sample(sampler_linear_repeat, float3(uv + offset, causticSlice)).x; #else caustic = KWS_CausticRTArray.Sample(sampler_linear_repeat, float3(uv, causticSlice)).x; #endif return caustic; } inline half GetCausticLod(float2 uv, uint causticSlice, float lod) { return KWS_CausticRTArray.SampleLevel(sampler_linear_repeat, float3(uv, causticSlice), lod).x; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Flowmap_Pass ///////////////////////////////////////////////////////////////////////////////////////// #define RIVER_FLOW_FRESNEL_MULTIPLIER 0.5 #define RIVER_FLOW_NORMAL_MULTIPLIER 1.5 struct FlowMapData { float2 uvOffset1; float2 uvOffset2; float lerpValue; }; inline FlowMapData GetFlowmapData(float2 direction, float timeScale) { FlowMapData data; /*float time = KWS_ScaledTime * timeScale * 0.5; float2 d = sin(uv * 3.1415); time -= (d.x + d.y) * 0.25 + 0.5; float progressA = frac(time) - 0.5; float progressB = frac(time + 0.5) - 0.5; float2 jump = float2(0.248, 0.201); float2 offsetA = (time - progressA) * jump; float2 offsetB = (time - progressB) * jump + 0.5; data.uvOffset1 = -progressA * direction + offsetA; data.uvOffset2 = -progressB * direction + offsetB; data.lerpValue = saturate(abs(progressA * 2.0));*/ float time = KWS_ScaledTime * timeScale * 0.5; half time1 = frac(time + 0.5); half time2 = frac(time); data.uvOffset1 = -direction * time1; data.uvOffset2 = -direction * time2; data.lerpValue = abs((0.5 - time1) / 0.5); return data; } inline FlowMapData GetFlowmapData(float2 uv, float2 direction, float timeScale) { FlowMapData data; float time = KWS_ScaledTime * timeScale * 0.5; float2 d = sin(uv * 3.1415); time -= (d.x + d.y) * 0.25 + 0.5; float progressA = frac(time) - 0.5; float progressB = frac(time + 0.5) - 0.5; float2 jump = float2(0.248, 0.201); float2 offsetA = (time - progressA) * jump; float2 offsetB = (time - progressB) * jump + 0.5; data.uvOffset1 = -progressA * direction + offsetA; data.uvOffset2 = -progressB * direction + offsetB; data.lerpValue = saturate(abs(progressA * 2.0)); //float time = KWS_ScaledTime * timeScale * 0.5; //half time1 = frac(time + 0.5); //half time2 = frac(time); //data.uvOffset1 = -direction * time1; //data.uvOffset2 = -direction * time2; //data.lerpValue = abs((0.5 - time1) / 0.5); return data; } inline float3 GetFftWavesDisplacementWithFlowMap(float3 worldPos, float2 direction, float timeScale) { FlowMapData data = GetFlowmapData(direction, timeScale); float3 disp1 = GetFftWavesDisplacementDetails(worldPos + data.uvOffset1.xyy); float3 disp2 = GetFftWavesDisplacementDetails(worldPos + data.uvOffset2.xyy); float3 result = lerp(disp1, disp2, data.lerpValue); return result; } inline float3 GetFftWavesNormalFoamWithFlowmap(float3 worldPos, float2 direction, float timeScale, float time) { FlowMapData data = GetFlowmapData(direction, timeScale); float3 result1 = GetFftWavesNormalDomain(worldPos + float3(data.uvOffset1.x, 0, data.uvOffset1.y), 0); float3 result2 = GetFftWavesNormalDomain(worldPos + float3(data.uvOffset2.x, 0, data.uvOffset2.y), 0); float3 normal = lerp(result1, result2, data.lerpValue); normal.y = 1; return normal; /// //time *= timeScale; //float2 noise1 = float2(SimpleNoise1(worldPos.xz * 3 + time * 1.4), SimpleNoise1(worldPos.xz * 3.2 - (time * 1.3 + 40))) * 2 - 1; //float2 noise2 = float2(SimpleNoise1(worldPos.xz * 8 - time * 2), SimpleNoise1(worldPos.xz * 7 + ( time * 2.2 + 40))) * 2 - 1; //noise1 *= 0.01; //noise2 *= 0.007; //float3 result1 = KWS_WaterDynamicWavesFlowMapNormal.Sample(sampler_linear_repeat, (noise1 + noise2 + worldPos.xz + data.uvOffset1)* 0.2).xzy * 2 - 1; //float3 result2 = KWS_WaterDynamicWavesFlowMapNormal.Sample(sampler_linear_repeat, (noise1 + noise2 + worldPos.xz + data.uvOffset2)* 0.2).xzy * 2 - 1; //float3 normal = lerp(result1, result2, data.lerpValue); //normal.y = 1; //normal.xz *= 0.35; //return normal; //return lerp(normal, normal2, Test4.x > 0); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Otrho depth ////////////////////////////////////////////////////////// Texture2D KWS_WaterOrthoDepthRT; Texture2D KWS_WaterOrthoDepthSDF; Texture2D KWS_WaterOrthoDepthBackfaceRT; float3 KWS_OrthoDepthPos; float4 KWS_OrthoDepthNearFarSize; float4x4 KWS_OrthoDepthCameraMatrix; float4 KWS_WaterOrthoDepthRT_TexelSize; float4 KWS_WaterOrthoDepthSDF_TexelSize; float4 ReconstructWaterOrthoDepthPos(float3 worldPos) { return mul(KWS_OrthoDepthCameraMatrix, float4(worldPos, 1)); } float2 GetWaterOrthoDepthUV(float3 worldPos) { return (worldPos.xz - KWS_OrthoDepthPos.xz) / KWS_OrthoDepthNearFarSize.zw + 0.5; } float GetWaterOrthoDepth(float2 uv) { float cameraHeight = KWS_OrthoDepthNearFarSize.x; float far = KWS_OrthoDepthNearFarSize.y; float terrainDepth = KWS_WaterOrthoDepthRT.SampleLevel(sampler_linear_clamp, uv, 0).r; float worldDepth = cameraHeight - (1.0 - terrainDepth) * far; return worldDepth; } //float GetWaterOrthoDepthBicubic(float2 uv) //{ // float cameraHeight = KWS_OrthoDepthNearFarSize.x; // float far = KWS_OrthoDepthNearFarSize.y; // float terrainDepth = KWS_WaterOrthoDepthRT.SampleLevel(sampler_linear_clamp, uv, 0).r; // float worldDepth = cameraHeight - (1.0 - terrainDepth) * far; // return worldDepth; //} float GetWaterOrthoDepthSDF(float2 uv) { //if (IsOutsideUvBorders(uv)) return 0; return KWS_WaterOrthoDepthSDF.SampleLevel(sampler_linear_clamp, uv, 0).x; } float GetWaterOrthoDepth(float3 worldPos) { float2 uv = GetWaterOrthoDepthUV(worldPos); return GetWaterOrthoDepth(uv); } float GetWaterOrthoDepthSDF(float3 worldPos) { float2 uv = GetWaterOrthoDepthUV(worldPos); return GetWaterOrthoDepthSDF(uv); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Local Water Zones ///////////////////////////////////////////////////////////// #define MAX_VISIBLE_LOCAL_ZONES 8 struct LocalZoneData { float3 center; float3 halfSize; float2x2 rotationMatrix; // (c0.x, c0.y, c1.x, c1.y) uint id; float2 uv; float overrideColorSettings; float transparent; float4 waterColor; float4 turbidityColor; float useSphereBlending; float overrideWindSettings; float windStrengthMultiplier; float windEdgeBlending; float overrideHeight; float heightEdgeBlending; float clipWaterBelowZone; }; StructuredBuffer KWS_GlobalTileIndices_LocalZone; StructuredBuffer KWS_TileIndexRanges_LocalZone; StructuredBuffer KWS_ZoneData_LocalZone; float2 _GridSize_LocalZone; float3 _WorldMin_LocalZone; float3 _WorldSize_LocalZone; uint KWS_WaterLocalZonesCount; bool GetTileRange_LocalZone(float3 worldPos, out uint offset, out uint count) { offset = 0; count = 0; float3 localPos = worldPos - _WorldMin_LocalZone.xyz; float2 uvGrid = float2(localPos.x / _WorldSize_LocalZone.x, localPos.z / _WorldSize_LocalZone.z); int x = floor(uvGrid.x * _GridSize_LocalZone.x); int y = floor(uvGrid.y * _GridSize_LocalZone.y); if (x < 0 || x >= _GridSize_LocalZone.x || y < 0 || y >= _GridSize_LocalZone.y) { return false; } uint tileIndex = y * _GridSize_LocalZone.x + x; uint2 range = KWS_TileIndexRanges_LocalZone[tileIndex]; offset = range.x; uint size = range.y; count = offset +size; return true; } bool GetWaterZone_LocalZone(float3 worldPos, uint idx, out LocalZoneData zone) { zone = (LocalZoneData)0; uint zoneID = KWS_GlobalTileIndices_LocalZone[idx]; if (zoneID == -1) return false; zone = KWS_ZoneData_LocalZone[zoneID]; float2 pos = worldPos.xz - zone.center.xz; float2 localPos = mul(pos, zone.rotationMatrix); if (abs(localPos.x) > zone.halfSize.x || abs(localPos.y) > zone.halfSize.z) return false; zone.uv = localPos / zone.halfSize.xz * 0.5 + 0.5; return true; } float GetLocalWaterZoneBlendFactor(float2 uv, float blendFactor) { uv = abs(uv * 2 - 1); float uvEdgeMask = max(uv.x, uv.y); uvEdgeMask = 1 - saturate(pow(uvEdgeMask * 1.01, blendFactor * 10)); return uvEdgeMask; } float GetLocalWaterZoneSphereBlendFactor(float2 uv, float blendFactor) { float2 localUV = uv * 2 - 1; float dist = length(localUV); float mask = 1 - saturate(pow(dist * 1.01, blendFactor * 4)); return mask; } float GetLocalWaterZoneWindBlendFactor(float2 uv, float blendFactor) { uv = abs(uv * 2 - 1); float uvEdgeMask = max(uv.x, uv.y); float fade = saturate(pow(uvEdgeMask * 1.01, blendFactor * 10)); return lerp(1, fade, blendFactor); } void EvaluateBlendedZoneData(inout LocalZoneData blendedZone, float3 rayStart, float3 rayDir, float rayLengthToSurface, float surfaceHeight, float noise) { UNITY_LOOP for (uint zoneIdx = 0; zoneIdx < KWS_WaterLocalZonesCount; zoneIdx++) { LocalZoneData zone = KWS_ZoneData_LocalZone[zoneIdx]; float3 surfaceOffset = float3(0, max(0, zone.center.y + zone.halfSize.y - surfaceHeight) * 0.5f, 0); float tEntry; float2 distanceToBox = abs(mul((rayStart.xz - zone.center.xz), zone.rotationMatrix)) / zone.halfSize.xz; float distanceToBorder = max(distanceToBox.x, distanceToBox.y); float zoneMinHeight = zone.center.y - zone.halfSize.y; if (zone.clipWaterBelowZone && distanceToBorder < 1.1 && rayStart.y < zoneMinHeight && rayStart.y > KWS_WaterPosition.y) { blendedZone.transparent = 0; return; } if (zone.overrideColorSettings > 0.5) { float density = 0; if (zone.useSphereBlending > 0.5) { density = KWS_SDF_SphereDensity(rayStart, rayDir, zone.center, zone.halfSize.x, rayLengthToSurface, tEntry); } else { float2 boxSDF = KWS_SDF_IntersectionBox(rayStart , rayDir, zone.rotationMatrix, zone.center, zone.halfSize); density = boxSDF.x < boxSDF.y && boxSDF.y > 0 && boxSDF.x < rayLengthToSurface; tEntry = boxSDF.x; } if (density > 0) { density = saturate(density * 2); density = lerp(0, density, saturate(blendedZone.transparent / max(1, tEntry))); blendedZone.transparent = lerp(blendedZone.transparent, zone.transparent, density); blendedZone.turbidityColor = lerp(blendedZone.turbidityColor, zone.turbidityColor, density); blendedZone.waterColor = lerp(blendedZone.waterColor, zone.waterColor, density); } } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Dynamic waves ////////////////////////////////////////////////////////// #define MAX_ZONES_PER_TILE 8 Texture2D KWS_DynamicWaves; Texture2D KWS_DynamicWavesNormals; Texture2D KWS_DynamicWavesLast; Texture2D KWS_DynamicWavesAdditionalDataRT; Texture2D KWS_DynamicWavesColorDataRT; Texture2D KWS_DynamicWavesMaskRT; Texture2D KWS_DynamicWavesMaskColorRT; Texture2D KWS_DynamicWavesDepthMask; Texture2D KWS_DynamicWavesMovable; Texture2D KWS_DynamicWavesNormalsMovable; Texture2D KWS_DynamicWavesAdditionalDataRTMovable; Texture2D KWS_DynamicWavesDepthMaskMovable; Texture2D KWS_DynamicWaves0; Texture2D KWS_DynamicWaves1; Texture2D KWS_DynamicWaves2; Texture2D KWS_DynamicWaves3; Texture2D KWS_DynamicWaves4; Texture2D KWS_DynamicWaves5; Texture2D KWS_DynamicWaves6; Texture2D KWS_DynamicWaves7; Texture2D KWS_DynamicWavesNormals0; Texture2D KWS_DynamicWavesNormals1; Texture2D KWS_DynamicWavesNormals2; Texture2D KWS_DynamicWavesNormals3; Texture2D KWS_DynamicWavesNormals4; Texture2D KWS_DynamicWavesNormals5; Texture2D KWS_DynamicWavesNormals6; Texture2D KWS_DynamicWavesNormals7; Texture2D KWS_DynamicWavesAdditionalDataRT0; Texture2D KWS_DynamicWavesAdditionalDataRT1; Texture2D KWS_DynamicWavesAdditionalDataRT2; Texture2D KWS_DynamicWavesAdditionalDataRT3; Texture2D KWS_DynamicWavesAdditionalDataRT4; Texture2D KWS_DynamicWavesAdditionalDataRT5; Texture2D KWS_DynamicWavesAdditionalDataRT6; Texture2D KWS_DynamicWavesAdditionalDataRT7; Texture2D KWS_DynamicWavesColorDataRT0; Texture2D KWS_DynamicWavesColorDataRT1; Texture2D KWS_DynamicWavesColorDataRT2; Texture2D KWS_DynamicWavesColorDataRT3; Texture2D KWS_DynamicWavesColorDataRT4; Texture2D KWS_DynamicWavesColorDataRT5; Texture2D KWS_DynamicWavesColorDataRT6; Texture2D KWS_DynamicWavesColorDataRT7; Texture2D KWS_DynamicWavesDepthMask0; Texture2D KWS_DynamicWavesDepthMask1; Texture2D KWS_DynamicWavesDepthMask2; Texture2D KWS_DynamicWavesDepthMask3; Texture2D KWS_DynamicWavesDepthMask4; Texture2D KWS_DynamicWavesDepthMask5; Texture2D KWS_DynamicWavesDepthMask6; Texture2D KWS_DynamicWavesDepthMask7; float4 KWS_DynamicWaves0_TexelSize; float4 KWS_DynamicWaves1_TexelSize; float4 KWS_DynamicWaves2_TexelSize; float4 KWS_DynamicWaves3_TexelSize; float4 KWS_DynamicWaves4_TexelSize; float4 KWS_DynamicWaves5_TexelSize; float4 KWS_DynamicWaves6_TexelSize; float4 KWS_DynamicWaves7_TexelSize; float4 KWS_DynamicWaves_TexelSize; float4 KWS_DynamicWavesDepthMask_TexelSize; float4 KWS_DynamicWavesAdditionalDataRT_TexelSize; float KWS_DynamicWavesTimeScale; float3 KWS_DynamicWavesZonePosition; float3 KWS_DynamicWavesZoneSize; float4 KWS_DynamicWavesZoneRotationMatrix; float3 KWS_DynamicWavesZonePositionMovable; float3 KWS_DynamicWavesZoneSizeMovable; float4 KWS_DynamicWavesZoneBoundsMovable; float4 KWS_DynamicWavesOrthoDepthNearFarSizeMovable; float3 KWS_DynamicWavesZonePositionArray[MAX_ZONES_PER_TILE]; float3 KWS_DynamicWavesZoneSizeArray[MAX_ZONES_PER_TILE]; float4 KWS_DynamicWavesOrthoDepthNearFarSizeArray[MAX_ZONES_PER_TILE]; float4 KWS_DynamicWavesZoneRotationMatrixArray[MAX_ZONES_PER_TILE]; #define DYNAMIC_WAVE_COLOR_MAX_TRANSPARENT 4 #define DYNAMIC_WAVE_PROCEDURAL_MASK_TYPE_SPHERE 1 #define KWS_DYNAMIC_WAVE_PARTICLE_TYPE_FOAM 0 #define KWS_DYNAMIC_WAVE_PARTICLE_TYPE_SPLASH 1 #define KWS_DYNAMIC_WAVE_PARTICLE_TYPE_SPLASH_SURFACE 2 struct KWS_DynamicWavesMask { uint zoneInteractionType; float force; float waterHeight; uint useColor; float4 size; float4 position; float3 forceDirection; uint useWaterIntersection; float4 color; float4x4 matrixTRS; }; StructuredBuffer KWS_DynamicWavesMaskBuffer; struct FoamParticle { float3 position; float initialRandom01; float3 prevPosition; float prevLifetime; float3 velocity; float currentLifetime; float4 color; float4 prevColor; float isFreeMoving; float shorelineMask; float maxLifeTime; float _pad; }; struct SplashParticle { float initialRandom01; float3 position; float3 velocity; float currentLifetime; float shorelineMask; float distanceToSurface; float uvOffset; float initialSpeed; float3 prevPosition; float prevLifetime; }; StructuredBuffer KWS_SplashParticlesBuffer; StructuredBuffer KWS_FoamParticlesBuffer; struct ZoneData { float3 center; float3 halfSize; float2x2 rotationMatrix; // (c0.x, c0.y, c1.x, c1.y) uint id; float2 uv; float flowSpeedMultiplier; }; StructuredBuffer KWS_GlobalTileIndices; StructuredBuffer KWS_TileIndexRanges; StructuredBuffer KWS_ZoneData; float2 _GridSize; float3 _WorldMin; float3 _WorldSize; uint KWS_WaterDynamicWavesZonesCount; float KWS_MovableZoneFlowSpeedMultiplier; bool GetTileRange(float3 worldPos, out uint offset, out uint count) { offset = 0; count = 0; float3 localPos = worldPos - _WorldMin.xyz; float2 uvGrid = float2(localPos.x / _WorldSize.x, localPos.z / _WorldSize.z); int x = floor(uvGrid.x * _GridSize.x); int y = floor(uvGrid.y * _GridSize.y); //if (x < 0 || x >= _GridSize.x || y < 0 || y >= _GridSize.y) //{ // return false; //} uint tileIndex = y * _GridSize.x + x; uint2 range = KWS_TileIndexRanges[tileIndex]; offset = range.x; uint size = range.y; count = offset +size; return true; } bool GetWaterZone(float3 worldPos, uint idx, out ZoneData zone) { zone = (ZoneData)0; uint zoneID = KWS_GlobalTileIndices[idx]; if (zoneID == -1) return false; zone = KWS_ZoneData[zoneID]; float2 pos = worldPos.xz - zone.center.xz; //float2x2 invRot = float2x2( // zone.rotMatrix.x, zone.rotMatrix.y, // zone.rotMatrix.z, zone.rotMatrix.w //); float2 localPos = mul(pos, zone.rotationMatrix); if (abs(localPos.x) > zone.halfSize.x || abs(localPos.y) > zone.halfSize.z) return false; zone.uv = localPos / zone.halfSize.xz * 0.5 + 0.5; return true; } inline float2 RotateDynamicWavesCoord(float2 coord) { return float2(dot(coord, KWS_DynamicWavesZoneRotationMatrix.xy), dot(coord, KWS_DynamicWavesZoneRotationMatrix.zw)); } inline float2 RotateDynamicWavesCoord(uint id, float2 coord) { float4 mat = KWS_DynamicWavesZoneRotationMatrixArray[id]; float2x2 invRot = float2x2(mat.x, mat.z, mat.y, mat.w); float2 rotated = mul(coord, invRot); return rotated; } inline float2 RotateDynamicWavesCoordInverse(float2 coord) { return float2(dot(coord, KWS_DynamicWavesZoneRotationMatrix.xz), dot(coord, KWS_DynamicWavesZoneRotationMatrix.yw)); } inline float2 GetDynamicWavesUV(float3 worldPos) { return (worldPos.xz - KWS_DynamicWavesZonePosition.xz) / KWS_DynamicWavesZoneSize.xz + 0.5; } inline float2 GetDynamicWavesUVRotated(float3 worldPos) { float2 local = worldPos.xz - KWS_DynamicWavesZonePosition.xz; float2x2 invRot = float2x2( KWS_DynamicWavesZoneRotationMatrix.x, KWS_DynamicWavesZoneRotationMatrix.z, KWS_DynamicWavesZoneRotationMatrix.y, KWS_DynamicWavesZoneRotationMatrix.w ); float2 rotated = mul(invRot, local); return rotated / KWS_DynamicWavesZoneSize.xz + 0.5; } bool GetWaterZoneMovable(float3 worldPos, out float2 uv) { uv = 0; float4 zoneBounds = KWS_DynamicWavesZoneBoundsMovable; if (worldPos.x <= zoneBounds.x || worldPos.x >= zoneBounds.z || worldPos.z <= zoneBounds.y || worldPos.z >= zoneBounds.w) { return false; } uv.x = (worldPos.x - zoneBounds.x) / (zoneBounds.z - zoneBounds.x); uv.y = (worldPos.z - zoneBounds.y) / (zoneBounds.w - zoneBounds.y); return true; } float GetWaterToZoneHeight(uint id) { return abs(KWS_DynamicWavesZonePositionArray[id].y + KWS_DynamicWavesZoneSizeArray[id].y * 0.5 - KWS_WaterPosition.y); } float GetWaterToZoneHeight() { return abs(KWS_DynamicWavesZonePosition.y + KWS_DynamicWavesZoneSize.y * 0.5 - KWS_WaterPosition.y); } float GetWaterToZoneHeightMovable() { return abs(KWS_DynamicWavesZonePositionMovable.y + KWS_DynamicWavesZoneSizeMovable.y * 0.5 - KWS_WaterPosition.y); } float EncodeDynamicWavesHeight(float worldHeight) { float minY = KWS_DynamicWavesZonePosition.y - KWS_DynamicWavesZoneSize.y; float maxY = KWS_DynamicWavesZonePosition.y + KWS_DynamicWavesZoneSize.y; return saturate((worldHeight - minY) / (maxY - minY)); } float DecodeDynamicWavesHeight(uint id, float worldHeight) { float zoneCenter = KWS_DynamicWavesZonePositionArray[id].y; float zoneSize = KWS_DynamicWavesZoneSizeArray[id].y; return lerp(zoneCenter - zoneSize, zoneCenter + zoneSize, worldHeight); } inline float4 GetDynamicWavesMask(float2 uv) { float threshold = 4.0 / 255.0; float4 data = KWS_DynamicWavesMaskRT.SampleLevel(sampler_linear_clamp, uv, 0); data.xyz = data.xyz * 2 - 1; data.xyz *= step(threshold, abs(data.xyz)); return data; } float4 PackSimulation(float4 value) { value.xy = clamp(value.xy, -10, 10) / 20.0 + 0.5; value.z = (clamp(value.z, -2, 20) + 2) / 22.0; value.w = value.w / GetWaterToZoneHeight(); return value; } float4 UnpackSimulation(float4 value) { value.xy = (value.xy - 0.5) * 20.0; value.z = value.z * 22.0 - 2.0; value.w = value.w * GetWaterToZoneHeight(); return value; } float4 UnpackSimulationMovable(float4 value) { value.xy = (value.xy - 0.5) * 20.0; value.z = value.z * 22.0 - 2.0; value.w = value.w * GetWaterToZoneHeightMovable(); return value; } float4 PackSimulation(uint id, float4 value) { value.xy = clamp(value.xy, -10, 10) / 20.0 + 0.5; value.z = (clamp(value.z, -2, 20) + 2) / 22.0; value.w = value.w / GetWaterToZoneHeight(id); return value; } float4 UnpackSimulation(uint id, float4 value) { value.xy = (value.xy - 0.5) * 20.0; value.z = value.z * 22.0 - 2.0; value.w = value.w * GetWaterToZoneHeight(id); return value; } inline float4 GetDynamicWavesMaskColor(float2 uv) { return KWS_DynamicWavesMaskColorRT.SampleLevel(sampler_linear_clamp, uv, 0); } inline float4 GetDynamicWavesZone(float2 uv) { float4 data = UnpackSimulation(KWS_DynamicWaves.SampleLevel(sampler_linear_clamp, uv, 0)); data.xy = RotateDynamicWavesCoord(data.xy); return data; } inline float4 GetDynamicWavesZoneMovable(float2 uv) { return UnpackSimulationMovable(KWS_DynamicWavesMovable.SampleLevel(sampler_linear_clamp, uv, 0)); } inline float4 GetDynamicWavesZone(uint id, float2 uv) { #if defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_2) float4 rawData = SampleTextureArray2(KWS_DynamicWaves0, KWS_DynamicWaves1, id, uv); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_4) float4 rawData = SampleTextureArray4(KWS_DynamicWaves0, KWS_DynamicWaves1, KWS_DynamicWaves2, KWS_DynamicWaves3, id, uv); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_8) float4 rawData = SampleTextureArray8(KWS_DynamicWaves0, KWS_DynamicWaves1, KWS_DynamicWaves2, KWS_DynamicWaves3, KWS_DynamicWaves4, KWS_DynamicWaves5, KWS_DynamicWaves6, KWS_DynamicWaves7, id, uv); #else float4 rawData = KWS_DynamicWaves0.SampleLevel(sampler_linear_clamp, uv, 0); #endif rawData = UnpackSimulation(id, rawData); rawData.xy = RotateDynamicWavesCoord(id, rawData.xy); return rawData; } inline float4 GetDynamicWavesZoneBicubic(float2 uv) { float4 data = UnpackSimulation(Texture2DSampleLevelBicubic(KWS_DynamicWaves, sampler_linear_clamp, uv, KWS_DynamicWaves_TexelSize, 0)); data.xy = RotateDynamicWavesCoord(data.xy); return data; } inline float4 GetDynamicWavesZoneBicubic(uint id, float2 uv) { #if defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_2) float4 rawData = SampleTextureArray2_FirstBicubic(KWS_DynamicWaves0, KWS_DynamicWaves1, id, uv, KWS_DynamicWaves0_TexelSize); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_4) float4 rawData = SampleTextureArray4_FirstBicubic(KWS_DynamicWaves0, KWS_DynamicWaves1, KWS_DynamicWaves2, KWS_DynamicWaves3, id, uv, KWS_DynamicWaves0_TexelSize); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_8) float4 rawData = SampleTextureArray8_FirstBicubic(KWS_DynamicWaves0, KWS_DynamicWaves1, KWS_DynamicWaves2, KWS_DynamicWaves3, KWS_DynamicWaves4, KWS_DynamicWaves5, KWS_DynamicWaves6, KWS_DynamicWaves7, id, uv, KWS_DynamicWaves0_TexelSize); #else float4 rawData = Texture2DSampleLevelBicubic(KWS_DynamicWaves0, sampler_linear_clamp, uv, KWS_DynamicWaves0_TexelSize, 0); #endif rawData = UnpackSimulation(id, rawData); rawData.xy = RotateDynamicWavesCoord(id, rawData.xy); return rawData; } inline float4 GetDynamicWavesZoneAdditionalData(float2 uv) { return KWS_DynamicWavesAdditionalDataRT.SampleLevel(sampler_linear_clamp, uv, 0); } inline float4 GetDynamicWavesZoneAdditionalDataMovable(float2 uv) { return KWS_DynamicWavesAdditionalDataRTMovable.SampleLevel(sampler_linear_clamp, uv, 0); } inline float4 GetDynamicWavesZoneAdditionalData(uint id, float2 uv) { #if defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_2) float4 rawData = SampleTextureArray2(KWS_DynamicWavesAdditionalDataRT0, KWS_DynamicWavesAdditionalDataRT1, id, uv); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_4) float4 rawData = SampleTextureArray4(KWS_DynamicWavesAdditionalDataRT0, KWS_DynamicWavesAdditionalDataRT1, KWS_DynamicWavesAdditionalDataRT2, KWS_DynamicWavesAdditionalDataRT3, id, uv); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_8) float4 rawData = SampleTextureArray8(KWS_DynamicWavesAdditionalDataRT0, KWS_DynamicWavesAdditionalDataRT1, KWS_DynamicWavesAdditionalDataRT2, KWS_DynamicWavesAdditionalDataRT3, KWS_DynamicWavesAdditionalDataRT4, KWS_DynamicWavesAdditionalDataRT5, KWS_DynamicWavesAdditionalDataRT6, KWS_DynamicWavesAdditionalDataRT7, id, uv); #else float4 rawData = KWS_DynamicWavesAdditionalDataRT0.SampleLevel(sampler_linear_clamp, uv, 0); #endif return rawData; } inline float4 GetDynamicWavesZoneAdditionalDataBicubic(uint id, float2 uv) { #if defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_2) float4 rawData = SampleTextureArray2_FirstBicubic(KWS_DynamicWavesAdditionalDataRT0, KWS_DynamicWavesAdditionalDataRT1, id, uv, KWS_DynamicWaves0_TexelSize); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_4) float4 rawData = SampleTextureArray4_FirstBicubic(KWS_DynamicWavesAdditionalDataRT0, KWS_DynamicWavesAdditionalDataRT1, KWS_DynamicWavesAdditionalDataRT2, KWS_DynamicWavesAdditionalDataRT3, id, uv, KWS_DynamicWaves0_TexelSize); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_8) float4 rawData = SampleTextureArray8_FirstBicubic(KWS_DynamicWavesAdditionalDataRT0, KWS_DynamicWavesAdditionalDataRT1, KWS_DynamicWavesAdditionalDataRT2, KWS_DynamicWavesAdditionalDataRT3, KWS_DynamicWavesAdditionalDataRT4, KWS_DynamicWavesAdditionalDataRT5, KWS_DynamicWavesAdditionalDataRT6, KWS_DynamicWavesAdditionalDataRT7, id, uv, KWS_DynamicWaves0_TexelSize); #else float4 rawData = Texture2DSampleLevelBicubic(KWS_DynamicWavesAdditionalDataRT0, sampler_linear_clamp, uv, KWS_DynamicWaves0_TexelSize, 0); #endif return rawData; } inline float4 GetDynamicWavesZoneAdditionalDataBicubic(float2 uv) { return Texture2DSampleLevelBicubic(KWS_DynamicWavesAdditionalDataRT, sampler_linear_clamp, uv, KWS_DynamicWavesAdditionalDataRT_TexelSize, 0); } inline float4 GetDynamicWavesZoneColorData(float2 uv) { return KWS_DynamicWavesColorDataRT.SampleLevel(sampler_linear_clamp, uv, 0); } inline float4 GetDynamicWavesZoneColorData(uint id, float2 uv) { #if defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_2) float4 rawData = SampleTextureArray2(KWS_DynamicWavesColorDataRT0, KWS_DynamicWavesColorDataRT1, id, uv); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_4) float4 rawData = SampleTextureArray4(KWS_DynamicWavesColorDataRT0, KWS_DynamicWavesColorDataRT1, KWS_DynamicWavesColorDataRT2, KWS_DynamicWavesColorDataRT3, id, uv); #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_8) float4 rawData = SampleTextureArray8(KWS_DynamicWavesColorDataRT0, KWS_DynamicWavesColorDataRT1, KWS_DynamicWavesColorDataRT2, KWS_DynamicWavesColorDataRT3, KWS_DynamicWavesColorDataRT4, KWS_DynamicWavesColorDataRT5, KWS_DynamicWavesColorDataRT6, KWS_DynamicWavesColorDataRT7, id, uv); #else float4 rawData = KWS_DynamicWavesColorDataRT0.SampleLevel(sampler_linear_clamp, uv, 0); #endif return rawData; } inline float3 GetDynamicWavesZoneNormals(float2 uv) { return KWS_DynamicWavesNormals.SampleLevel(sampler_linear_clamp, uv, 0).xyz; } inline float3 GetDynamicWavesZoneNormalsMovable(float2 uv) { return KWS_DynamicWavesNormalsMovable.SampleLevel(sampler_linear_clamp, uv, 0).xyz; } inline float3 GetDynamicWavesZoneNormals(uint id, float2 uv) { #if defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_2) float3 rawData = SampleTextureArray2(KWS_DynamicWavesNormals0, KWS_DynamicWavesNormals1, id, uv).xyz; #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_4) float3 rawData = SampleTextureArray4(KWS_DynamicWavesNormals0, KWS_DynamicWavesNormals1, KWS_DynamicWavesNormals2, KWS_DynamicWavesNormals3, id, uv).xyz; #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_8) float3 rawData = SampleTextureArray8(KWS_DynamicWavesNormals0, KWS_DynamicWavesNormals1, KWS_DynamicWavesNormals2, KWS_DynamicWavesNormals3, KWS_DynamicWavesNormals4, KWS_DynamicWavesNormals5, KWS_DynamicWavesNormals6, KWS_DynamicWavesNormals7, id, uv).xyz; #else float3 rawData = KWS_DynamicWavesNormals0.SampleLevel(sampler_linear_clamp, uv, 0).xyz; #endif return rawData; } inline float3 GetDynamicWavesZoneNormalsBicubic(uint id, float2 uv) { #if defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_2) float3 rawData = SampleTextureArray2_FirstBicubic(KWS_DynamicWavesNormals0, KWS_DynamicWavesNormals1, id, uv, KWS_DynamicWaves0_TexelSize).xyz; #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_4) float3 rawData = SampleTextureArray4_FirstBicubic(KWS_DynamicWavesNormals0, KWS_DynamicWavesNormals1, KWS_DynamicWavesNormals2, KWS_DynamicWavesNormals3, id, uv, KWS_DynamicWaves0_TexelSize).xyz; #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_8) float3 rawData = SampleTextureArray8_FirstBicubic(KWS_DynamicWavesNormals0, KWS_DynamicWavesNormals1, KWS_DynamicWavesNormals2, KWS_DynamicWavesNormals3, KWS_DynamicWavesNormals4, KWS_DynamicWavesNormals5, KWS_DynamicWavesNormals6, KWS_DynamicWavesNormals7, id, uv, KWS_DynamicWaves0_TexelSize).xyz; #else float3 rawData = Texture2DSampleLevelBicubic(KWS_DynamicWavesNormals0, sampler_linear_clamp, uv, KWS_DynamicWaves0_TexelSize, 0).xyz; #endif return rawData; } inline float UnpackDepthMask(float maskDepth) { if (maskDepth > 0) { float cameraHeight = KWS_OrthoDepthNearFarSize.x; float far = KWS_OrthoDepthNearFarSize.y; float near = 0.0001; float worldDepth = cameraHeight - far + near + maskDepth * far; return worldDepth; } else return -100000; } inline float UnpackDepthMaskMovable(float maskDepth) { if (maskDepth > 0) { float cameraHeight = KWS_DynamicWavesOrthoDepthNearFarSizeMovable.x; float far = KWS_DynamicWavesOrthoDepthNearFarSizeMovable.y; float near = 0.0001; float worldDepth = cameraHeight - far + near + maskDepth * far; return worldDepth; } else return -100000; } inline float UnpackDepthMask(float maskDepth, uint id) { if (id >= MAX_ZONES_PER_TILE) return -100000; //fix Shader warning: use of potentially uninitialized variable (UnpackDepthMask) if (maskDepth > 0) { float4 nearFarSize = KWS_DynamicWavesOrthoDepthNearFarSizeArray[id]; float cameraHeight = nearFarSize.x; float far = nearFarSize.y; float near = 0.0001; float worldDepth = cameraHeight - far + near + maskDepth * far; return worldDepth; } else return -100000; } inline float GetDynamicWavesZoneDepthMaskBicubic(float2 uv) { //return 0; float maskDepth = Texture2DSampleLevelBicubic(KWS_DynamicWavesDepthMask, sampler_linear_clamp, uv, KWS_DynamicWavesDepthMask_TexelSize, 0).x; return UnpackDepthMask(maskDepth); } inline float GetDynamicWavesZoneDepthMask(float2 uv) { //return 0; float maskDepth = KWS_DynamicWavesDepthMask.SampleLevel(sampler_linear_clamp, uv, 0).x; return UnpackDepthMask(maskDepth); } inline float GetDynamicWavesZoneDepthMaskMovable(float2 uv) { float maskDepth = KWS_DynamicWavesDepthMaskMovable.SampleLevel(sampler_linear_clamp, uv, 0).x; return UnpackDepthMaskMovable(maskDepth); } inline float GetDynamicWavesZoneDepthMask(uint id, float2 uv) { #if defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_2) float maskDepth = SampleTextureArray2(KWS_DynamicWavesDepthMask0, KWS_DynamicWavesDepthMask1, id, uv).x; #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_4) float maskDepth = SampleTextureArray4(KWS_DynamicWavesDepthMask0, KWS_DynamicWavesDepthMask1, KWS_DynamicWavesDepthMask2, KWS_DynamicWavesDepthMask3, id, uv).x; #elif defined(KWS_DYNAMIC_WAVES_VISIBLE_ZONES_8) float maskDepth = SampleTextureArray8(KWS_DynamicWavesDepthMask0, KWS_DynamicWavesDepthMask1, KWS_DynamicWavesDepthMask2, KWS_DynamicWavesDepthMask3, KWS_DynamicWavesDepthMask4, KWS_DynamicWavesDepthMask5, KWS_DynamicWavesDepthMask6, KWS_DynamicWavesDepthMask7, id, uv).x; #else float maskDepth = KWS_DynamicWavesDepthMask0.SampleLevel(sampler_linear_clamp, uv, 0).x; #endif return UnpackDepthMask(maskDepth); } inline float GetDynamicWavesBorderFading(float2 uv) { uv = abs(uv * 2 - 1); float uvEdgeMask = max(uv.x, uv.y); uvEdgeMask = 1 - saturate(pow(uvEdgeMask * 1.01, 8)); return uvEdgeMask; } inline float2 NormalizeDynamicWavesVelocity(float2 velocity) { float flowStrength = max(length(velocity), 0.001); return (velocity / flowStrength) * saturate(1 - exp(-flowStrength)); } float GetDynamicWavesHeightOffset(float3 worldPos) { float3 disp = GetFftWavesHeight(worldPos, 3); float2 uv = GetDynamicWavesUV(worldPos); float4 dynamicWaves = GetDynamicWavesZone(uv); float zoneFade = GetDynamicWavesBorderFading(uv); float waterLevel = disp.y + dynamicWaves.z * zoneFade + dynamicWaves.w + KWS_WaterPosition.y; return waterLevel; } float GetDynamicWavesHeightOffset(float3 worldPos, float2 dynamicWavesUV, float4 dynamicWaves) { float3 disp = GetFftWavesHeight(worldPos, 3); float zoneFade = GetDynamicWavesBorderFading(dynamicWavesUV); float waterLevel = disp.y + dynamicWaves.z * zoneFade + dynamicWaves.w + KWS_WaterPosition.y; return waterLevel; } float GetDynamicWavesWaterfallTreshold(float3 zoneNormal) { float waterfallThreshold = saturate(2 * saturate(1 - dot(zoneNormal, float3(0, 1, 0))) - 0.05); return waterfallThreshold; } ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Underwater ////////////////////////////////////////////////////////// #define UNDERWATER_DEPTH_SCALE 0.75 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// Refraction ////////////////////////////////////////////////////////// inline void FixRefractionSurfaceLeaking(float surfaceDepthZEye, float sceneZ, float sceneZEye, float2 screenUV, inout float refractedSceneZ, inout float refractedSceneZEye, inout float2 refractionUV) { if (surfaceDepthZEye > refractedSceneZEye) { refractedSceneZ = sceneZ; refractedSceneZEye = sceneZEye; refractionUV = screenUV; } } half3 ComputeWaterRefractRay(half3 viewDir, half3 normal, half depth) { half nv = dot(normal, viewDir); half v2 = dot(viewDir, viewDir); half knormal = (sqrt(((1.7689 - 1.0) * v2) / (nv * nv) + 1.0) - 1.0) * nv; half3 result = depth * (viewDir + (knormal * normal)); return result; } inline float2 GetRefractedUV_IOR(float3 viewDir, half3 normal, float3 worldPos, float sceneZEye, float waterSurfaceZEye, float transparent) { float zFix = saturate(sceneZEye - waterSurfaceZEye); float aproximatedDepth = min(transparent, KWS_RefractionAproximatedDepth * zFix); float3 refractedRay = ComputeWaterRefractRay(-viewDir, normal, aproximatedDepth); refractedRay.y *= 0.4; //fix information lost in the near camera float4 refractedClipPos = mul(UNITY_MATRIX_VP, float4(GetCameraRelativePosition(worldPos + refractedRay), 1.0)); float4 refractionScreenPos = ComputeScreenPos(refractedClipPos); float2 uv = refractionScreenPos.xy / refractionScreenPos.w; /*float2 overflowUV = abs(uv * 2 - 1); float overflowFix = saturate(max(overflowUV.x, overflowUV.y) - 0.6); overflowFix *= overflowFix; overflowFix = lerp(0, overflowFix, zFix); uv = lerp(uv, uv * 0.5 + 0.25, overflowFix);*/ if (uv.x >= 0.995) uv.x = 1.99 - uv.x; if (uv.y >= 0.995) uv.y = 1.99 - uv.y; if (uv.y <= 0) uv.y = -uv.y; if (uv.x <= 0) uv.x = -uv.x; return uv; } inline float2 GetRefractedUV_Simple(float2 uv, half3 normal) { return uv + normal.xz * KWS_RefractionSimpleStrength * 0.5; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// float4 LocalPosToToClipPosOrtho(float3 vertex) { float3 worldPos = GetCameraRelativePositionOrtho(mul(UNITY_MATRIX_M, float4(vertex.xyz, 1.0)).xyz); return mul(KWS_MATRIX_VP_ORTHO, float4(GetCameraAbsolutePosition(worldPos), 1)); } float4 WorldPosToToClipPosOrtho(float3 worldPos) { worldPos = GetCameraRelativePositionOrtho(worldPos); return mul(KWS_MATRIX_VP_ORTHO, float4(worldPos, 1)); } inline half GetSurfaceToSceneFading(float zEye, float surfaceZEye, float multiplier) { return saturate((zEye - surfaceZEye) * multiplier); } inline half GetWaterRawFade(float3 worldPos, float surfaceDepthZEye, float refractedSceneZEye, half surfaceMask, half depthAngleFix) { return (refractedSceneZEye - surfaceDepthZEye) * lerp(depthAngleFix, 1, unity_OrthoParams.w); } ////////////////////////////////////////////// Scene Color Pass ////////////////////////////////////////////////////////// float4 KWS_CameraOpaqueTextureAfterWaterPass_TexelSize; float4 KWS_CameraOpaqueTextureAfterWaterPass_RTHandleScale; DECLARE_TEXTURE(KWS_CameraOpaqueTextureAfterWaterPass); inline float2 GetSceneColorAfterWaterPassNormalizedUV(float2 uv) { float2 maxCoord = 1.0f - KWS_CameraOpaqueTextureAfterWaterPass_TexelSize.xy * 0.5; return min(uv, maxCoord) * KWS_CameraOpaqueTextureAfterWaterPass_RTHandleScale.xy; } inline half3 GetSceneColorAfterWaterPass(float2 uv) { #ifdef KWS_HDRP return GetSceneColor(uv); #else return SAMPLE_TEXTURE_LOD(KWS_CameraOpaqueTextureAfterWaterPass, sampler_linear_clamp, GetSceneColorAfterWaterPassNormalizedUV(uv), 0).xyz; #endif } #endif