From 30bcc7d6d5fa4ceedfec28cc0973c253364c555e Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Wed, 5 Oct 2022 08:11:33 -0500
Subject: [PATCH] SL-18190 WIP - Add refraction to water without splitting
 scene between above and below water

---
 indra/llrender/llrendertarget.cpp             |  2 +
 .../shaders/class1/deferred/deferredUtil.glsl | 15 ++--
 .../shaders/class1/environment/waterFogF.glsl | 42 ++++++++++
 .../shaders/class1/environment/waterV.glsl    |  1 +
 .../shaders/class2/environment/waterF.glsl    | 82 +++++++++++--------
 indra/newview/lldrawpool.h                    |  3 +
 indra/newview/lldrawpoolwater.cpp             | 20 +++++
 indra/newview/lldrawpoolwater.h               |  2 +
 indra/newview/llsurface.cpp                   |  7 ++
 indra/newview/llviewerdisplay.cpp             |  4 +-
 indra/newview/llviewerregion.cpp              |  3 -
 indra/newview/llviewershadermgr.cpp           |  2 +
 indra/newview/pipeline.cpp                    |  3 +-
 indra/newview/pipeline.h                      |  2 +-
 14 files changed, 141 insertions(+), 47 deletions(-)

diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index b0da054d760..7fcb130ac6a 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -623,6 +623,8 @@ void LLRenderTarget::flush(bool fetch_depth)
 void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, S32 srcX1, S32 srcY1,
 						S32 dstX0, S32 dstY0, S32 dstX1, S32 dstY1, U32 mask, U32 filter)
 {
+    LL_PROFILE_GPU_ZONE("LLRenderTarget::copyContents");
+
 	GLboolean write_depth = mask & GL_DEPTH_BUFFER_BIT ? TRUE : FALSE;
 
 	LLGLDepthTest depth(write_depth, write_depth);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
index bca4771c273..1c2034de693 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
@@ -310,16 +310,21 @@ vec4 getPosition(vec2 pos_screen)
     return pos;
 }
 
+// get position given a normalized device coordinate
+vec3 getPositionWithNDC(vec3 ndc)
+{
+    vec4 pos = inv_proj * vec4(ndc, 1.0);
+    return pos.xyz / pos.w;
+}
+
 vec4 getPositionWithDepth(vec2 pos_screen, float depth)
 {
     vec2 sc = getScreenCoordinate(pos_screen);
-    vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
-    vec4 pos = inv_proj * ndc;
-    pos /= pos.w;
-    pos.w = 1.0;
-    return pos;
+    vec3 ndc = vec3(sc.x, sc.y, 2.0*depth-1.0);
+    return vec4(getPositionWithNDC(ndc), 1.0);
 }
 
