From eb27c679c3de4b0f3326438c2a4b0ff23d7e871b Mon Sep 17 00:00:00 2001
From: shr <saharan.contact@gmail.com>
Date: Thu, 14 Sep 2023 06:28:59 +0900
Subject: [PATCH] update shader

---
 .../shaders/RuntimePostEffectShader.hx        | 92 +++++++++++++++----
 .../graphics/shaders/RuntimeRainShader.hx     | 36 ++++++--
 2 files changed, 100 insertions(+), 28 deletions(-)

diff --git a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx
index b9ec73c76..7488d086f 100644
--- a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx
+++ b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx
@@ -1,38 +1,92 @@
 package funkin.shaderslmfao;
 
+import flixel.FlxCamera;
 import flixel.FlxG;
 import flixel.addons.display.FlxRuntimeShader;
-import flixel.system.FlxAssets.FlxShader;
-import haxe.CallStack;
 import lime.graphics.opengl.GLProgram;
 import lime.utils.Log;
 
 class RuntimePostEffectShader extends FlxRuntimeShader
 {
-  @:glVertexHeader("
-		varying vec2 fragCoord; // normalized texture coord
-		varying vec2 screenPos; // y: always between 0 and 1, x: between 0 and (width/height)
-		uniform vec2 screenResolution;
-	", true)
-  @:glVertexBody("
-		fragCoord = vec2(
+  @:glVertexHeader('
+		// normalized screen coord
+		//   (0, 0) is the top left of the window
+		//   (1, 1) is the bottom right of the window
+		varying vec2 screenCoord;
+	', true)
+  @:glVertexBody('
+		screenCoord = vec2(
 			openfl_TextureCoord.x > 0.0 ? 1.0 : 0.0,
 			openfl_TextureCoord.y > 0.0 ? 1.0 : 0.0
 		);
-		screenPos = fragCoord * vec2(screenResolution.x / screenResolution.y, 1.0);
-	")
-  @:glFragmentHeader("
-		varying vec2 fragCoord;
-		varying vec2 screenPos;
+	')
+  @:glFragmentHeader('
+		// normalized screen coord
+		//   (0, 0) is the top left of the window
+		//   (1, 1) is the bottom right of the window
+		varying vec2 screenCoord;
 
-		vec2 texCoordSize() { // hack
-			return openfl_TextureCoordv / fragCoord;
+		// equals (FlxG.width, FlxG.height)
+		uniform vec2 uScreenResolution;
+
+		// equals (camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom)
+		uniform vec4 uCameraBounds;
+
+		// screen coord -> world coord conversion
+		// returns world coord in px
+		vec2 screenToWorld(vec2 screenCoord) {
+			float left = uCameraBounds.x;
+			float top = uCameraBounds.y;
+			float right = uCameraBounds.z;
+			float bottom = uCameraBounds.w;
+			vec2 scale = vec2(right - left, bottom - top);
+			vec2 offset = vec2(left, top);
+			return screenCoord * scale + offset;
 		}
-	", true)
+
+		// world coord -> screen coord conversion
+		// returns normalized screen coord
+		vec2 worldToScreen(vec2 worldCoord) {
+			float left = uCameraBounds.x;
+			float top = uCameraBounds.y;
+			float right = uCameraBounds.z;
+			float bottom = uCameraBounds.w;
+			vec2 scale = vec2(right - left, bottom - top);
+			vec2 offset = vec2(left, top);
+			return (worldCoord - offset) / scale;
+		}
+
+		// internally used to get the maximum `openfl_TextureCoordv`
+		vec2 bitmapCoordScale() {
+			return openfl_TextureCoordv / screenCoord;
+		}
+
+		// internally used to compute bitmap coord
+		vec2 screenToBitmap(vec2 screenCoord) {
+			return screenCoord * bitmapCoordScale();
+		}
+
+		// samples the frame buffer using a screen coord
+		vec4 sampleBitmapScreen(vec2 screenCoord) {
+			return texture2D(bitmap, screenToBitmap(screenCoord));
+		}
+
+		// samples the frame buffer using a world coord
+		vec4 sampleBitmapWorld(vec2 worldCoord) {
+			return sampleBitmapScreen(worldToScreen(worldCoord));
+		}
+	', true)
   public function new(fragmentSource:String = null, glVersion:String = null)
   {
     super(fragmentSource, null, glVersion);
-    screenResolution.value = [FlxG.width, FlxG.height];
+    uScreenResolution.value = [FlxG.width, FlxG.height];
+  }
+
+  // basically `updateViewInfo(FlxG.width, FlxG.height, FlxG.camera)` is good
+  public function updateViewInfo(screenWidth:Float, screenHeight:Float, camera:FlxCamera):Void
+  {
+    uScreenResolution.value = [screenWidth, screenHeight];
+    uCameraBounds.value = [camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom];
   }
 
   override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
@@ -44,7 +98,7 @@ class RuntimePostEffectShader extends FlxRuntimeShader
     }
     catch (error)
     {
-      Log.warn(error);
+      Log.warn(error); // prevent the app from dying immediately
       return null;
     }
   }
diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx
index cab0a8964..1150e4002 100644
--- a/source/funkin/graphics/shaders/RuntimeRainShader.hx
+++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx
@@ -24,8 +24,6 @@ class RuntimeRainShader extends RuntimePostEffectShader
       radius:ShaderParameter<Float>,
     }>;
 
-  // This is a property, whenever the value is set it calls the set_time function.
-  // This makes the code cleaner elsewhere.
   public var time(default, set):Float = 1;
 
   function set_time(value:Float):Float
@@ -34,13 +32,33 @@ class RuntimeRainShader extends RuntimePostEffectShader
     return time = value;
   }
 
+  // The scale of the rain depends on the world coordinate system, so higher resolution makes
+  // the raindrops smaller. This parameter can be used to adjust the total scale of the scene.
+  // The size of the raindrops is proportional to the value of this parameter.
+  public var scale(default, set):Float = 1;
+
+  function set_scale(value:Float):Float
+  {
+    this.setFloat('uScale', value);
+    return scale = value;
+  }
+
+  // The intensity of the rain. Zero means no rain and one means the maximum amount of rain.
+  public var intensity(default, set):Float = 1;
+
+  function set_intensity(value:Float):Float
+  {
+    this.setFloat('uIntensity', value);
+    return intensity = value;
+  }
+
   public var puddleMap(default, set):BitmapData;
 
   public var groundMap(default, set):BitmapData;
 
   function set_groundMap(value:BitmapData):BitmapData
   {
-    trace("groundmap set");
+    trace('groundmap set');
     this.setBitmapData('uGroundMap', value);
     // this.setFloat2('uPuddleTextureSize', value.width, value.height);
     return groundMap = value;
@@ -56,7 +74,7 @@ class RuntimeRainShader extends RuntimePostEffectShader
 
   function set_lightMap(value:BitmapData):BitmapData
   {
-    trace("lightmap set");
+    trace('lightmap set');
     this.setBitmapData('uLightMap', value);
     return lightMap = value;
   }
@@ -71,7 +89,7 @@ class RuntimeRainShader extends RuntimePostEffectShader
 
   public function new()
   {
-    super(Assets.getText(Paths.frag("rain")));
+    super(Assets.getText(Paths.frag('rain')));
   }
 
   public function update(elapsed:Float):Void
@@ -82,14 +100,14 @@ class RuntimeRainShader extends RuntimePostEffectShader
   override function __processGLData(source:String, storageType:String):Void
   {
     super.__processGLData(source, storageType);
-    if (storageType == "uniform")
+    if (storageType == 'uniform')
     {
       lights = [
         for (i in 0...MAX_LIGHTS)
           {
-            position: addFloatUniform("lights[" + i + "].position", 2),
-            color: addFloatUniform("lights[" + i + "].color", 3),
-            radius: addFloatUniform("lights[" + i + "].radius", 1),
+            position: addFloatUniform('lights[$i].position', 2),
+            color: addFloatUniform('lights[$i].color', 3),
+            radius: addFloatUniform('lights[$i].radius', 1),
           }
       ];
     }