diff --git a/examples/shaders/ascii_rendering/ascii.fs b/examples/shaders/ascii_rendering/ascii.fs new file mode 100644 index 00000000..8a6cc01d --- /dev/null +++ b/examples/shaders/ascii_rendering/ascii.fs @@ -0,0 +1,73 @@ +#version 330 + +// Input from the vertex shader +in vec2 fragTexCoord; + +// Output color for the screen +out vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 resolution; + +// Fontsize less then 9 may be not complete +uniform float fontSize; + +float GreyScale(in vec3 col) +{ + return dot(col, vec3(0.2126, 0.7152, 0.0722)); +} + +float GetCharacter(int n, vec2 p) +{ + p = floor(p*vec2(-4.0, 4.0) + 2.5); + + // Check if the coordinate is inside the 5x5 grid (0 to 4) + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) + { + int a = int(round(p.x) + 5.0*round(p.y)); + if (((n >> a) & 1) == 1) + { + return 1.0; + } + } + + return 0.0; // The bit is off, or we are outside the grid +} + +// ----------------------------------------------------------------------------- +// Main shader logic +// ----------------------------------------------------------------------------- + +void main() +{ + vec2 charPixelSize = vec2(fontSize, fontSize); + vec2 uvCellSize = charPixelSize/resolution; + + // The cell size is based on the fontSize set by application + vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize; + + vec3 cellColor = texture(texture0, cellUV).rgb; + + // Gray is used to define what character will be selected to draw + float gray = GreyScale(cellColor); + + int n = 4096; + + // Character set from https://www.shadertoy.com/view/lssGDj + // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ + if (gray > 0.2) n = 65600; // : + if (gray > 0.3) n = 18725316; // v + if (gray > 0.4) n = 15255086; // o + if (gray > 0.5) n = 13121101; // & + if (gray > 0.6) n = 15252014; // 8 + if (gray > 0.7) n = 13195790; // @ + if (gray > 0.8) n = 11512810; // # + + vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0] + + vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0] + + vec3 color = cellColor*GetCharacter(n, p); + + finalColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/ascii_rendering/fudesumi.png b/examples/shaders/ascii_rendering/fudesumi.png new file mode 100644 index 00000000..1bf4ab75 Binary files /dev/null and b/examples/shaders/ascii_rendering/fudesumi.png differ diff --git a/examples/shaders/ascii_rendering/main.go b/examples/shaders/ascii_rendering/main.go new file mode 100644 index 00000000..314d324d --- /dev/null +++ b/examples/shaders/ascii_rendering/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "fmt" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +func main() { + // Initialization + const screenWidth = 800 + const screenHeight = 450 + + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - ascii rendering") + + fudesumi := rl.LoadTexture("fudesumi.png") + raysan := rl.LoadTexture("raysan.png") + shader := rl.LoadShader("", "ascii.fs") + + // These locations are used to send data to the GPU + resolutionLoc := rl.GetShaderLocation(shader, "resolution") + fontSizeLoc := rl.GetShaderLocation(shader, "fontSize") + + // Set the character size for the ASCII effect (Fontsize should be 9 or more) + fontSize := float32(9.0) + + // Send the updated values to the shader + resolution := []float32{float32(screenWidth), float32(screenHeight)} + rl.SetShaderValue(shader, resolutionLoc, resolution, rl.ShaderUniformVec2) + + circlePos := rl.NewVector2(40.0, float32(screenHeight)*0.5) + circleSpeed := float32(1.0) + + // RenderTexture to apply postprocessing + target := rl.LoadRenderTexture(screenWidth, screenHeight) + + rl.SetTargetFPS(60) + + // Main game loop + for !rl.WindowShouldClose() { + // Update + circlePos.X += circleSpeed + if circlePos.X > 200.0 || circlePos.X < 40.0 { + circleSpeed *= -1 // Revert speed + } + + if rl.IsKeyPressed(rl.KeyLeft) && fontSize > 9.0 { + fontSize -= 1.0 // Reduce fontSize + } + if rl.IsKeyPressed(rl.KeyRight) && fontSize < 15.0 { + fontSize += 1.0 // Increase fontSize + } + + // Set fontsize for the shader + rl.SetShaderValue(shader, fontSizeLoc, []float32{fontSize}, rl.ShaderUniformFloat) + + // Draw + rl.BeginTextureMode(target) + + rl.ClearBackground(rl.White) + + // Draw scene in our render texture + rl.DrawTexture(fudesumi, 500, -30, rl.White) + rl.DrawTextureV(raysan, circlePos, rl.White) + + rl.EndTextureMode() + + rl.BeginDrawing() + + rl.ClearBackground(rl.RayWhite) + + rl.BeginShaderMode(shader) + + // Draw scene texture (rendered earlier) to screen with vertical flip + srcRec := rl.NewRectangle(0, 0, float32(target.Texture.Width), -float32(target.Texture.Height)) + rl.DrawTextureRec(target.Texture, srcRec, rl.Vector2Zero(), rl.White) + + rl.EndShaderMode() + + rl.DrawRectangle(0, 0, screenWidth, 40, rl.Black) + rl.DrawText(fmt.Sprintf("Ascii effect - FontSize:%2.0f - [Left] -1 [Right] +1 ", fontSize), 120, 10, 20, rl.LightGray) + rl.DrawFPS(10, 10) + + rl.EndDrawing() + } + + rl.UnloadTexture(fudesumi) + rl.UnloadTexture(raysan) + rl.UnloadShader(shader) + rl.UnloadRenderTexture(target) + + rl.CloseWindow() +} diff --git a/examples/shaders/ascii_rendering/raysan.png b/examples/shaders/ascii_rendering/raysan.png new file mode 100644 index 00000000..36e13ba9 Binary files /dev/null and b/examples/shaders/ascii_rendering/raysan.png differ diff --git a/examples/shaders/color_correction/cat.png b/examples/shaders/color_correction/cat.png new file mode 100644 index 00000000..db56b9ea Binary files /dev/null and b/examples/shaders/color_correction/cat.png differ diff --git a/examples/shaders/color_correction/color_correction.fs b/examples/shaders/color_correction/color_correction.fs new file mode 100644 index 00000000..c489d6df --- /dev/null +++ b/examples/shaders/color_correction/color_correction.fs @@ -0,0 +1,34 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +uniform float contrast; +uniform float saturation; +uniform float brightness; + +// Output fragment color +out vec4 finalColor; + +void main() +{ + vec4 texel = texture(texture0, fragTexCoord); // Get texel color + + // Apply contrast + texel.rgb = (texel.rgb - 0.5f)*(contrast/100.0f + 1.0f) + 0.5f; + + // Apply brightness + texel.rgb = texel.rgb + brightness/100.0f; + + // Apply saturation + float intensity = dot(texel.rgb, vec3(0.299f, 0.587f, 0.114f)); + texel.rgb = (texel.rgb - intensity)*saturation/100.0f + texel.rgb; + + // Output resulting color + finalColor = texel; +} \ No newline at end of file diff --git a/examples/shaders/color_correction/fudesumi.png b/examples/shaders/color_correction/fudesumi.png new file mode 100644 index 00000000..1bf4ab75 Binary files /dev/null and b/examples/shaders/color_correction/fudesumi.png differ diff --git a/examples/shaders/color_correction/main.go b/examples/shaders/color_correction/main.go new file mode 100644 index 00000000..9a7a3535 --- /dev/null +++ b/examples/shaders/color_correction/main.go @@ -0,0 +1,190 @@ +package main + +import ( + "fmt" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +const maxTextures = 4 + +func main() { + // Initialization + const screenWidth = 800 + const screenHeight = 450 + + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - color correction") + + textures := [maxTextures]rl.Texture2D{ + rl.LoadTexture("parrots.png"), + rl.LoadTexture("cat.png"), + rl.LoadTexture("mandrill.png"), + rl.LoadTexture("fudesumi.png"), + } + + shdrColorCorrection := rl.LoadShader("", "color_correction.fs") + + imageIndex := int32(0) + resetButtonClicked := false + + contrast := float32(0.0) + saturation := float32(0.0) + brightness := float32(0.0) + + // Get shader locations + contrastLoc := rl.GetShaderLocation(shdrColorCorrection, "contrast") + saturationLoc := rl.GetShaderLocation(shdrColorCorrection, "saturation") + brightnessLoc := rl.GetShaderLocation(shdrColorCorrection, "brightness") + + // Set initial shader values + rl.SetShaderValue(shdrColorCorrection, contrastLoc, []float32{contrast}, rl.ShaderUniformFloat) + rl.SetShaderValue(shdrColorCorrection, saturationLoc, []float32{saturation}, rl.ShaderUniformFloat) + rl.SetShaderValue(shdrColorCorrection, brightnessLoc, []float32{brightness}, rl.ShaderUniformFloat) + + rl.SetTargetFPS(60) + + // Main game loop + for !rl.WindowShouldClose() { + // Update + // Select texture to draw via keys + if rl.IsKeyPressed(rl.KeyOne) { + imageIndex = 0 + } else if rl.IsKeyPressed(rl.KeyTwo) { + imageIndex = 1 + } else if rl.IsKeyPressed(rl.KeyThree) { + imageIndex = 2 + } else if rl.IsKeyPressed(rl.KeyFour) { + imageIndex = 3 + } + + // Reset values to 0 + if rl.IsKeyPressed(rl.KeyR) || resetButtonClicked { + contrast = 0.0 + saturation = 0.0 + brightness = 0.0 + } + + // Send values to shader + rl.SetShaderValue(shdrColorCorrection, contrastLoc, []float32{contrast}, rl.ShaderUniformFloat) + rl.SetShaderValue(shdrColorCorrection, saturationLoc, []float32{saturation}, rl.ShaderUniformFloat) + rl.SetShaderValue(shdrColorCorrection, brightnessLoc, []float32{brightness}, rl.ShaderUniformFloat) + + // Draw + rl.BeginDrawing() + + rl.ClearBackground(rl.RayWhite) + + rl.BeginShaderMode(shdrColorCorrection) + + currTex := textures[imageIndex] + posX := int32(580/2) - currTex.Width/2 + posY := int32(rl.GetScreenHeight()/2) - currTex.Height/2 + rl.DrawTexture(currTex, posX, posY, rl.White) + + rl.EndShaderMode() + + rl.DrawLine(580, 0, 580, int32(rl.GetScreenHeight()), rl.NewColor(218, 218, 218, 255)) + rl.DrawRectangle(580, 0, int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight()), rl.NewColor(232, 232, 232, 255)) + + // Draw UI info text + rl.DrawText("Color Correction", 585, 40, 20, rl.Gray) + + rl.DrawText("Picture", 602, 75, 10, rl.Gray) + rl.DrawText("Press [1] - [4] to Change Picture", 600, 230, 8, rl.Gray) + rl.DrawText("Press [R] to Reset Values", 600, 250, 8, rl.Gray) + + // Draw GUI controls using pure Raylib helpers + drawGuiToggleGroup(rl.NewRectangle(645, 70, 20, 20), []string{"1", "2", "3", "4"}, &imageIndex) + + drawGuiSliderBar(rl.NewRectangle(645, 100, 120, 20), "Contrast", fmt.Sprintf("%.0f", contrast), &contrast, -100.0, 100.0) + drawGuiSliderBar(rl.NewRectangle(645, 130, 120, 20), "Saturation", fmt.Sprintf("%.0f", saturation), &saturation, -100.0, 100.0) + drawGuiSliderBar(rl.NewRectangle(645, 160, 120, 20), "Brightness", fmt.Sprintf("%.0f", brightness), &brightness, -100.0, 100.0) + + resetButtonClicked = drawGuiButton(rl.NewRectangle(645, 190, 40, 20), "Reset") + + rl.DrawFPS(710, 10) + + rl.EndDrawing() + } + + for i := 0; i < maxTextures; i++ { + rl.UnloadTexture(textures[i]) + } + rl.UnloadShader(shdrColorCorrection) + + rl.CloseWindow() +} + +func drawGuiToggleGroup(bounds rl.Rectangle, options []string, active *int32) { + mousePos := rl.GetMousePosition() + for i, option := range options { + rec := rl.NewRectangle(bounds.X+float32(i)*(bounds.Width+5), bounds.Y, bounds.Width, bounds.Height) + isSelected := *active == int32(i) + isHovered := rl.CheckCollisionPointRec(mousePos, rec) + + bgColor := rl.LightGray + textColor := rl.DarkGray + if isSelected { + bgColor = rl.Blue + textColor = rl.White + } else if isHovered { + bgColor = rl.Gray + textColor = rl.White + } + + rl.DrawRectangleRec(rec, bgColor) + rl.DrawRectangleLinesEx(rec, 1, rl.DarkGray) + rl.DrawText(option, int32(rec.X+rec.Width/2-3), int32(rec.Y+rec.Height/2-5), 10, textColor) + + if isHovered && rl.IsMouseButtonPressed(rl.MouseButtonLeft) { + *active = int32(i) + } + } +} + +func drawGuiSliderBar(bounds rl.Rectangle, label, textRight string, value *float32, minValue, maxValue float32) { + rl.DrawText(label, int32(bounds.X-60), int32(bounds.Y+5), 10, rl.DarkGray) + + rl.DrawRectangleRec(bounds, rl.LightGray) + rl.DrawRectangleLinesEx(bounds, 1, rl.DarkGray) + + // Handle mouse dragging + mousePos := rl.GetMousePosition() + if rl.IsMouseButtonDown(rl.MouseButtonLeft) && rl.CheckCollisionPointRec(mousePos, bounds) { + pct := (mousePos.X - bounds.X) / bounds.Width + if pct < 0 { + pct = 0 + } + if pct > 1 { + pct = 1 + } + *value = minValue + pct*(maxValue-minValue) + } + + // Draw filled bar + pct := (*value - minValue) / (maxValue - minValue) + fillWidth := bounds.Width * pct + if fillWidth > 0 { + rl.DrawRectangleRec(rl.NewRectangle(bounds.X, bounds.Y, fillWidth, bounds.Height), rl.SkyBlue) + } + + rl.DrawText(textRight, int32(bounds.X+bounds.Width+10), int32(bounds.Y+5), 10, rl.DarkGray) +} + +func drawGuiButton(bounds rl.Rectangle, text string) bool { + mousePos := rl.GetMousePosition() + isHovered := rl.CheckCollisionPointRec(mousePos, bounds) + + bgColor := rl.LightGray + textColor := rl.DarkGray + if isHovered { + bgColor = rl.Gray + textColor = rl.White + } + + rl.DrawRectangleRec(bounds, bgColor) + rl.DrawRectangleLinesEx(bounds, 1, rl.DarkGray) + rl.DrawText(text, int32(bounds.X+bounds.Width/2-13), int32(bounds.Y+bounds.Height/2-5), 10, textColor) + + return isHovered && rl.IsMouseButtonPressed(rl.MouseButtonLeft) +} diff --git a/examples/shaders/color_correction/mandrill.png b/examples/shaders/color_correction/mandrill.png new file mode 100644 index 00000000..02d058eb Binary files /dev/null and b/examples/shaders/color_correction/mandrill.png differ diff --git a/examples/shaders/color_correction/parrots.png b/examples/shaders/color_correction/parrots.png new file mode 100644 index 00000000..9a0e7f80 Binary files /dev/null and b/examples/shaders/color_correction/parrots.png differ diff --git a/examples/shaders/julia_set/julia_set.fs b/examples/shaders/julia_set/julia_set.fs new file mode 100644 index 00000000..a3e5bec5 --- /dev/null +++ b/examples/shaders/julia_set/julia_set.fs @@ -0,0 +1,80 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; + +// Output fragment color +out vec4 finalColor; + +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 maxIterations = 255; // Max iterations to do +const float colorCycles = 2.0; // 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); +} + +// 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); +} + +void main() +{ + /********************************************************************************************** + Julia sets use a function z^2 + c, where c is a constant + This function is iterated until the nature of the point is determined + + If the magnitude of the number becomes greater than 2, then from that point onward + the number will get bigger and bigger, and will never get smaller (tends towards infinity) + 2^2 = 4, 4^2 = 8 and so on + So at 2 we stop iterating + + 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 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 + And once the magnitude squared is > 4, then magnitude > 2 is also true (saves computational power) + *************************************************************************************************/ + + // 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 - 0.5f)*2.5, (fragTexCoord.y - 0.5)*1.5)/zoom; + z.x += offset.x; + z.y += offset.y; + + int iterations = 0; + for (iterations = 0; iterations < maxIterations; iterations++) + { + z = ComplexSquare(z) + c; // Iterate function + + if (dot(z, z) > 4.0) break; + } + + // Another few iterations decreases errors in the smoothing calculation + // See http://linas.org/art-gallery/escape/escape.html for more information + z = ComplexSquare(z) + c; + 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)); + + // Normalize the value so it is between 0 and 1 + 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*colorCycles, 1.0, 1.0)), 1.0); +} diff --git a/examples/shaders/julia_set/main.go b/examples/shaders/julia_set/main.go new file mode 100644 index 00000000..64ca0940 --- /dev/null +++ b/examples/shaders/julia_set/main.go @@ -0,0 +1,171 @@ +package main + +import ( + rl "github.com/gen2brain/raylib-go/raylib" +) + +// A few good julia sets +var pointsOfInterest = [6][2]float32{ + {-0.348827, 0.607167}, + {-0.786268, 0.169728}, + {-0.8, 0.156}, + {0.285, 0.0}, + {-0.835, -0.2321}, + {-0.70176, -0.3842}, +} + +const ( + screenWidth = 800 + screenHeight = 450 + zoomSpeed = 1.01 + offsetSpeedMul = 2.0 + startingZoom = 0.75 +) + +func main() { + // Initialization + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia set") + + shader := rl.LoadShader("", "julia_set.fs") + target := rl.LoadRenderTexture(int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight())) + cVal := []float32{pointsOfInterest[0][0], pointsOfInterest[0][1]} + + // Offset and zoom to draw the julia set at + offset := []float32{0.0, 0.0} + zoom := float32(startingZoom) + + // Get variable (uniform) locations on the shader + cLoc := rl.GetShaderLocation(shader, "c") + zoomLoc := rl.GetShaderLocation(shader, "zoom") + offsetLoc := rl.GetShaderLocation(shader, "offset") + + // Upload initial shader uniform values + rl.SetShaderValue(shader, cLoc, cVal, rl.ShaderUniformVec2) + rl.SetShaderValue(shader, zoomLoc, []float32{zoom}, rl.ShaderUniformFloat) + rl.SetShaderValue(shader, offsetLoc, offset, rl.ShaderUniformVec2) + + incrementSpeed := 0 + showControls := true + + rl.SetTargetFPS(60) + + // Main game loop + for !rl.WindowShouldClose() { + // Update + // Press [1 - 6] to reset c to a point of interest + if rl.IsKeyPressed(rl.KeyOne) { + cVal[0] = pointsOfInterest[0][0] + cVal[1] = pointsOfInterest[0][1] + rl.SetShaderValue(shader, cLoc, cVal, rl.ShaderUniformVec2) + } else if rl.IsKeyPressed(rl.KeyTwo) { + cVal[0] = pointsOfInterest[1][0] + cVal[1] = pointsOfInterest[1][1] + rl.SetShaderValue(shader, cLoc, cVal, rl.ShaderUniformVec2) + } else if rl.IsKeyPressed(rl.KeyThree) { + cVal[0] = pointsOfInterest[2][0] + cVal[1] = pointsOfInterest[2][1] + rl.SetShaderValue(shader, cLoc, cVal, rl.ShaderUniformVec2) + } else if rl.IsKeyPressed(rl.KeyFour) { + cVal[0] = pointsOfInterest[3][0] + cVal[1] = pointsOfInterest[3][1] + rl.SetShaderValue(shader, cLoc, cVal, rl.ShaderUniformVec2) + } else if rl.IsKeyPressed(rl.KeyFive) { + cVal[0] = pointsOfInterest[4][0] + cVal[1] = pointsOfInterest[4][1] + rl.SetShaderValue(shader, cLoc, cVal, rl.ShaderUniformVec2) + } else if rl.IsKeyPressed(rl.KeySix) { + cVal[0] = pointsOfInterest[5][0] + cVal[1] = pointsOfInterest[5][1] + rl.SetShaderValue(shader, cLoc, cVal, rl.ShaderUniformVec2) + } + + // If "R" is pressed, reset zoom and offset + if rl.IsKeyPressed(rl.KeyR) { + zoom = startingZoom + offset[0] = 0.0 + offset[1] = 0.0 + rl.SetShaderValue(shader, zoomLoc, []float32{zoom}, rl.ShaderUniformFloat) + rl.SetShaderValue(shader, offsetLoc, offset, rl.ShaderUniformVec2) + } + + if rl.IsKeyPressed(rl.KeySpace) { + incrementSpeed = 0 // Pause animation + } + + if rl.IsKeyPressed(rl.KeyF1) { + showControls = !showControls // Toggle whether or not to show controls + } + + if rl.IsKeyPressed(rl.KeyRight) { + incrementSpeed++ + } else if rl.IsKeyPressed(rl.KeyLeft) { + incrementSpeed-- + } + + // If either left or right button is pressed, zoom in/out + if rl.IsMouseButtonDown(rl.MouseButtonLeft) || rl.IsMouseButtonDown(rl.MouseButtonRight) { + // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out + if rl.IsMouseButtonDown(rl.MouseButtonLeft) { + zoom *= zoomSpeed + } else { + zoom *= 1.0 / zoomSpeed + } + + mousePos := rl.GetMousePosition() + offsetVelocityX := (mousePos.X/float32(screenWidth) - 0.5) * offsetSpeedMul / zoom + offsetVelocityY := (mousePos.Y/float32(screenHeight) - 0.5) * offsetSpeedMul / zoom + + // Apply move velocity to camera + dt := rl.GetFrameTime() + offset[0] += dt * offsetVelocityX + offset[1] += dt * offsetVelocityY + + // Update the shader uniform values + rl.SetShaderValue(shader, zoomLoc, []float32{zoom}, rl.ShaderUniformFloat) + rl.SetShaderValue(shader, offsetLoc, offset, rl.ShaderUniformVec2) + } + + // Increment c value with time + dc := rl.GetFrameTime() * float32(incrementSpeed) * 0.0005 + cVal[0] += dc + cVal[1] += dc + rl.SetShaderValue(shader, cLoc, cVal, rl.ShaderUniformVec2) + + // Draw + // Using a render texture to draw Julia set + rl.BeginTextureMode(target) + + rl.ClearBackground(rl.Black) + + // Draw a rectangle in shader mode to be used as shader canvas + rl.DrawRectangle(0, 0, int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight()), rl.Black) + + rl.EndTextureMode() + + rl.BeginDrawing() + + rl.ClearBackground(rl.Black) + + // Draw saved texture and rendered julia set with shader + rl.BeginShaderMode(shader) + + rl.DrawTextureEx(target.Texture, rl.Vector2Zero(), 0.0, 1.0, rl.White) + + rl.EndShaderMode() + + if showControls { + rl.DrawText("Press Mouse buttons right/left to zoom in/out and move", 10, 15, 10, rl.RayWhite) + rl.DrawText("Press KEY_F1 to toggle these controls", 10, 30, 10, rl.RayWhite) + rl.DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, rl.RayWhite) + rl.DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, rl.RayWhite) + rl.DrawText("Press KEY_SPACE to stop movement animation", 10, 75, 10, rl.RayWhite) + rl.DrawText("Press KEY_R to recenter the camera", 10, 90, 10, rl.RayWhite) + } + + rl.EndDrawing() + } + rl.UnloadShader(shader) + rl.UnloadRenderTexture(target) + + rl.CloseWindow() +}