1013 lines
35 KiB
HLSL
1013 lines
35 KiB
HLSL
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
|
|
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
|
|
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
|
|
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
|
|
|
|
#define COLLAPSIBLE_GROUP 1
|
|
|
|
//Normalize the amount of normal-based distortion between reflection probes and screen-space reflections
|
|
#define SCREENSPACE_REFLECTION_DISTORTION_MULTIPLIER 0.15
|
|
|
|
struct SceneData
|
|
{
|
|
float4 positionSS; //Unnormalized
|
|
float2 screenPos; //Normalized and no refraction
|
|
float3 positionWS;
|
|
float3 color;
|
|
half3 normalWS;
|
|
|
|
#if defined(SCENE_SHADOWMASK)
|
|
half shadowMask;
|
|
#endif
|
|
|
|
float viewDepth;
|
|
float verticalDepth;
|
|
|
|
#if RESAMPLE_REFRACTION_DEPTH && _REFRACTION
|
|
float viewDepthRefracted;
|
|
float verticalDepthRefracted;
|
|
#endif
|
|
|
|
half skyMask;
|
|
|
|
//More easy debugging
|
|
half refractionMask;
|
|
};
|
|
|
|
void PopulateSceneData(inout SceneData scene, Varyings input, WaterSurface water)
|
|
{
|
|
scene.positionSS = input.screenPos;
|
|
scene.screenPos = scene.positionSS.xy / scene.positionSS.w;
|
|
|
|
//Default for disabled depth texture
|
|
scene.viewDepth = 1;
|
|
scene.verticalDepth = 1;
|
|
|
|
scene.refractionMask = 1.0;
|
|
#if !_DISABLE_DEPTH_TEX
|
|
SceneDepth depth = SampleDepth(scene.positionSS);
|
|
scene.positionWS = ReconstructWorldPosition(scene.positionSS, water.viewDelta, depth);
|
|
|
|
//Invert normal when viewing backfaces
|
|
float normalSign = ceil(dot(water.viewDir, water.waveNormal));
|
|
normalSign = normalSign == 0 ? -1 : 1;
|
|
|
|
//Z-distance to opaque surface
|
|
scene.viewDepth = SurfaceDepth(depth, input.positionCS);
|
|
//Distance to opaque geometry in normal direction
|
|
scene.verticalDepth = DepthDistance(water.positionWS, scene.positionWS, water.waveNormal * normalSign);
|
|
|
|
//Compare position of water to opaque geometry, in order to filter out pixels in front of the water for refraction
|
|
#if _REFRACTION
|
|
SceneDepth depthRefracted = SampleDepth(scene.positionSS + water.refractionOffset);
|
|
float3 opaqueWorldPosRefracted = ReconstructWorldPosition(scene.positionSS + water.refractionOffset, water.viewDelta, depthRefracted);
|
|
|
|
//Reject any offset pixels in front of the water surface
|
|
scene.refractionMask = saturate(SurfaceDepth(depthRefracted, input.positionCS));
|
|
//Lerp to un-refracted screen-position
|
|
water.refractionOffset *= scene.refractionMask;
|
|
|
|
#if RESAMPLE_REFRACTION_DEPTH
|
|
//With the current screen-space UV known, re-compose the water density
|
|
depthRefracted = SampleDepth(scene.positionSS + water.refractionOffset);
|
|
opaqueWorldPosRefracted = ReconstructWorldPosition(scene.positionSS + water.refractionOffset, water.viewDelta, depthRefracted);
|
|
|
|
//Also use the world-position sample as the representation of the underwater geometry (more accurate)
|
|
scene.positionWS = lerp(scene.positionWS, opaqueWorldPosRefracted, scene.refractionMask);
|
|
|
|
scene.viewDepthRefracted = SurfaceDepth(depthRefracted, input.positionCS);
|
|
scene.verticalDepthRefracted = DepthDistance(water.positionWS, opaqueWorldPosRefracted, water.waveNormal * normalSign);
|
|
#endif
|
|
#endif
|
|
|
|
scene.normalWS = half3(0,1,0);
|
|
#if defined(RECONSTRUCT_WORLD_NORMAL)
|
|
if(_EnableDirectionalCaustics)
|
|
{
|
|
scene.normalWS = ReconstructWorldNormal(scene.screenPos.xy + water.refractionOffset.xy);
|
|
}
|
|
#endif
|
|
|
|
#if defined(SCENE_SHADOWMASK)
|
|
float4 sceneShadowCoords = TransformWorldToShadowCoord(scene.positionWS);
|
|
|
|
Light sceneLight = GetMainLight(sceneShadowCoords, scene.positionWS, 1.0);
|
|
|
|
scene.shadowMask = sceneLight.shadowAttenuation;
|
|
#endif
|
|
|
|
#if !_RIVER && _ADVANCED_SHADING
|
|
half VdotN = 1.0 - saturate(dot(water.viewDir, water.waveNormal));
|
|
float grazingTerm = saturate(pow(VdotN, 64));
|
|
|
|
//Resort to z-depth at surface edges. Otherwise makes intersection/edge fade visible through the water surface
|
|
scene.verticalDepth = lerp(scene.verticalDepth, scene.viewDepth, grazingTerm);
|
|
|
|
#if RESAMPLE_REFRACTION_DEPTH && _REFRACTION
|
|
scene.verticalDepthRefracted = lerp(scene.verticalDepthRefracted, scene.viewDepthRefracted, grazingTerm);
|
|
#endif
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if _REFRACTION
|
|
float dispersion = _RefractionChromaticAberration * lerp(1.0, 2.0, unity_OrthoParams.w);
|
|
|
|
scene.color = SampleOpaqueTexture(scene.positionSS, water.refractionOffset.xy, dispersion);
|
|
#endif
|
|
|
|
//Skybox mask is used for backface (underwater) reflections, to blend between refraction and reflection probes
|
|
scene.skyMask = 0;
|
|
#ifdef DEPTH_MASK
|
|
|
|
#if !_DISABLE_DEPTH_TEX
|
|
float depthSource = depth.linear01;
|
|
|
|
#if RESAMPLE_REFRACTION_DEPTH && _REFRACTION
|
|
//Use depth resampled with refracted screen UV
|
|
depthSource = depthRefracted.linear01;
|
|
#endif
|
|
|
|
scene.skyMask = depthSource > 0.99 ? 1 : 0;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
float GetWaterDensity(SceneData scene, float mask, float heightScalar, float viewDepthScalar)
|
|
{
|
|
//Best default value, otherwise water just turns invisible (infinitely shallow)
|
|
float density = 1.0;
|
|
|
|
#if !_DISABLE_DEPTH_TEX
|
|
if(_FogSource == 0)
|
|
{
|
|
|
|
float viewDepth = scene.viewDepth;
|
|
float verticalDepth = scene.verticalDepth;
|
|
|
|
#if defined(RESAMPLE_REFRACTION_DEPTH) && _REFRACTION
|
|
viewDepth = scene.viewDepthRefracted;
|
|
verticalDepth = scene.verticalDepthRefracted;
|
|
#endif
|
|
|
|
float depthAttenuation = 1.0 - exp(-viewDepth * viewDepthScalar * 0.1);
|
|
float heightAttenuation = 1.0 - exp(-verticalDepth * heightScalar);
|
|
|
|
density = max(depthAttenuation, heightAttenuation);
|
|
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
density = mask;
|
|
}
|
|
|
|
density = saturate(density);
|
|
return density;
|
|
}
|
|
|
|
float3 GetWaterColor(SceneData scene, float3 scatterColor, float density, float absorption)
|
|
{
|
|
float depth = scene.verticalDepth;
|
|
float accumulation = scene.viewDepth;
|
|
|
|
#if defined(RESAMPLE_REFRACTION_DEPTH) && _REFRACTION
|
|
depth = scene.verticalDepthRefracted;
|
|
accumulation = scene.viewDepthRefracted;
|
|
#endif
|
|
|
|
float3 underwaterColor = saturate(scene.color * LightExtinction(depth, accumulation, density));
|
|
//Energy loss of ray, as it travels deeper and scatters (absorption)
|
|
float scatterAmount = LightAbsorption(absorption, accumulation);
|
|
|
|
//If the depth is near infinite (ie. hitting the skybox) consider the water completely shallow
|
|
//if(accumulation > _ProjectionParams.z-0.1) scatterAmount = 1;
|
|
|
|
return lerp(underwaterColor, scatterColor, scatterAmount);
|
|
}
|
|
|
|
|
|
//Note: Throws an error about a BLENDEIGHTS vertex attribute on GLES when VR is enabled (fixed in URP 10+)
|
|
//Possibly related to: https://issuetracker.unity3d.com/issues/oculus-a-non-system-generated-input-signature-parameter-blendindices-cannot-appear-after-a-system-generated-value
|
|
#if SHADER_API_GLES3 && defined(STEREO_MULTIVIEW_ON)
|
|
#define FRONT_FACE_SEMANTIC_REAL SV_IsFrontFace
|
|
#define FRONT_FACE_TYPE_REAL bool
|
|
#else
|
|
#define FRONT_FACE_SEMANTIC_REAL FRONT_FACE_SEMANTIC
|
|
#define FRONT_FACE_TYPE_REAL FRONT_FACE_TYPE
|
|
#endif
|
|
|
|
float4 ForwardPass(Varyings input, FRONT_FACE_TYPE_REAL vertexFace : FRONT_FACE_SEMANTIC_REAL)
|
|
{
|
|
UNITY_SETUP_INSTANCE_ID(input);
|
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
|
|
|
//Initialize with null values. Anything that isn't assigned, shouldn't be used either
|
|
WaterSurface water = (WaterSurface)0;
|
|
SceneData scene = (SceneData)0;
|
|
|
|
water.alpha = 1.0;
|
|
water.vFace = IS_FRONT_VFACE(vertexFace, true, false); //0 = back face
|
|
//return float4(lerp(float3(1,0,0), float3(0,1,0), water.vFace), 1.0);
|
|
int faceSign = water.vFace > 0 ? 1 : -1;
|
|
|
|
//return float4(ReconstructWorldNormal(input.positionCS), 1.0);
|
|
|
|
/* ========
|
|
// GEOMETRY DATA
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
|
|
float4 vertexColor = input.color; //Mask already applied in vertex shader
|
|
//return float4(vertexColor.rgb, 1);
|
|
|
|
float2 flowVector = float2(0,0);
|
|
|
|
#if _FLOWMAP
|
|
flowVector = input.uv2.xy * 2.0 - 1.0;
|
|
#endif
|
|
|
|
//Vertex normal in world-space
|
|
water.vertexNormal = normalize(input.normalWS.xyz);
|
|
#if REQUIRES_TANGENT_TO_WORLD
|
|
float3 WorldTangent = input.tangent.xyz;
|
|
float3 WorldBiTangent = input.bitangent.xyz;
|
|
//return float4(WorldBiTangent, 1.0);
|
|
|
|
float3 positionWS = float3(input.normalWS.w, input.tangent.w, input.bitangent.w);
|
|
|
|
//Matrix used to transform a tangent-space normal to world-space
|
|
water.tangentToWorldMatrix = half3x3(WorldTangent, WorldBiTangent, water.vertexNormal);
|
|
#else
|
|
float3 positionWS = input.positionWS;
|
|
#endif
|
|
|
|
#if defined(TESSELLATION_ON)
|
|
//Debug tessellation factor
|
|
//return float4(saturate(CalcDistanceTessFactor(float4(TransformWorldToObject(positionWS.xyz), 1.0), _TessMin, _TessMax, _TessValue)).xxx, 1.0);
|
|
#endif
|
|
|
|
water.positionWS = positionWS;
|
|
//Not normalized for depth-pos reconstruction. Normalization required for lighting (otherwise breaks on mobile)
|
|
water.viewDelta = GetCurrentViewPosition() - positionWS;
|
|
//water.viewDir = GetWorldSpaceViewDir(positionWS); //Uses the camera's forward vector for orthographic projection, the result isn't as useful
|
|
|
|
//Note: SafeNormalize() tends to cause issues on mobile when dealing with large numbers
|
|
water.viewDir = normalize(water.viewDelta);
|
|
//return float4(water.viewDir, 1);
|
|
|
|
half VdotN = 1.0 - saturate(dot(water.viewDir * faceSign, water.vertexNormal));
|
|
|
|
#if _FLAT_SHADING
|
|
float3 dpdx = ddx(positionWS.xyz);
|
|
float3 dpdy = ddy(positionWS.xyz);
|
|
water.vertexNormal = normalize(cross(dpdy, dpdx));
|
|
#endif
|
|
|
|
//return float4(water.vertexNormal, 1.0);
|
|
|
|
//Returns mesh or world-space UV
|
|
float2 uv = GetSourceUV(input.uv.xy, positionWS.xz, _WorldSpaceUV);
|
|
//return float4(frac(uv), 0, 1);
|
|
#endif
|
|
|
|
|
|
/* ========
|
|
// WAVES
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
|
|
water.waveNormal = water.vertexNormal;
|
|
water.waveCrest = 0.0;
|
|
#if _WAVES
|
|
float3 waveOffset = float3(0,0,0);
|
|
|
|
CalculateWaves(_WaveProfile, _WaveProfile_TexelSize.z, _WaveMaxLayers, uv, _WaveFrequency, water.positionWS, _Direction, water.vertexNormal, (TIME_FRAG_INPUT * _Speed) * _WaveSpeed, vertexColor.b, float3(_WaveSteepness, _WaveHeight, _WaveSteepness),
|
|
_WaveNormalStr, _WaveFadeDistance.x, _WaveFadeDistance.y,
|
|
//Out
|
|
waveOffset, water.waveNormal);
|
|
|
|
water.offset.xyz += waveOffset;
|
|
water.waveCrest = waveOffset.y * 0.5 + 0.5;
|
|
//return float4(water.waveCrest.xxx, 1.0);
|
|
|
|
#if _FLAT_SHADING
|
|
water.waveNormal = water.vertexNormal;
|
|
#endif
|
|
|
|
//After wave displacement, recalculated world-space UVs
|
|
if(_WorldSpaceUV == 1)
|
|
{
|
|
//Clamp UV distortion created by lateral displacement
|
|
half waveDistortionScalar = min(0.5, length(waveOffset.xz));
|
|
uv = GetSourceUV(input.uv.xy, positionWS.xz + (waveOffset.xz * waveDistortionScalar), _WorldSpaceUV);
|
|
}
|
|
#endif
|
|
|
|
//return float4(water.waveNormal, 1.0);
|
|
|
|
//return float4(frac(water.offset.xz).xy, 0, 1.0);
|
|
#endif
|
|
|
|
|
|
#if DYNAMIC_EFFECTS_ENABLED
|
|
float4 dynamicEffectsData = 0;
|
|
half dynamicEffectsTopMask = 0;
|
|
if(_ReceiveDynamicEffectsHeight || _ReceiveDynamicEffectsFoam > 0 || _ReceiveDynamicEffectsNormal)
|
|
{
|
|
dynamicEffectsData = SampleDynamicEffectsData(positionWS.xyz);
|
|
dynamicEffectsTopMask = saturate(dot(water.vertexNormal, UP_VECTOR));
|
|
dynamicEffectsData[DE_HEIGHT_CHANNEL] *= dynamicEffectsTopMask;
|
|
dynamicEffectsData[DE_FOAM_CHANNEL] *= dynamicEffectsTopMask * _ReceiveDynamicEffectsFoam;
|
|
//return float4(dynamicEffectsData.bbb, 1.0);
|
|
//return float4(DynamicEffectsBoundsEdgeMask(positionWS).xxx, 1.0);
|
|
}
|
|
#endif
|
|
|
|
/* ========
|
|
// SHADOWS
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
|
|
water.shadowMask = 1.0;
|
|
float4 shadowCoords = float4(0, 0, 0, 0);
|
|
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
|
|
shadowCoords = input.shadowCoord;
|
|
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
|
|
shadowCoords = TransformWorldToShadowCoord(water.positionWS);
|
|
#endif
|
|
|
|
half4 shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);
|
|
Light mainLight = GetMainLight(shadowCoords, water.positionWS, shadowMask);
|
|
bool isMatchingLightLayer = true;
|
|
//return float4(shadowMask.xyz, 1.0);
|
|
|
|
#if _LIGHT_LAYERS
|
|
uint meshRenderingLayers = GetMeshRenderingLayer();
|
|
isMatchingLightLayer = IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers);
|
|
if (isMatchingLightLayer)
|
|
#endif
|
|
{
|
|
water.shadowMask = mainLight.shadowAttenuation;
|
|
}
|
|
|
|
//return float4(water.shadowMask.xxx,1);
|
|
half backfaceShadows = 1;
|
|
|
|
#if UNDERWATER_ENABLED
|
|
//Separate so shadows applied by Unity's lighting do not appear on backfaces
|
|
backfaceShadows = water.shadowMask;
|
|
water.shadowMask = lerp(1.0, water.shadowMask, water.vFace);
|
|
#endif
|
|
#endif
|
|
|
|
#if _RIVER
|
|
water.slope = CalculateSlopeMask(water.vertexNormal, _SlopeAngleThreshold, _SlopeAngleFalloff);
|
|
//return float4(water.slope.xxx, 1);
|
|
#endif
|
|
|
|
/* ========
|
|
// NORMALS
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
water.tangentNormal = float3(0.5, 0.5, 1);
|
|
water.tangentWorldNormal = water.waveNormal;
|
|
|
|
#if DYNAMIC_EFFECTS_ENABLED && !_FLAT_SHADING
|
|
if(_ReceiveDynamicEffectsNormal && NORMALS_AVAILABLE)
|
|
{
|
|
float4 dynamicNormals = SampleDynamicEffectsNormals(water.positionWS);
|
|
dynamicNormals.xyz = normalize(lerp(water.vertexNormal, dynamicNormals.xyz, dynamicNormals.a * dynamicEffectsTopMask));
|
|
//return float4(dynamicNormals.xyz, 1.0);
|
|
|
|
//Composite into wave normal. Not using the tangent normal, since this has variable influence on reflection, dynamic effects should denote geometry curvature
|
|
water.waveNormal = BlendNormalWorldspaceRNM(dynamicNormals.xyz, water.waveNormal, water.vertexNormal);
|
|
//return float4(water.waveNormal, 1.0);
|
|
}
|
|
#endif
|
|
|
|
#if _NORMALMAP
|
|
//Tangent-space
|
|
water.tangentNormal = SampleNormals(uv, _NormalTiling, _NormalSubTiling, positionWS, TIME * -_Direction, _NormalSpeed, _NormalSubSpeed, water.slope, water.vFace);
|
|
//return float4(SRGBToLinear(float3(water.tangentNormal.x * 0.5 + 0.5, water.tangentNormal.y * 0.5 + 0.5, 1)), 1.0);
|
|
|
|
//World-space
|
|
water.tangentWorldNormal = normalize(TransformTangentToWorld(water.tangentNormal, water.tangentToWorldMatrix));
|
|
|
|
#if DYNAMIC_EFFECTS_ENABLED
|
|
if(_ReceiveDynamicEffectsNormal)
|
|
{
|
|
//Let the normals from dynamic effects taken president. For example, smoothing the normals on shoreline waves as they crest
|
|
water.tangentWorldNormal = lerp(water.tangentWorldNormal, water.waveNormal, saturate(dynamicEffectsData[DE_NORMALS_CHANNEL]));
|
|
}
|
|
#endif
|
|
|
|
//return float4(water.tangentWorldNormal, 1.0);
|
|
#endif
|
|
#endif
|
|
|
|
#if _REFRACTION
|
|
float3 refractionViewDir = water.viewDir;
|
|
|
|
#if !_RIVER
|
|
//Technically not correct (as opposed to view direction towards the surface world position), but works better for flat water. Value represents the camera's forward vector.
|
|
refractionViewDir = GetWorldToViewMatrix()[2].xyz;
|
|
#endif
|
|
|
|
water.refractionOffset.xy = RefractionOffset(input.screenPos.xy / input.screenPos.w, refractionViewDir, water.tangentWorldNormal, _RefractionStrength * lerp(1, 0.1, unity_OrthoParams.w));
|
|
//Float4 so it can simply be added to the un-normalized screen position
|
|
water.refractionOffset.zw = 0;
|
|
|
|
//return float4(ScreenEdgeMask(input.screenPos.xy / input.screenPos.w, length(water.refractionOffset.xy)).xxx, 1.0);
|
|
#endif
|
|
|
|
float2 offsetVector = saturate(water.offset.yy + water.tangentWorldNormal.xz);
|
|
|
|
//Normals can perturb the screen coordinates, so needs to be calculated first
|
|
PopulateSceneData(scene, input, water);
|
|
|
|
//float terrainDepth = SampleTerrainDepth(water.positionWS, _IntersectionLength);
|
|
//return float4(terrainDepth.xxx, 1.0);
|
|
//float sdf = SampleTerrainIntersection(water.positionWS);
|
|
//return float4(sdf.xxx, 1.0);
|
|
|
|
//return float4(scene.normalWS * 0.5 + 0.5, 1.0);
|
|
//return float4(scene.shadowMask.xxx, 1.0);
|
|
//return float4(scene.verticalDepth.xxx, 1.0);
|
|
//return float4(scene.viewDepth.xxx, 1.0);
|
|
//return float4((input.screenPos.xy / input.screenPos.w).xy, 0, 1.0);
|
|
//return float4(frac(scene.positionWS.xyz), 1.0);
|
|
//return float4(frac(water.refractionOffset.xy), 0, 1.0);
|
|
//return float4(scene.refractionMask.xxx, 1.0);
|
|
|
|
#if UNDERWATER_ENABLED
|
|
//const float underwaterMask = SampleUnderwaterMask(scene.positionSS.xy / scene.positionSS.w);
|
|
//return float4(underwaterMask.xxx, 1.0);
|
|
ClipSurface(scene.positionSS.xyzw, positionWS, input.positionCS.xyz, water.vFace);
|
|
#endif
|
|
|
|
/* =========
|
|
// COLOR + FOG
|
|
============ */
|
|
#if COLLAPSIBLE_GROUP
|
|
|
|
water.fog = GetWaterDensity(scene, 1-vertexColor.g, _DepthHorizontal, _DepthVertical);
|
|
|
|
#if UNDERWATER_ENABLED
|
|
//When looking through the water from the bottom the depth is practically infinite, seeing as its air
|
|
water.fog = lerp(1, water.fog, water.vFace);
|
|
#endif
|
|
//return float4(water.fog.xxx, 1.0);
|
|
|
|
//Albedo
|
|
float4 baseColor = lerp(_ShallowColor, _BaseColor, water.fog);
|
|
//Avoid color bleeding for foam/intersection on clear water (assumes white foam)
|
|
//baseColor = lerp(1.0, baseColor, baseColor.a);
|
|
|
|
#if COLOR_ABSORPTION && _REFRACTION && !_DISABLE_DEPTH_TEX
|
|
if (_ColorAbsorption > 0)
|
|
{
|
|
baseColor.rgb = GetWaterColor(scene, baseColor.rgb, water.fog, _ColorAbsorption * water.vFace);
|
|
}
|
|
#endif
|
|
|
|
baseColor.rgb += saturate(_WaveTint * water.waveCrest);
|
|
|
|
water.fog *= baseColor.a;
|
|
water.alpha = baseColor.a;
|
|
|
|
|
|
water.albedo.rgb = baseColor.rgb;
|
|
#endif
|
|
|
|
/* ========
|
|
// INTERSECTION FOAM
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
|
|
water.intersection = 0;
|
|
#if _INTERSECTION_FOAM
|
|
|
|
float interSecGradient = 0;
|
|
|
|
#if !_DISABLE_DEPTH_TEX
|
|
float intersectionHeightDelta = scene.verticalDepth;
|
|
|
|
#if defined(RESAMPLE_REFRACTION_DEPTH) && _REFRACTION && defined(INTERSECTION_REFRACTION)
|
|
intersectionHeightDelta = scene.verticalDepthRefracted;
|
|
#endif
|
|
|
|
interSecGradient = 1-saturate(exp(intersectionHeightDelta) / _IntersectionLength);
|
|
|
|
#endif
|
|
|
|
if (_IntersectionSource == 1) interSecGradient = vertexColor.r;
|
|
if (_IntersectionSource == 2) interSecGradient = saturate(interSecGradient + vertexColor.r);
|
|
//interSecGradient = saturate(SampleTerrainSDF(water.positionWS) / _IntersectionLength);
|
|
|
|
#if DYNAMIC_EFFECTS_ENABLED
|
|
//interSecGradient += dynamicEffectsData[DE_ALPHA_CHANNEL];
|
|
#endif
|
|
//interSecGradient = terrainDepth;
|
|
|
|
water.intersection = SampleIntersection(uv.xy + (offsetVector * _IntersectionDistortion), (TIME * -_Direction),
|
|
_IntersectionTiling, interSecGradient, _IntersectionFalloff, _IntersectionSpeed, _IntersectionRippleDist, _IntersectionRippleStrength, _IntersectionRippleSpeed, _IntersectionClipping, _IntersectionSharp) * _IntersectionColor.a;
|
|
|
|
#if UNDERWATER_ENABLED
|
|
//Hide on backfaces
|
|
water.intersection *= water.vFace;
|
|
#endif
|
|
|
|
#if _WAVES && !_DISABLE_DEPTH_TEX
|
|
//Prevent from peering through waves when camera is at the water level
|
|
if(positionWS.y < scene.positionWS.y) water.intersection = 0;
|
|
#endif
|
|
|
|
//water.density += water.intersection;
|
|
|
|
//Flatten normals on intersection foam
|
|
water.waveNormal = lerp(water.waveNormal, water.vertexNormal, water.intersection);
|
|
//return float4(water.intersection.xxx,1);
|
|
#endif
|
|
|
|
#if _NORMALMAP
|
|
water.tangentWorldNormal = lerp(water.tangentWorldNormal, water.vertexNormal, water.intersection);
|
|
#endif
|
|
#endif
|
|
|
|
/* ========
|
|
// SURFACE FOAM
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
water.foam = 0;
|
|
|
|
#if _SURFACE_FOAM
|
|
bool enableSlopeFoam = false;
|
|
bool enableDistanceFoam = false;
|
|
#if _RIVER
|
|
enableSlopeFoam = true;
|
|
#endif
|
|
#if _SURFACE_FOAM_DUAL
|
|
enableDistanceFoam = true;
|
|
#endif
|
|
|
|
float crestFoam = 0.0;
|
|
#if _WAVES
|
|
//Composed mask for foam caps, based on wave height
|
|
crestFoam = CalculateCrestFoam(_FoamCrestMinMaxHeight.x, _FoamCrestMinMaxHeight.y, water.waveCrest);
|
|
#endif
|
|
|
|
#if !_RIVER
|
|
float foamSlopeMask = 0;
|
|
#else
|
|
float foamSlopeMask = saturate((water.slope * _SlopeFoam) + vertexColor.a);
|
|
#endif
|
|
|
|
float baseFoam = saturate(_FoamBaseAmount - water.slope + vertexColor.a);
|
|
float foamGradient = saturate(crestFoam + baseFoam + foamSlopeMask);
|
|
|
|
//Parallaxing
|
|
half2 foamDistortion = -(_FoamDistortion * water.viewDir.xz * saturate(dot(water.waveNormal, water.viewDir))) ;
|
|
//half2 foamDistortion = offsetVector * _FoamDistortion.xx;
|
|
|
|
#if _RIVER
|
|
//Only distort sideways, makes the effect appear more like foam is moving around obstacles or shallow rocks
|
|
foamDistortion.y = 0;
|
|
#endif
|
|
|
|
float2 foamTex = SampleFoamTexture(water.positionWS, (uv + foamDistortion.xy), _FoamTiling, _FoamSubTiling, (TIME * -_Direction), _FoamSpeed, _FoamSubSpeed, foamSlopeMask,
|
|
_SlopeSpeed, _SlopeStretching, enableSlopeFoam, enableDistanceFoam, _DistanceFoamFadeDist.x, _DistanceFoamFadeDist.y, _DistanceFoamTiling);
|
|
if(_FoamClipping > 0) foamTex.r = smoothstep(_FoamClipping, 1.0, foamTex.r);
|
|
|
|
//Dissolve the foam based on the input gradient
|
|
water.foam = CalculateFoamWeight(foamGradient, foamTex.r) * _FoamColor.a * _FoamStrength;
|
|
|
|
float foamBubbles = foamGradient;
|
|
|
|
//Dynamic foam (separately sampled)
|
|
#if DYNAMIC_EFFECTS_ENABLED
|
|
if(_ReceiveDynamicEffectsFoam > 0)
|
|
{
|
|
foamDistortion = _FoamDistortion * dynamicEffectsData[DE_HEIGHT_CHANNEL].xx;
|
|
|
|
float2 dynamicFoamTex = SampleDynamicFoam((uv + foamDistortion.xy), _FoamTilingDynamic, _FoamSubTilingDynamic, (TIME * -_Direction), _FoamSpeedDynamic, _FoamSubSpeedDynamic);
|
|
#if _RIVER
|
|
//foamGradient -= water.slope;
|
|
#endif
|
|
|
|
//return float4(dynamicFoamTex.rg, 0, 1);
|
|
|
|
water.foam += CalculateFoamWeight(dynamicEffectsData[DE_FOAM_CHANNEL], dynamicFoamTex.r);
|
|
if(_FoamClippingDynamic > 0) water.foam = smoothstep(_FoamClippingDynamic, 1.0, water.foam);
|
|
|
|
//Add foam weight, as this is used for bubbles
|
|
foamGradient += dynamicEffectsData[DE_FOAM_CHANNEL];
|
|
|
|
foamBubbles = saturate(foamBubbles + dynamicEffectsData[DE_FOAM_CHANNEL]);
|
|
}
|
|
#endif
|
|
|
|
water.foam = saturate(water.foam);
|
|
|
|
if(_FoamBubblesStrength > 0)
|
|
{
|
|
foamBubbles = CalculateFoamWeight(foamGradient * _FoamBubblesSpread, saturate(foamBubbles)) * _FoamBubblesStrength;
|
|
//return float4(foamBubbles.xxx, 1.0);
|
|
water.albedo = lerp(water.albedo, _ShallowColor.rgb, foamBubbles);
|
|
}
|
|
|
|
#if _NORMALMAP
|
|
//Flatten normal map on foam
|
|
water.tangentWorldNormal = lerp(water.tangentWorldNormal, water.waveNormal, water.foam);
|
|
#endif
|
|
//return float4(water.foam.xxx, 1);
|
|
#endif
|
|
#endif
|
|
|
|
/* ========
|
|
// EMISSION (Caustics + Specular)
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
|
|
#if _CAUSTICS
|
|
float3 causticsCoords = scene.positionWS;
|
|
#if _DISABLE_DEPTH_TEX
|
|
causticsCoords = uv.xyy;
|
|
#endif
|
|
|
|
float causticsMask = saturate((1-water.fog) - water.intersection - water.foam - scene.skyMask) * water.vFace;
|
|
|
|
bool directional = _EnableDirectionalCaustics && isMatchingLightLayer;
|
|
float2 causticsProjection = GetCausticsProjection(input.positionCS, mainLight.direction, causticsCoords, scene.normalWS, directional, causticsMask);
|
|
|
|
//Refraction creates discrepancy
|
|
//causticsProjection = CalculateTriPlanarProjection(scene.positionWS, ReconstructWorldNormal(input.positionCS));
|
|
#ifdef SCENE_SHADOWMASK
|
|
causticsMask *= scene.shadowMask;
|
|
#endif
|
|
|
|
float3 causticsDistortion = lerp(water.waveNormal.xyz, water.tangentWorldNormal.xyz, _CausticsDistortion);
|
|
|
|
#if _ADVANCED_SHADING
|
|
//causticsDistortion = TransformWorldToViewDir(causticsDistortion);
|
|
//causticsDistortion.xz = causticsDistortion.xy;
|
|
#endif
|
|
|
|
water.caustics = SampleCaustics(causticsProjection + causticsDistortion.xz, (TIME * -_Direction) * _CausticsSpeed, _CausticsTiling, _CausticsChromance);
|
|
|
|
//return float4(causticsMask.xxx, 1.0);
|
|
|
|
//Note: not masked by surface shadows, this occurs in the lighting function so it also takes point/spot lights into account
|
|
water.caustics *= causticsMask * _CausticsBrightness;
|
|
//return float4(water.caustics.rgb, 1);
|
|
#endif
|
|
|
|
#if _NORMALMAP
|
|
if(_SparkleIntensity > 0)
|
|
{
|
|
//Can piggyback on the tangent normal
|
|
half3 sparkles = mainLight.color * saturate(step(_SparkleSize, (water.tangentNormal.y))) * _SparkleIntensity;
|
|
|
|
#if !_UNLIT
|
|
//Fade out the effect as the sun approaches the horizon
|
|
float sunAngle = saturate(dot(water.vertexNormal, mainLight.direction));
|
|
float angleMask = saturate(sunAngle * 10); /* 1.0/0.10 = 10 */
|
|
sparkles *= angleMask;
|
|
#endif
|
|
|
|
water.specular += sparkles.rgb;
|
|
}
|
|
#endif
|
|
|
|
#ifndef _SPECULARHIGHLIGHTS_OFF
|
|
float3 lightReflectionNormal = water.tangentWorldNormal;
|
|
|
|
#if _FLAT_SHADING //Use face normals
|
|
lightReflectionNormal = water.waveNormal;
|
|
#endif
|
|
|
|
half specularMask = 1-saturate(water.foam + water.intersection * (1-water.shadowMask));
|
|
//return float4(specularMask.xxx, 1.0);
|
|
|
|
float3 sunSpecular = 0;
|
|
|
|
if(isMatchingLightLayer)
|
|
{
|
|
sunSpecular = SpecularReflection(mainLight, water.viewDir, water.waveNormal, lightReflectionNormal, _SunReflectionDistortion, lerp(8196, 64, _SunReflectionSize), _SunReflectionStrength * specularMask, _SunReflectionSharp);
|
|
water.specular += sunSpecular;
|
|
}
|
|
//return float4(water.specular, 1.0);
|
|
#endif
|
|
//return float4(specular, 1.0);
|
|
|
|
//Reflection probe/planar
|
|
float3 renderedReflections = 0;
|
|
#ifndef _ENVIRONMENTREFLECTIONS_OFF
|
|
|
|
//Blend between smooth surface normal and normal map to control the reflection perturbation (probes only!)
|
|
#if !_FLAT_SHADING
|
|
float3 refWorldNormal = lerp(water.waveNormal, normalize(water.waveNormal + water.tangentWorldNormal), _ReflectionDistortion);
|
|
#else //Skip, not a good fit
|
|
float3 refWorldNormal = water.waveNormal;
|
|
#endif
|
|
|
|
half3 reflectionViewDir = water.viewDir;
|
|
|
|
#if _REFLECTION_PROBE_BOX_PROJECTION
|
|
//Use the camera's forward vector when the camera is orthographic
|
|
if(unity_OrthoParams.w == 1) reflectionViewDir = GetWorldSpaceViewDir(positionWS);
|
|
#endif
|
|
|
|
half3 reflectionVector = reflect(-reflectionViewDir, refWorldNormal);
|
|
|
|
#if !_RIVER
|
|
//Ensure only the top hemisphere of the reflection probe is used
|
|
//reflectionVector.y = max(0, reflectionVector.y);
|
|
#endif
|
|
|
|
//Pixel offset for planar reflection, sampled in screen-space
|
|
float3 reflectionOffsetVector = lerp(water.vertexNormal, water.tangentWorldNormal, _ReflectionDistortion);
|
|
|
|
#if _ADVANCED_SHADING
|
|
//reflectionOffsetVector = TransformWorldToViewDir(reflectionOffsetVector);
|
|
//reflectionOffsetVector.xz = reflectionOffsetVector.xy;
|
|
#endif
|
|
|
|
float2 reflectionPixelOffset = (reflectionOffsetVector.xz * scene.positionSS.w * SCREENSPACE_REFLECTION_DISTORTION_MULTIPLIER).xy;
|
|
|
|
//SSR + Planar
|
|
|
|
water.reflections = SampleReflections(reflectionVector, _ReflectionBlur, scene.positionSS.xyzw, positionWS, refWorldNormal, water.viewDir, reflectionPixelOffset, _PlanarReflectionsEnabled, _ScreenSpaceReflectionsEnabled, renderedReflections);
|
|
//return float4(water.reflections, 1.0);
|
|
|
|
float reflectionFresnel = ReflectionFresnel(refWorldNormal, water.viewDir * faceSign, _ReflectionFresnel);
|
|
//return float4(reflectionFresnel.xxx, 1.0);
|
|
|
|
water.reflectionMask = _ReflectionStrength * reflectionFresnel;
|
|
water.reflectionLighting = 1-_ReflectionLighting;
|
|
|
|
#if _UNLIT
|
|
//Nullify, otherwise reflections turn black
|
|
water.reflectionLighting = 1.0;
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
/* ========
|
|
// COMPOSITION
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
|
|
//Foam application on top of everything up to this point
|
|
#if _SURFACE_FOAM
|
|
//Mitigate color bleeding into the foam by scaling it
|
|
water.albedo.rgb = lerp(water.albedo.rgb, _FoamColor.rgb, water.foam);
|
|
#endif
|
|
|
|
#if _INTERSECTION_FOAM
|
|
//Layer intersection on top of everything
|
|
water.albedo.rgb = lerp(water.albedo.rgb, _IntersectionColor.rgb, water.intersection);
|
|
#endif
|
|
|
|
#if _SURFACE_FOAM || _INTERSECTION_FOAM
|
|
//Sum values to compose alpha
|
|
water.alpha = saturate(water.alpha + water.intersection + water.foam);
|
|
#endif
|
|
|
|
#ifndef _ENVIRONMENTREFLECTIONS_OFF
|
|
//Foam complete, use it to mask out the reflection (considering that foam is rough)
|
|
water.reflectionMask = saturate(water.reflectionMask - water.foam - water.intersection) * _ReflectionStrength;
|
|
//return float4(reflectionFresnel.xxx, 1);
|
|
|
|
#if !_UNLIT
|
|
//Blend reflection with albedo. Diffuse lighting will affect it
|
|
water.albedo.rgb = lerp(water.albedo, lerp(water.albedo.rgb, water.reflections, water.reflectionMask), _ReflectionLighting);
|
|
//return float4(water.albedo.rgb, 1);
|
|
#endif
|
|
#endif
|
|
//return float4(water.reflections.rgb, 1);
|
|
|
|
#if !_UNLIT
|
|
//Blend between smooth geometry normal and normal map for diffuse lighting
|
|
water.diffuseNormal = lerp(water.waveNormal, water.tangentWorldNormal, _NormalStrength);
|
|
#endif
|
|
|
|
#if _FLAT_SHADING
|
|
//Moving forward, consider the tangent world normal the same as the flat-shaded normals
|
|
water.tangentWorldNormal = water.waveNormal;
|
|
#endif
|
|
|
|
//Horizon color (note: not using normals, since they are perturbed by waves)
|
|
float fresnel = saturate(pow(VdotN, _HorizonDistance)) * _HorizonColor.a;
|
|
#if UNDERWATER_ENABLED
|
|
fresnel *= water.vFace;
|
|
#endif
|
|
water.albedo.rgb = lerp(water.albedo.rgb, _HorizonColor.rgb, fresnel);
|
|
|
|
#if UNITY_COLORSPACE_GAMMA
|
|
//Gamma-space is likely a choice, enabling this will have the water stand out from non gamma-corrected shaders
|
|
//water.albedo.rgb = LinearToSRGB(water.albedo.rgb);
|
|
#endif
|
|
|
|
//Final alpha
|
|
water.edgeFade = saturate(scene.verticalDepth / (_EdgeFade * 0.01));
|
|
|
|
#if UNDERWATER_ENABLED
|
|
water.edgeFade = lerp(1.0, water.edgeFade, water.vFace);
|
|
#endif
|
|
|
|
water.alpha *= water.edgeFade;
|
|
#endif
|
|
|
|
/* ========
|
|
// TRANSLUCENCY
|
|
=========== */
|
|
TranslucencyData translucencyData = (TranslucencyData)0;
|
|
#if _TRANSLUCENCY
|
|
if(isMatchingLightLayer)
|
|
{
|
|
float scatteringMask = 1.0;
|
|
scatteringMask = saturate((water.fog + water.edgeFade) - (water.reflectionMask * water.vFace)) * water.shadowMask;
|
|
scatteringMask -= water.foam;
|
|
|
|
scatteringMask = saturate(scatteringMask);
|
|
|
|
//return float4(scatteringMask.xxx, 1);
|
|
|
|
translucencyData = PopulateTranslucencyData(_ShallowColor.rgb, mainLight.direction, mainLight.color, water.viewDir, water.waveNormal, water.tangentWorldNormal, scatteringMask, _TranslucencyStrength, _TranslucencyStrengthDirect * water.vFace, _TranslucencyExp, _TranslucencyCurvatureMask * water.vFace, true);
|
|
|
|
#if UNDERWATER_ENABLED
|
|
//Override the strength of the effect for the backfaces, to match the underwater shading post effect
|
|
translucencyData.strength *= lerp(_UnderwaterFogBrightness * _UnderwaterSubsurfaceStrength, 1, water.vFace);
|
|
translucencyData.exponent = lerp(_UnderwaterSubsurfaceExponent, _TranslucencyExp, water.vFace);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/* ========
|
|
// UNITY SURFACE & INPUT DATA
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
SurfaceData surfaceData = (SurfaceData)0;
|
|
|
|
surfaceData.albedo = water.albedo.rgb;
|
|
surfaceData.specular = water.specular.rgb;
|
|
surfaceData.metallic = 0;
|
|
surfaceData.smoothness = 0;
|
|
surfaceData.normalTS = water.tangentNormal;
|
|
surfaceData.emission = 0; //To be populated with translucency+caustics
|
|
surfaceData.occlusion = 1.0;
|
|
surfaceData.alpha = water.alpha;
|
|
|
|
//https://github.com/Unity-Technologies/Graphics/blob/31106afc882d7d1d7e3c0a51835df39c6f5e3073/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl#L34
|
|
InputData inputData = (InputData)0;
|
|
inputData.positionWS = positionWS;
|
|
inputData.viewDirectionWS = water.viewDir;
|
|
inputData.shadowCoord = shadowCoords;
|
|
#if UNDERWATER_ENABLED
|
|
//Flatten normals for underwater lighting (distracting, peers through the fog)
|
|
inputData.normalWS = lerp(water.waveNormal, water.tangentWorldNormal, water.vFace);
|
|
#else
|
|
inputData.normalWS = water.tangentWorldNormal;
|
|
#endif
|
|
inputData.fogCoord = InitializeInputDataFog(float4(positionWS, 1.0), input.fogFactorAndVertexLight.x);
|
|
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
|
|
inputData.shadowMask = water.shadowMask.xxxx;
|
|
inputData.normalizedScreenSpaceUV = scene.positionSS.xy / scene.positionSS.w;
|
|
inputData.bakedGI = 0;
|
|
|
|
#if defined(DYNAMICLIGHTMAP_ON)
|
|
inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.dynamicLightmapUV.xy, input.vertexSH, inputData.normalWS);
|
|
#elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2))
|
|
inputData.bakedGI = SAMPLE_GI(input.vertexSH,
|
|
GetAbsolutePositionWS(inputData.positionWS),
|
|
inputData.normalWS,
|
|
inputData.viewDirectionWS,
|
|
input.positionCS.xy
|
|
//Unity 6000.0.9+
|
|
//#if UNITY_VERSION > 600000
|
|
,input.probeOcclusion
|
|
,inputData.shadowMask
|
|
//#endif
|
|
);
|
|
#else
|
|
inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.vertexSH, inputData.normalWS);
|
|
#endif
|
|
|
|
|
|
//Lightmap(static+dynamic) or SH
|
|
//return float4(inputData.bakedGI, 1.0);
|
|
|
|
#endif
|
|
|
|
//return float4(surfaceData.emission, 1.0);
|
|
/* ========
|
|
// RENDERING DEBUGGER (URP 12+)
|
|
=========== */
|
|
#if COLLAPSIBLE_GROUP
|
|
|
|
#if defined(DEBUG_DISPLAY)
|
|
inputData.positionCS = input.positionCS;
|
|
#if _NORMALMAP
|
|
inputData.tangentToWorld = water.tangentToWorldMatrix;
|
|
#else
|
|
inputData.tangentToWorld = 0;
|
|
#endif
|
|
inputData.shadowMask = water.shadowMask.xxxx;
|
|
#if defined(DYNAMICLIGHTMAP_ON)
|
|
inputData.dynamicLightmapUV = input.dynamicLightmapUV;
|
|
#endif
|
|
#if defined(LIGHTMAP_ON)
|
|
inputData.staticLightmapUV = input.staticLightmapUV;
|
|
#else
|
|
inputData.vertexSH = input.vertexSH;
|
|
#endif
|
|
|
|
surfaceData.emission = water.caustics;
|
|
ApplyTranslucency(translucencyData, surfaceData.emission.rgb);
|
|
|
|
inputData.brdfDiffuse = surfaceData.albedo;
|
|
inputData.brdfSpecular = surfaceData.specular;
|
|
inputData.uv = uv;
|
|
inputData.mipCount = 0;
|
|
inputData.texelSize = float4(1/uv.x, 1/uv.y, uv.x, uv.y);
|
|
inputData.mipInfo = 0;
|
|
half4 debugColor;
|
|
|
|
if (_DebugLightingMode == DEBUGLIGHTINGMODE_REFLECTIONS || _DebugLightingMode == DEBUGLIGHTINGMODE_REFLECTIONS_WITH_SMOOTHNESS)
|
|
{
|
|
return float4(water.reflections * (_DebugLightingMode == DEBUGLIGHTINGMODE_REFLECTIONS_WITH_SMOOTHNESS ? water.reflectionMask : 1), 1.0);
|
|
}
|
|
|
|
if (_DebugLightingMode == DEBUGMATERIALMODE_RENDERING_LAYER_MASKS)
|
|
{
|
|
//return float4(GetRenderingLayerMasksDebugColor(inputData.positionCS, inputData.normalWS).xyz, 1.0);
|
|
}
|
|
|
|
if (CanDebugOverrideOutputColor(inputData, surfaceData, debugColor))
|
|
{
|
|
return debugColor;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if UNDERWATER_ENABLED
|
|
//Snell's window
|
|
float reflectionCoefficient = UnderwaterReflectionFactor(inputData.normalWS, water.tangentWorldNormal, water.viewDir, _UnderwaterSurfaceSmoothness, _UnderwaterRefractionOffset);
|
|
#endif
|
|
|
|
float4 finalColor = float4(ApplyLighting(surfaceData, scene.color, mainLight, inputData, water, translucencyData, _ShadowStrength, water.vFace, isMatchingLightLayer), water.alpha);
|
|
|
|
#if _REFRACTION
|
|
finalColor.rgb = lerp(scene.color.rgb, finalColor.rgb, saturate(water.fog + water.intersection + water.foam));
|
|
//The opaque color texture is now used. The "real" alpha value is solely the edge fade factor
|
|
water.alpha = water.edgeFade;
|
|
#endif
|
|
|
|
half fogMask = 1.0;
|
|
#if UNDERWATER_ENABLED
|
|
//Limit to front faces as underwater fog already applies to the bottom
|
|
fogMask = water.vFace;
|
|
#endif
|
|
|
|
ApplyFog(finalColor.rgb, inputData.fogCoord, scene.positionSS, positionWS, fogMask);
|
|
|
|
#if UNDERWATER_ENABLED
|
|
float4 underwaterColor = ShadeUnderwaterSurface(surfaceData.albedo.rgb, surfaceData.emission.rgb, surfaceData.specular.rgb, renderedReflections * _UnderwaterReflectionStrength, scene.color.rgb, scene.skyMask,
|
|
backfaceShadows, inputData.positionWS, inputData.normalWS, water.tangentWorldNormal, water.viewDir, scene.positionSS.xy,
|
|
_ShallowColor, _BaseColor, water.vFace, _UnderwaterSurfaceSmoothness, _UnderwaterRefractionOffset);
|
|
|
|
#if _REFRACTION
|
|
underwaterColor.a = 1.0;
|
|
#endif
|
|
|
|
finalColor.rgb = lerp(underwaterColor.rgb, finalColor.rgb, water.vFace);
|
|
water.alpha = lerp(underwaterColor.a, water.alpha, water.vFace);
|
|
#endif
|
|
|
|
//return float4(water.alpha.xxx, 1.0);
|
|
|
|
finalColor.a = water.alpha;
|
|
|
|
//Vertex color green channel controls real alpha in this case
|
|
if(_VertexColorTransparency > 0.5) finalColor.a = water.alpha * saturate(water.alpha - vertexColor.g);
|
|
|
|
return finalColor;
|
|
}
|
|
|
|
|
|
void ForwardPassFragment(
|
|
Varyings input, FRONT_FACE_TYPE_REAL vertexFace : FRONT_FACE_SEMANTIC_REAL
|
|
, out half4 outColor : SV_Target0
|
|
#ifdef _WRITE_RENDERING_LAYERS
|
|
, out float4 outRenderingLayers : SV_Target1
|
|
#endif
|
|
)
|
|
{
|
|
outColor = ForwardPass(input, vertexFace);
|
|
|
|
#ifdef _WRITE_RENDERING_LAYERS
|
|
uint renderingLayers = GetMeshRenderingLayer();
|
|
outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0);
|
|
#endif
|
|
} |