Bladeren bron

Fix the Julia set shader example (#3467)

* Simplify POI selection

* Improve mouse logic

* Add colour cycles to the shader to show finer details. Works well with high iteration numbers

* Testing things...

* Actually fix zoom. Also allow user to reset camera with 'R'

* Reset max iterations

* Tidying & comments

* Revert to original if statement

* Make mouse logic more readable

* Style conventions

* Coding conventions - f postifx on floating points

* Missed a few f postfixes
Josh Colclough 1 jaar geleden
bovenliggende
commit
975d4154e6

+ 17 - 15
examples/shaders/resources/shaders/glsl100/julia_set.fs

@@ -6,30 +6,30 @@ precision mediump float;
 varying vec2 fragTexCoord;
 varying vec4 fragColor;
 
-uniform vec2 screenDims;        // Dimensions of the screen
 uniform vec2 c;                 // c.x = real, c.y = imaginary component. Equation done is z^2 + c
 uniform vec2 offset;            // Offset of the scale.
 uniform float zoom;             // Zoom of the scale.
 
 // NOTE: Maximum number of shader for-loop iterations depend on GPU,
 // for example, on RasperryPi for this examply only supports up to 60
-const int MAX_ITERATIONS = 48;  // Max iterations to do
+const int maxIterations = 48;     // Max iterations to do.
+const float colorCycles = 1.0f;   // Number of times the color palette repeats.
 
 // Square a complex number
 vec2 ComplexSquare(vec2 z)
 {
     return vec2(
-        z.x * z.x - z.y * z.y,
-        z.x * z.y * 2.0
+        z.x*z.x - z.y*z.y,
+        z.x*z.y*2.0f
     );
 }
 
 // Convert Hue Saturation Value (HSV) color into RGB
 vec3 Hsv2rgb(vec3 c)
 {
-    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
-    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
-    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+    vec4 K = vec4(1.0f, 2.0f/3.0f, 1.0f/3.0f, 3.0f);
+    vec3 p = abs(fract(c.xxx + K.xyz)*6.0f - K.www);
+    return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y);
 }
 
 void main()
@@ -45,8 +45,8 @@ void main()
 
       If the number is below 2, we keep iterating.
       But when do we stop iterating if the number is always below 2 (it converges)?
-      That is what MAX_ITERATIONS is for.
-      Then we can divide the iterations by the MAX_ITERATIONS value to get a normalized value that we can
+      That is what maxIterations is for.
+      Then we can divide the iterations by the maxIterations value to get a normalized value that we can
       then map to a color.
 
       We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared.
@@ -55,13 +55,15 @@ void main()
 
     // The pixel coordinates are scaled so they are on the mandelbrot scale
     // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom
-    vec2 z = vec2((fragTexCoord.x + offset.x/screenDims.x)*2.5/zoom, (fragTexCoord.y + offset.y/screenDims.y)*1.5/zoom);
+    vec2 z = vec2((fragTexCoord.x - 0.5f)*2.5f, (fragTexCoord.y - 0.5f)*1.5f)/zoom;
+    z.x += offset.x;
+    z.y += offset.y;
 
     int iter = 0;
     for (int iterations = 0; iterations < 60; iterations++)
     {
         z = ComplexSquare(z) + c;  // Iterate function
-        if (dot(z, z) > 4.0) break;
+        if (dot(z, z) > 4.0f) break;
 
         iter = iterations;
     }
@@ -72,12 +74,12 @@ void main()
     z = ComplexSquare(z) + c;
 
     // This last part smooths the color (again see link above).
-    float smoothVal = float(iter) + 1.0 - (log(log(length(z)))/log(2.0));
+    float smoothVal = float(iter) + 1.0f - (log(log(length(z)))/log(2.0f));
 
     // Normalize the value so it is between 0 and 1.
-    float norm = smoothVal/float(MAX_ITERATIONS);
+    float norm = smoothVal/float(maxIterations);
 
     // If in set, color black. 0.999 allows for some float accuracy error.
-    if (norm > 0.999) gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
-    else gl_FragColor = vec4(Hsv2rgb(vec3(norm, 1.0, 1.0)), 1.0);
+    if (norm > 0.999f) gl_FragColor = vec4(0.0f, 0.0f, 0.0f, 1.0f);
+    else gl_FragColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0f, 1.0f)), 1.0f);
 }

+ 18 - 16
examples/shaders/resources/shaders/glsl330/julia_set.fs

@@ -7,28 +7,28 @@ in vec4 fragColor;
 // Output fragment color
 out vec4 finalColor;
 
-uniform vec2 screenDims;        // Dimensions of the screen
 uniform vec2 c;                 // c.x = real, c.y = imaginary component. Equation done is z^2 + c
 uniform vec2 offset;            // Offset of the scale.
 uniform float zoom;             // Zoom of the scale.
 