+
 vec2 getScreenXY(vec4 clip)
 {
     vec4 ndc = clip;
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterFogF.glsl b/indra/newview/app_settings/shaders/class1/environment/waterFogF.glsl
index df640cba05e..7dbba125025 100644
--- a/indra/newview/app_settings/shaders/class1/environment/waterFogF.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/waterFogF.glsl
@@ -32,6 +32,8 @@ uniform float waterFogKS;
 
 vec3 getPositionEye();
 
+vec3 srgb_to_linear(vec3 col);
+
 vec4 applyWaterFogView(vec3 pos, vec4 color)
 {
     vec3 view = normalize(pos);
@@ -71,6 +73,46 @@ vec4 applyWaterFogView(vec3 pos, vec4 color)
     return color;
 }
 
+vec4 applyWaterFogViewLinear(vec3 pos, vec4 color)
+{
+    vec3 view = normalize(pos);
+    //normalize view vector
+    float es = -(dot(view, waterPlane.xyz));
+
+    //find intersection point with water plane and eye vector
+
+    //get eye depth
+    float e0 = max(-waterPlane.w, 0.0);
+
+    vec3 int_v = waterPlane.w > 0.0 ? view * waterPlane.w / es : vec3(0.0, 0.0, 0.0);
+
+    //get object depth
+    float depth = length(pos - int_v);
+
+    //get "thickness" of water
+    float l = max(depth, 0.1);
+
+    float kd = waterFogDensity;
+    float ks = waterFogKS;
+    vec4 kc = waterFogColor;
+    kc.rgb = srgb_to_linear(kc.rgb); // TODO -- pass in waterFogColor linear
+
+    float F = 0.98;
+
+    float t1 = -kd * pow(F, ks * e0);
+    float t2 = kd + ks * es;
+    float t3 = pow(F, t2 * l) - 1.0;
+
+    float L = min(t1 / t2 * t3, 1.0);
+
+    float D = pow(0.98, l * kd);
+
+    color.rgb = color.rgb * D + kc.rgb * L;
+    color.a = kc.a + color.a;
+
+    return color;
+}
+
 vec4 applyWaterFog(vec4 color)
 {
     //normalize view vector
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterV.glsl b/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
index e95f268681d..a0a0e7dfcaa 100644
--- a/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
@@ -46,6 +46,7 @@ out vec3 vary_position;
 out vec3 vary_light_dir;
 out vec3 vary_tangent;
 out vec3 vary_normal;
+out vec2 vary_fragcoord;
 
 float wave(vec2 v, float t, float f, vec2 d, float s) 
 {
diff --git a/indra/newview/app_settings/shaders/class2/environment/waterF.glsl b/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
index 2e7efa9b2a8..59e7e64fbbb 100644
--- a/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
+++ b/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
@@ -32,6 +32,7 @@ out vec4 frag_color;
 vec3 scaleSoftClipFragLinear(vec3 l);
 vec3 atmosFragLightingLinear(vec3 light, vec3 additive, vec3 atten);
 void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive);
+vec4 applyWaterFogViewLinear(vec3 pos, vec4 color);
 
 // PBR interface
 vec3 pbrIbl(vec3 diffuseColor,
@@ -53,6 +54,8 @@ uniform sampler2D bumpMap;
 uniform sampler2D bumpMap2;
 uniform float     blend_factor;
 uniform sampler2D screenTex;
+uniform sampler2D screenDepth;
+
 uniform sampler2D refTex;
 
 uniform float sunAngle;
@@ -100,6 +103,8 @@ vec3 transform_normal(vec3 vNt)
 void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv,
     vec3 pos, vec3 norm, float glossiness);
 
+vec3 getPositionWithNDC(vec3 ndc);
+
 void main() 
 {
 	vec4 color;
@@ -110,7 +115,7 @@ void main()
 
     vec3 pos = vary_position.xyz;
 
-	float dist = length(view.xy);
+	float dist = length(pos.xyz);
 	
 	//normalize view vector
 	vec3 viewVec = normalize(pos.xyz);
@@ -121,7 +126,6 @@ void main()
     vec3 wave2_a = texture2D(bumpMap, littleWave.xy).xyz*2.0-1.0;
     vec3 wave3_a = texture2D(bumpMap, littleWave.zw).xyz*2.0-1.0;
 
-
     vec3 wave1_b = texture2D(bumpMap2, bigwave      ).xyz*2.0-1.0;
     vec3 wave2_b = texture2D(bumpMap2, littleWave.xy).xyz*2.0-1.0;
     vec3 wave3_b = texture2D(bumpMap2, littleWave.zw).xyz*2.0-1.0;
@@ -142,11 +146,11 @@ void main()
 
 	//get base fresnel components	
 	
-	/*vec3 df = vec3(
+	vec3 df = vec3(
 					dot(viewVec, wave1),
 					dot(viewVec, (wave2 + wave3) * 0.5),
 					dot(viewVec, wave3)
-				 ) * fresnelScale + fresnelOffset;*/
+				 ) * fresnelScale + fresnelOffset;
 		    
 	vec2 distort = (refCoord.xy/refCoord.z) * 0.5 + 0.5;
 	
@@ -157,20 +161,33 @@ void main()
 	
 	vec2 dmod_scale = vec2(dmod*dmod, dmod);
 	
-    //float df1 = df.x + df.y + df.z;
+    float df1 = df.x + df.y + df.z;
 
-    vec4 refcol = vec4(0, 0, 0, 0);
+    wavef = normalize(wavef + vary_normal);
+    //wavef = vary_normal;
 
-	//get specular component
-	float spec = clamp(dot(vary_light_dir, (reflect(viewVec,wavef))),0.0,1.0);
-		
-	//harden specular
-	spec = pow(spec, 128.0);
+    vec3 waver = reflect(viewVec, -wavef)*3;
 
 	//figure out distortion vector (ripply)   
-	//vec2 distort2 = distort+wavef.xy*refScale/max(dmod*df1, 1.0);
-		
-	//vec4 fb = texture2D(screenTex, distort2);
+    vec2 distort2 = distort + waver.xy * refScale / max(dmod * df1, 1.0);
+    distort2 = clamp(distort2, vec2(0), vec2(0.99));
+ 
+    vec4 fb = texture2D(screenTex, distort2);
+    float depth = texture2D(screenDepth, distort2).r;
+    vec3 refPos = getPositionWithNDC(vec3(distort2*2.0-vec2(1.0), depth*2.0-1.0));
+
+#if 1
+    if (refPos.z > pos.z-0.05)
+    {
+        //we sampled an above water sample, don't distort
+        distort2 = distort;
+        fb = texture2D(screenTex, distort2);
+        depth = texture2D(screenDepth, distort2).r;
+        refPos = getPositionWithNDC(vec3(distort2 * 2.0 - vec2(1.0), depth * 2.0 - 1.0));
+    }
+#endif
+
+    fb = applyWaterFogViewLinear(refPos, fb);
 
     vec3 sunlit;
     vec3 amblit;
@@ -186,48 +203,43 @@ void main()
     float roughness = 0.08;
     float gloss = 1.0 - roughness;
 
-    vec3 baseColor = vec3(0);
+    vec3 baseColor = vec3(0.25);
     vec3 f0 = vec3(0.04);
     vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
     diffuseColor *= gloss;
 
     vec3 specularColor = mix(f0, baseColor.rgb, metallic);
 
+    //vec3 refnorm = normalize(wavef + vary_normal);
+    vec3 refnorm = wavef;
     
-    vec3 refnorm = normalize(wavef + vary_normal);
-
+   
     vec3 irradiance = vec3(0);
     vec3 radiance = vec3(0);
     sampleReflectionProbes(irradiance, radiance, pos, refnorm, gloss);
-    
-    // fudge -- use refracted color as irradiance
-    irradiance = srgb_to_linear(waterFogColor.rgb);
+    radiance *= 0.5;
+    irradiance = fb.rgb;
 
     color.rgb = pbrIbl(diffuseColor, specularColor, radiance, irradiance, gloss, NdotV, 0.0);
 
-    //vec4 fb = waterFogColor;
-    //fb.rgb = srgb_to_linear(fb.rgb);
-	
-    //refcol.rgb = vec3(0, 1, 0);
-
-	//mix with reflection
-	//color.rgb = mix(fb.rgb, refcol.rgb, df1);
-	
-    //color.rgb += spec * specular;
-
+    
     // fudge -- for punctual lighting, pretend water is metallic
     diffuseColor = vec3(0);
     specularColor = vec3(1);
     roughness = 0.1;
-    color.rgb += pbrPunctual(diffuseColor, specularColor, roughness, metallic, wavef, v, vary_light_dir);
+    float scol = 1.0; // TODO -- incorporate shadow map
+
+    //color.rgb += pbrPunctual(diffuseColor, specularColor, roughness, metallic, wavef, v, vary_light_dir) * sunlit * 2.75 * scol;
 	color.rgb = atmosFragLightingLinear(color.rgb, additive, atten);
 	color.rgb = scaleSoftClipFragLinear(color.rgb);
 
     color.a = 0.f;
-
-	//color.a = spec * sunAngle2;
-
-    frag_color = color;
+    //color.rgb = fb.rgb;
+    //color.rgb = vec3(depth*depth*depth*depth);
+    //color.rgb = srgb_to_linear(normalize(refPos) * 0.5 + 0.5);
+    //color.rgb = srgb_to_linear(normalize(pos) * 0.5 + 0.5);
+    //color.rgb = srgb_to_linear(wavef * 0.5 + 0.5);
+	frag_color = color;
 
 #if defined(WATER_EDGE)
     gl_FragDepth = 0.9999847f;
diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h
index f7fc2d20611..0fcb2b2e585 100644
--- a/indra/newview/lldrawpool.h
+++ b/indra/newview/lldrawpool.h
@@ -48,6 +48,9 @@ class LLDrawPool
 	enum
 	{
 		// Correspond to LLPipeline render type
+        // Also controls render order, so passes that don't use alpha masking/blending should come before
+        // other passes and occlusion culling should happen just before rendering alpha masked passes
+        // in order to take advantage of hierarchical Z
         // NOTE: Keep in sync with gPoolNames
 		POOL_SIMPLE = 1,
 		POOL_GROUND,
diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp
index 23de00e3a43..0b9bca06db4 100644
--- a/indra/newview/lldrawpoolwater.cpp
+++ b/indra/newview/lldrawpoolwater.cpp
@@ -122,6 +122,19 @@ S32 LLDrawPoolWater::getNumPostDeferredPasses()
     return 1;
 }
 
+void LLDrawPoolWater::beginPostDeferredPass(S32 pass)
+{
+    // copy framebuffer contents so far to a texture to be used for
+    // reflections and refractions
+    LLRenderTarget& src = gPipeline.mRT->screen;
+    LLRenderTarget& dst = gPipeline.mWaterDis;
+    dst.copyContents(src, 
+        0, 0, src.getWidth(), src.getHeight(), 
+        0, 0, dst.getWidth(), dst.getHeight(), 
+        GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, 
+        GL_NEAREST);
+}
+
 void LLDrawPoolWater::renderPostDeferred(S32 pass) 
 {
     renderWater();
@@ -587,6 +600,8 @@ void LLDrawPoolWater::renderWater()
 
         // bind reflection texture from RenderTarget
         S32 screentex   = shader->enableTexture(LLShaderMgr::WATER_SCREENTEX);
+        S32 screenDepth = shader->enableTexture(LLShaderMgr::WATER_SCREENDEPTH);
+
         F32 screenRes[] = {1.f / gGLViewport[2], 1.f / gGLViewport[3]};
 
         S32 diffTex = shader->enableTexture(LLShaderMgr::DIFFUSE_MAP);
@@ -613,6 +628,11 @@ void LLDrawPoolWater::renderWater()
             gGL.getTexUnit(screentex)->bind(&gPipeline.mWaterDis);
         }
 
+        if (screenDepth > -1)
+        {
+            gGL.getTexUnit(screenDepth)->bind(&gPipeline.mWaterDis, true);
+        }
+
         if (mShaderLevel == 1)
         {
             fog_color.mV[VW] = log(fog_density) / log(2);
diff --git a/indra/newview/lldrawpoolwater.h b/indra/newview/lldrawpoolwater.h
index e3073018c33..39f25541efe 100644
--- a/indra/newview/lldrawpoolwater.h
+++ b/indra/newview/lldrawpoolwater.h
@@ -63,7 +63,9 @@ class LLDrawPoolWater final: public LLFacePool
 
 	static void restoreGL();
 	
+    
     S32 getNumPostDeferredPasses() override;
+    void beginPostDeferredPass(S32 pass) override;
     void renderPostDeferred(S32 pass) override;
     S32 getNumDeferredPasses() override;
 	void renderDeferred(S32 pass = 0) override;
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index ea36e1d7be0..1418499f8b3 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -681,6 +681,13 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
 			}
 		}
 	}
