@@ -88,7 +88,7 @@ struct VS_OUTPUT
8888 float4 TexCoord3 : TEXCOORD3 ;
8989# endif
9090# if defined (FLOWMAP)
91- nointerpolation float TexCoord4 : TEXCOORD4 ;
91+ nointerpolation float2 TexCoord4 : TEXCOORD4 ;
9292# endif
9393# if NUM_SPECULAR_LIGHTS == 0
9494 float4 MPosition : TEXCOORD5 ;
@@ -453,7 +453,7 @@ struct FlowmapData
453453FlowmapData GetFlowmapDataTextureSpace (PS_INPUT input, float2 uvShift)
454454{
455455 FlowmapData data;
456- data.color = FlowMapTex.Sample (FlowMapSampler, input.TexCoord2.zw + uvShift);
456+ data.color = FlowMapTex.SampleLevel (FlowMapSampler, input.TexCoord2.zw + uvShift, 0 );
457457 data.flowVector = (64 * input.TexCoord3.xy) * sqrt (1.01 - data.color.z);
458458 // NOTE: flowVector is NOT transformed yet - this is the raw vector before rotation matrix
459459 return data;
@@ -486,23 +486,90 @@ FlowmapData GetFlowmapDataUV(PS_INPUT input, float2 uvShift)
486486 data.flowVector = mul (transpose (flowRotationMatrix), data.flowVector);
487487 return data;
488488}
489+ // ----------------------------------------------------------------
490+ // Flowmap Parallax Functions
491+ // ----------------------------------------------------------------
489492
490493/**
491- * Generates flowmap-based normal perturbation for water surface
494+ * Samples height from flowmap texture using the same 4-sample blend as flowmap normals
495+ * This ensures height transitions match the normal transitions exactly
492496 *
493- * @param input Pixel shader input containing texture coordinates and world position
494- * @param uvShift UV offset for flowmap sampling (used for animation phases)
495- * @param multiplier Intensity multiplier for the flow effect
496- * @param offset Base UV offset for the normal texture sampling
497- * @return float3 Normal perturbation (XY=normal offset, Z=flow strength mask)
498- *
499- * @details This function uses flowmap data to:
500- * - Calculate flow-displaced UV coordinates for normal texture sampling
501- * - Apply flow-based animation to water normal textures
502- * - Return both the normal perturbation and flow strength information
503- *
504- * @note The returned Z component contains the original flowmap strength value
505- * which can be used for blending between flow and non-flow normals
497+ * @param input PS_INPUT for flowmap coordinate access
498+ * @param normalMul The blend weights from the flowmap system (same as used for normals)
499+ * @param uvShift The UV shift value (1 / (128 * flowmapDimensions))
500+ * @param mipLevel Mip level for texture sampling
501+ */
502+ float GetFlowmapHeightBlended (PS_INPUT input, float2 normalMul, float2 uvShift, float mipLevel)
503+ {
504+ // Sample height using the EXACT same UV computation as GetFlowmapNormal
505+ // This ensures the height blending matches the normal blending perfectly
506+
507+ // Sample 0: uvShift, multiplier=9.92, offset=0
508+ FlowmapData flowData0 = GetFlowmapDataUV (input, uvShift);
509+ float2 uv0 = 0 + (flowData0.flowVector - float2 (9.92 * ((0.001 * ReflectionColor.w) * flowData0.color.w), 0 ));
510+ float height0 = FlowMapNormalsTex.SampleLevel (FlowMapNormalsSampler, uv0, mipLevel).w;
511+
512+ // Sample 1: float2(0, uvShift.y), multiplier=10.64, offset=0.27
513+ FlowmapData flowData1 = GetFlowmapDataUV (input, float2 (0 , uvShift.y));
514+ float2 uv1 = 0.27 + (flowData1.flowVector - float2 (10.64 * ((0.001 * ReflectionColor.w) * flowData1.color.w), 0 ));
515+ float height1 = FlowMapNormalsTex.SampleLevel (FlowMapNormalsSampler, uv1, mipLevel).w;
516+
517+ // Sample 2: 0.0.xx, multiplier=8, offset=0
518+ FlowmapData flowData2 = GetFlowmapDataUV (input, 0.0 .xx);
519+ float2 uv2 = 0 + (flowData2.flowVector - float2 (8 * ((0.001 * ReflectionColor.w) * flowData2.color.w), 0 ));
520+ float height2 = FlowMapNormalsTex.SampleLevel (FlowMapNormalsSampler, uv2, mipLevel).w;
521+
522+ // Sample 3: float2(uvShift.x, 0), multiplier=8.48, offset=0.62
523+ FlowmapData flowData3 = GetFlowmapDataUV (input, float2 (uvShift.x, 0 ));
524+ float2 uv3 = 0.62 + (flowData3.flowVector - float2 (8.48 * ((0.001 * ReflectionColor.w) * flowData3.color.w), 0 ));
525+ float height3 = FlowMapNormalsTex.SampleLevel (FlowMapNormalsSampler, uv3, mipLevel).w;
526+
527+ // Use the EXACT same blending formula as flowmap normals
528+ float blendedHeight =
529+ normalMul.y * (normalMul.x * height2 + (1 - normalMul.x) * height3) +
530+ (1 - normalMul.y) * (normalMul.x * height1 + (1 - normalMul.x) * height0);
531+
532+ return blendedHeight;
533+ }
534+
535+ // Keep this for compatibility - just forwards to the proper function
536+ float GetFlowmapHeightBarycentric (PS_INPUT input, float2 flowmapDimensions, float2 baseUV, float mipLevel)
537+ {
538+ // This is now unused - we use GetFlowmapHeightBlended directly
539+ return FlowMapNormalsTex.SampleLevel (FlowMapNormalsSampler, baseUV, mipLevel).w;
540+ }
541+
542+ /**
543+ * Computes mip level for flowmap texture sampling
544+ */
545+ float GetFlowmapMipLevel (float2 flowmapUV)
546+ {
547+ float2 textureDims;
548+ FlowMapNormalsTex.GetDimensions (textureDims.x, textureDims.y);
549+
550+ #if defined (VR)
551+ textureDims /= 16.0 ;
552+ #else
553+ textureDims /= 8.0 ;
554+ #endif
555+
556+ float2 texCoordsPerSize = flowmapUV * textureDims;
557+ float2 dxSize = ddx (texCoordsPerSize);
558+ float2 dySize = ddy (texCoordsPerSize);
559+ float2 dTexCoords = dxSize * dxSize + dySize * dySize;
560+ float minTexCoordDelta = max (dTexCoords.x, dTexCoords.y);
561+ return max (0.5 * log2 (minTexCoordDelta), 0 );
562+ }
563+
564+ /**
565+ * Samples height from flowmap texture (riverflow.dds alpha channel)
566+ * Uses the same UV calculation as GetFlowmapNormal for consistency
567+ */
568+
569+
570+ /**
571+ * Generates flowmap-based normal (no parallax - flowmap normals are not parallax-shifted)
572+ * Uses mip clamping to preserve detail at distance and prevent over-blurring
506573 */
507574float3 GetFlowmapNormal (PS_INPUT input, float2 uvShift, float multiplier, float offset)
508575{
@@ -588,14 +655,34 @@ WaterNormalData GetWaterNormal(PS_INPUT input, float distanceFactor, float norma
588655# endif
589656
590657# if defined (FLOWMAP)
591- float2 normalMul =
592- 0.5 + -(-0.5 + abs (frac (input.TexCoord2.zw * (64 * input.TexCoord4)) * 2 - 1 ));
593- float uvShift = 1 / (128 * input.TexCoord4);
658+ # if defined (UNIFIED_WATER)
659+ float2 flowmapDimensions = input.TexCoord4.xy;
660+ # else
661+ float2 flowmapDimensions = input.TexCoord4.xx;
662+ # endif
663+ float2 uvShift = 1 / (128 * flowmapDimensions);
664+
665+ // Compute flowmap parallax and create parallaxed input for normal sampling
666+ PS_INPUT flowmapInput = input;
667+ float2 flowmapParallaxOffset = float2 (0 , 0 );
668+ # if defined (WATER_PARALLAX) && !defined (LOD)
669+ float parallaxAmount = WaterEffects::GetFlowmapParallaxAmount (input, flowmapDimensions, viewDirection);
670+ float2 parallaxDir = viewDirection.xy / -viewDirection.z;
671+ parallaxDir.y = -parallaxDir.y;
672+ float viewDotUp = -viewDirection.z;
673+ parallaxDir *= 0.008 * saturate (viewDotUp * 2.0 );
674+ flowmapInput.TexCoord3.xy = input.TexCoord3.xy + parallaxAmount * parallaxDir;
675+ flowmapParallaxOffset = WaterEffects::GetFlowmapParallaxOffset (input, flowmapDimensions, viewDirection, normalScalesRcp);
676+ # endif
594677
595- float3 flowmapNormal0 = GetFlowmapNormal (input, uvShift.xx, 9.92 , 0 );
596- float3 flowmapNormal1 = GetFlowmapNormal (input, float2 (0 , uvShift), 10.64 , 0.27 );
597- float3 flowmapNormal2 = GetFlowmapNormal (input, 0.0 .xx, 8 , 0 );
598- float3 flowmapNormal3 = GetFlowmapNormal (input, float2 (uvShift, 0 ), 8.48 , 0.62 );
678+ // Calculate cell blend weights using parallaxed input
679+ float2 normalMul = 0.5 + -(-0.5 + abs (frac (flowmapInput.TexCoord2.zw * (64 * flowmapDimensions)) * 2 - 1 ));
680+
681+ // Sample flowmap normals with parallax applied
682+ float3 flowmapNormal0 = GetFlowmapNormal (flowmapInput, uvShift, 9.92 , 0 );
683+ float3 flowmapNormal1 = GetFlowmapNormal (flowmapInput, float2 (0 , uvShift.y), 10.64 , 0.27 );
684+ float3 flowmapNormal2 = GetFlowmapNormal (flowmapInput, 0.0 .xx, 8 , 0 );
685+ float3 flowmapNormal3 = GetFlowmapNormal (flowmapInput, float2 (uvShift.x, 0 ), 8.48 , 0.62 );
599686
600687 float2 flowmapNormalWeighted =
601688 normalMul.y * (normalMul.x * flowmapNormal2.xy + (1 - normalMul.x) * flowmapNormal3.xy) +
@@ -608,14 +695,21 @@ WaterNormalData GetWaterNormal(PS_INPUT input, float distanceFactor, float norma
608695 0 );
609696 flowmapNormal.z =
610697 sqrt (1 - flowmapNormal.x * flowmapNormal.x - flowmapNormal.y * flowmapNormal.y);
611- # endif
612-
698+ float2 baseNormalUv = input.TexCoord1.xy;
613699# if defined (WATER_PARALLAX)
614- float3 normals1 = Normals01Tex.SampleBias (Normals01Sampler, input.TexCoord1.xy + parallaxOffset.xy * normalScalesRcp.x, SharedData::MipBias).xyz * 2.0 + float3 (-1 , -1 , -2 );
615- # else
616- float3 normals1 = Normals01Tex.SampleBias (Normals01Sampler, input.TexCoord1.xy, SharedData::MipBias).xyz * 2.0 + float3 (-1 , -1 , -2 );
700+ // Use flowmap-derived parallax offset for base normals
701+ baseNormalUv += flowmapParallaxOffset.xy * normalScalesRcp.x;
617702# endif
618-
703+ float3 normals1 = Normals01Tex.SampleBias (Normals01Sampler, baseNormalUv, SharedData::MipBias).xyz * 2.0 + float3 (-1 , -1 , -2 );
704+ # endif // End of FLOWMAP block
705+
706+ # if !defined (FLOWMAP)
707+ # if defined (WATER_PARALLAX)
708+ float3 normals1 = Normals01Tex.SampleBias (Normals01Sampler, input.TexCoord1.xy + parallaxOffset.xy * normalScalesRcp.x, SharedData::MipBias).xyz * 2.0 + float3 (-1 , -1 , -2 );
709+ # else
710+ float3 normals1 = Normals01Tex.SampleBias (Normals01Sampler, input.TexCoord1.xy, SharedData::MipBias).xyz * 2.0 + float3 (-1 , -1 , -2 );
711+ # endif
712+ # endif // End of !FLOWMAP block
619713# if defined (FLOWMAP) && !defined (BLEND_NORMALS)
620714# ifdef DISABLE_FLOWMAP_NORMALS
621715 // FLOWMAP NORMALS DISABLED: Using only base normals (flow system still active for ripples/splashes)
0 commit comments