-const int MAX_ITERATIONS = 255; // Max iterations to do.
+const int maxIterations = 255;     // Max iterations to do.
+const float colorCycles = 2.0f;    // Number of times the color palette repeats. Can show higher detail for higher iteration numbers.
 
 // Square a complex number
 vec2 ComplexSquare(vec2 z)
 {
     return vec2(
-        z.x * z.x - z.y * z.y,
-        z.x * z.y * 2.0
+        z.x*z.x - z.y*z.y,
+        z.x*z.y*2.0f
     );
 }
 
 // Convert Hue Saturation Value (HSV) color into RGB
 vec3 Hsv2rgb(vec3 c)
 {
-    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
-    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
-    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+    vec4 K = vec4(1.0f, 2.0f/3.0f, 1.0f/3.0f, 3.0f);
+    vec3 p = abs(fract(c.xxx + K.xyz)*6.0f - K.www);
+    return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y);
 }
 
 void main()
@@ -44,8 +44,8 @@ void main()
 
       If the number is below 2, we keep iterating.
       But when do we stop iterating if the number is always below 2 (it converges)?
-      That is what MAX_ITERATIONS is for.
-      Then we can divide the iterations by the MAX_ITERATIONS value to get a normalized value that we can
+      That is what maxIterations is for.
+      Then we can divide the iterations by the maxIterations value to get a normalized value that we can
       then map to a color.
 
       We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared.
@@ -54,14 +54,16 @@ void main()
 
     // The pixel coordinates are scaled so they are on the mandelbrot scale
     // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom
-    vec2 z = vec2((fragTexCoord.x + offset.x/screenDims.x)*2.5/zoom, (fragTexCoord.y + offset.y/screenDims.y)*1.5/zoom);
+    vec2 z = vec2((fragTexCoord.x - 0.5f)*2.5f, (fragTexCoord.y - 0.5f)*1.5f)/zoom;
+    z.x += offset.x;
+    z.y += offset.y;
 
     int iterations = 0;
-    for (iterations = 0; iterations < MAX_ITERATIONS; iterations++)
+    for (iterations = 0; iterations < maxIterations; iterations++)
     {
         z = ComplexSquare(z) + c;  // Iterate function
 
-        if (dot(z, z) > 4.0) break;
+        if (dot(z, z) > 4.0f) break;
     }
 
     // Another few iterations decreases errors in the smoothing calculation.
@@ -70,12 +72,12 @@ void main()
     z = ComplexSquare(z) + c;
 
     // This last part smooths the color (again see link above).
-    float smoothVal = float(iterations) + 1.0 - (log(log(length(z)))/log(2.0));
+    float smoothVal = float(iterations) + 1.0f - (log(log(length(z)))/log(2.0f));
 
     // Normalize the value so it is between 0 and 1.
-    float norm = smoothVal/float(MAX_ITERATIONS);
+    float norm = smoothVal/float(maxIterations);
 
     // If in set, color black. 0.999 allows for some float accuracy error.
-    if (norm > 0.999) finalColor = vec4(0.0, 0.0, 0.0, 1.0);
-    else finalColor = vec4(Hsv2rgb(vec3(norm, 1.0, 1.0)), 1.0);
+    if (norm > 0.999f) finalColor = vec4(0.0f, 0.0f, 0.0f, 1.0f);
+    else finalColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0f, 1.0f)), 1.0f);
 }

+ 51 - 45
examples/shaders/shaders_julia_set.c

@@ -9,12 +9,12 @@
 *
 *   Example originally created with raylib 2.5, last time updated with raylib 4.0
 *
-*   Example contributed by eggmund (@eggmund) and reviewed by Ramon Santamaria (@raysan5)
+*   Example contributed by Josh Colclough (@joshcol9232) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2023 eggmund (@eggmund) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2023 Josh Colclough (@joshcol9232) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -37,6 +37,13 @@ const float pointsOfInterest[6][2] =
     { -0.70176f, -0.3842f },
 };
 