+
+    if (did_update)
+    {
+        // some patches changed, update region reflection probes
+        mRegionp->updateReflectionProbes();
+    }
+
 	return did_update;
 }
 
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 65f1170eb43..118dbb833ed 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -1007,7 +1007,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 
         if (LLPipeline::sRenderDeferred)
         {
-			gPipeline.renderDeferredLighting(&gPipeline.mRT->screen);
+			gPipeline.renderDeferredLighting();
 		}
 
 		LLPipeline::sUnderWaterRender = FALSE;
@@ -1146,7 +1146,7 @@ void display_cube_face()
 
     gPipeline.mRT->deferredScreen.flush();
        
-    gPipeline.renderDeferredLighting(&gPipeline.mRT->screen);
+    gPipeline.renderDeferredLighting();
 
     LLPipeline::sUnderWaterRender = FALSE;
 
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 19f8cc105ea..628d0ecc6ac 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -830,9 +830,6 @@ void LLViewerRegion::sendReliableMessage()
 void LLViewerRegion::setWaterHeight(F32 water_level)
 {
 	mImpl->mLandp->setWaterHeight(water_level);
-
-    // reflection probes move with the water height
-    updateReflectionProbes();
 }
 
 F32 LLViewerRegion::getWaterHeight() const
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index f2b01808a56..46abd83449d 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -1021,6 +1021,7 @@ BOOL LLViewerShaderMgr::loadShadersWater()
 		gWaterProgram.mName = "Water Shader";
 		gWaterProgram.mFeatures.calculatesAtmospherics = true;
         gWaterProgram.mFeatures.hasAtmospherics = true;