+const int screenWidth = 800;
+const int screenHeight = 450;
+const float zoomSpeed = 1.01f;
+const float offsetSpeedMul = 2.0f;
+
+const float startingZoom = 0.75f;
+
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
@@ -44,10 +51,6 @@ int main(void)
 {
     // Initialization
     //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    //SetConfigFlags(FLAG_WINDOW_HIGHDPI);
     InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia sets");
 
     // Load julia set shader
@@ -61,10 +64,8 @@ int main(void)
     float c[2] = { pointsOfInterest[0][0], pointsOfInterest[0][1] };
 
     // Offset and zoom to draw the julia set at. (centered on screen and default size)
-    float offset[2] = { -(float)GetScreenWidth()/2, -(float)GetScreenHeight()/2 };
-    float zoom = 1.0f;
-
-    Vector2 offsetSpeed = { 0.0f, 0.0f };
+    float offset[2] = { 0.0f, 0.0f };
+    float zoom = startingZoom;
 
     // Get variable (uniform) locations on the shader to connect with the program
     // NOTE: If uniform variable could not be found in the shader, function returns -1
@@ -72,17 +73,13 @@ int main(void)
     int zoomLoc = GetShaderLocation(shader, "zoom");
     int offsetLoc = GetShaderLocation(shader, "offset");
 
-    // Tell the shader what the screen dimensions, zoom, offset and c are
-    float screenDims[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() };
-    SetShaderValue(shader, GetShaderLocation(shader, "screenDims"), screenDims, SHADER_UNIFORM_VEC2);
-
+    // Upload the shader uniform values!
     SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2);
     SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT);
     SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2);
 
     int incrementSpeed = 0;             // Multiplier of speed to change c value
     bool showControls = true;           // Show controls
-    bool pause = false;                 // Pause animation
 
     SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
@@ -110,42 +107,50 @@ int main(void)
             SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2);
         }
 
-        if (IsKeyPressed(KEY_SPACE)) pause = !pause;                 // Pause animation (c change)
-        if (IsKeyPressed(KEY_F1)) showControls = !showControls;  // Toggle whether or not to show controls
-
-        if (!pause)
+        // If "R" is pressed, reset zoom and offset.
+        if (IsKeyPressed(KEY_R))
         {
-            if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++;
-            else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--;
-
-            // TODO: The idea is to zoom and move around with mouse
-            // Probably offset movement should be proportional to zoom level
-            if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
-            {
-                if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) zoom += zoom*0.003f;
-                if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) zoom -= zoom*0.003f;
-
-                Vector2 mousePos = GetMousePosition();
+            zoom = startingZoom;
+            offset[0] = 0.0f;
+            offset[1] = 0.0f;
+            SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT);
+            SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2);
+        }
 
-                offsetSpeed.x = mousePos.x -(float)screenWidth/2;
-                offsetSpeed.y = mousePos.y -(float)screenHeight/2;
+        if (IsKeyPressed(KEY_SPACE)) incrementSpeed = 0;         // Pause animation (c change)
+        if (IsKeyPressed(KEY_F1)) showControls = !showControls;  // Toggle whether or not to show controls
 
-                // Slowly move camera to targetOffset
-                offset[0] += GetFrameTime()*offsetSpeed.x*0.8f;
-                offset[1] += GetFrameTime()*offsetSpeed.y*0.8f;
-            }
-            else offsetSpeed = (Vector2){ 0.0f, 0.0f };
+        if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++;
+        else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--;
 
+        // If either left or right button is pressed, zoom in/out.
+        if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
+        {
+            // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out.
+            zoom *= IsMouseButtonDown(MOUSE_BUTTON_LEFT)? zoomSpeed : 1.0f/zoomSpeed;
+
+            const Vector2 mousePos = GetMousePosition();
+            Vector2 offsetVelocity;
+            // Find the velocity at which to change the camera. Take the distance of the mouse
+            // from the center of the screen as the direction, and adjust magnitude based on
+            // the current zoom.
+            offsetVelocity.x = (mousePos.x/(float)screenWidth - 0.5f)*offsetSpeedMul/zoom;
+            offsetVelocity.y = (mousePos.y/(float)screenHeight - 0.5f)*offsetSpeedMul/zoom;
+
+            // Apply move velocity to camera
+            offset[0] += GetFrameTime()*offsetVelocity.x;
+            offset[1] += GetFrameTime()*offsetVelocity.y;
+
+            // Update the shader uniform values!
             SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT);
             SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2);
-
-            // Increment c value with time
-            float amount = GetFrameTime()*incrementSpeed*0.0005f;
-            c[0] += amount;
-            c[1] += amount;
-
-            SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2);
         }
+
+        // Increment c value with time
+        const float dc = GetFrameTime()*(float)incrementSpeed*0.0005f;
+        c[0] += dc;
+        c[1] += dc;
+        SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2);
         //----------------------------------------------------------------------------------
 
         // Draw
@@ -178,7 +183,8 @@ int main(void)
                 DrawText("Press KEY_F1 to toggle these controls", 10, 30, 10, RAYWHITE);
                 DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, RAYWHITE);
                 DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, RAYWHITE);
-                DrawText("Press KEY_SPACE to pause movement animation", 10, 75, 10, RAYWHITE);
+                DrawText("Press KEY_SPACE to stop movement animation", 10, 75, 10, RAYWHITE);
+                DrawText("Press KEY_R to recenter the camera", 10, 90, 10, RAYWHITE);
             }
         EndDrawing();
         //----------------------------------------------------------------------------------