+        gWaterProgram.mFeatures.hasWaterFog = true;
 		gWaterProgram.mFeatures.hasGamma = true;
 		gWaterProgram.mFeatures.hasTransport = true;
         gWaterProgram.mFeatures.hasSrgb = true;
@@ -1040,6 +1041,7 @@ BOOL LLViewerShaderMgr::loadShadersWater()
 		gWaterEdgeProgram.mName = "Water Edge Shader";
 		gWaterEdgeProgram.mFeatures.calculatesAtmospherics = true;
         gWaterEdgeProgram.mFeatures.hasAtmospherics = true;
+        gWaterEdgeProgram.mFeatures.hasWaterFog = true;
 		gWaterEdgeProgram.mFeatures.hasGamma = true;
 		gWaterEdgeProgram.mFeatures.hasTransport = true;
         gWaterEdgeProgram.mFeatures.hasSrgb = true;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 0da2faae293..08c99266f84 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -8442,7 +8442,7 @@ LLVector4 pow4fsrgb(LLVector4 v, F32 f)
 	return v;
 }
 
-void LLPipeline::renderDeferredLighting(LLRenderTarget *screen_target)
+void LLPipeline::renderDeferredLighting()
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
     LL_PROFILE_GPU_ZONE("renderDeferredLighting");
@@ -8451,6 +8451,7 @@ void LLPipeline::renderDeferredLighting(LLRenderTarget *screen_target)
         return;
     }
 
+    LLRenderTarget *screen_target         = &mRT->screen;
     LLRenderTarget *deferred_target       = &mRT->deferredScreen;
     LLRenderTarget *deferred_depth_target = &mRT->deferredDepth;
     LLRenderTarget *deferred_light_target = &mRT->deferredLight;
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 0b6b7d91ae9..a31df9c16fe 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -306,7 +306,7 @@ class LLPipeline
 
     
 
-	void renderDeferredLighting(LLRenderTarget* light_target);
+	void renderDeferredLighting();
 	void postDeferredGammaCorrect(LLRenderTarget* screen_target);
 
 	void generateWaterReflection(LLCamera& camera);
-- 
GitLab