Merge pull request #835 from eggmund/master

Added julia set shader example.
This commit is contained in:
Ray 2019-05-12 21:50:48 +02:00 committed by GitHub
commit 4b8c56ebd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 300 additions and 0 deletions

View File

@ -423,6 +423,7 @@ EXAMPLES = \
shaders/shaders_postprocessing \
shaders/shaders_raymarching \
shaders/shaders_palette_switch \
shaders/shaders_julia_set \
audio/audio_sound_loading \
audio/audio_music_stream \
audio/audio_module_playing \

View File

@ -0,0 +1,86 @@
#version 330
// Input vertex attributes (from vertex shader)
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.
// Output fragment color
out vec4 finalColor;
const int MAX_ITERATIONS = 255; // Max iterations to do.
// 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 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()
{
// The pixel coordinates scaled so they are on the mandelbrot scale.
vec2 z = vec2(((gl_FragCoord.x + offset.x)/screenDims.x) * 2.5 * zoom,
((screenDims.y - gl_FragCoord.y + offset.y)/screenDims.y) * 1.5 * zoom); // y also flipped due to opengl
int iterations = 0;
/*
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 MAX_ITERATIONS is for.
Then we can divide the iterations by the MAX_ITERATIONS 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).
*/
for (iterations = 0; iterations < MAX_ITERATIONS; 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(MAX_ITERATIONS);
// 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);
}
}

View File

@ -0,0 +1,213 @@
/*******************************************************************************************
*
* raylib [shaders] example - Render julia sets using a shader.
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
*
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3).
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
*
* Author: eggmund (https://github.com/eggmund)
*
********************************************************************************************/
#include "raylib.h"
#include <string.h> // For memcpy
// Speed when using auto
const float AUTO_SPEED = 0.0005;
// A few good julia sets
const float POINTS_OF_INTEREST[6][2] =
{
{-0.348827, 0.607167},
{-0.786268, 0.169728},
{-0.8, 0.156},
{0.285, 0.0},
{-0.835, -0.2321},
{-0.70176, -0.3842},
};
int main()
{
// Initialization
//--------------------------------------------------------------------------------------
int screenWidth = 1280;
int screenHeight = 720;
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia set renderer");
// If julia set is rendered for this frame.
bool rendered = false;
bool showControls = true;
// Multiplier of speed to change c value. Set to 3 to start off with.
int incrementSpeed = 3;
// Offset and zoom to draw the julia set at. (centered on screen and 1.6 times smaller)
float offset[2] = { -(float)screenWidth/2, -(float)screenHeight/2 };
float zoom = 1.6;
// c constant to use in z^2 + c
float c[2];
// Copy a point of interest into the c variable. 4 bytes per float (32 bits).
memcpy(c, &POINTS_OF_INTEREST[0], 8);
// Load julia set shader
// NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
Shader shader = LoadShader(0, "resources/shaders/glsl330/julia_shader.fs");
// Get variable (uniform) location on the shader to connect with the program
// NOTE: If uniform variable could not be found in the shader, function returns -1
// The location of c will be stored since we will need to change this whenever c changes
int cLoc = GetShaderLocation(shader, "c");
// Tell the shader what the screen dimensions, zoom, offset and c are
float screenDims[2] = { (float)screenWidth, (float)screenHeight };
SetShaderValue(shader, GetShaderLocation(shader, "screenDims"), screenDims, UNIFORM_VEC2);
SetShaderValue(shader, GetShaderLocation(shader, "zoom"), &zoom, UNIFORM_FLOAT);
SetShaderValue(shader, GetShaderLocation(shader, "offset"), offset, UNIFORM_VEC2);
SetShaderValue(shader, cLoc, c, UNIFORM_VEC2);
// Create a RenderTexture2D to be used for render to texture
RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
SetTargetFPS(60); // Set the window to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Get input
//----------------------------------------------------------------------------------
// Press 1 - 6 to reset c to a point of interest.
if (IsKeyPressed(KEY_ONE) || IsKeyPressed(KEY_TWO) || IsKeyPressed(KEY_THREE) || IsKeyPressed(KEY_FOUR) || IsKeyPressed(KEY_FIVE) || IsKeyPressed(KEY_SIX))
{
if (IsKeyPressed(KEY_ONE))
{
memcpy(c, &POINTS_OF_INTEREST[0], 8);
}
else if (IsKeyPressed(KEY_TWO))
{
memcpy(c, &POINTS_OF_INTEREST[1], 8);
}
else if (IsKeyPressed(KEY_THREE))
{
memcpy(c, &POINTS_OF_INTEREST[2], 8);
}
else if (IsKeyPressed(KEY_FOUR))
{
memcpy(c, &POINTS_OF_INTEREST[3], 8);
}
else if (IsKeyPressed(KEY_FIVE))
{
memcpy(c, &POINTS_OF_INTEREST[4], 8);
}
else if (IsKeyPressed(KEY_SIX))
{
memcpy(c, &POINTS_OF_INTEREST[5], 8);
}
SetShaderValue(shader, cLoc, c, UNIFORM_VEC2);
rendered = false; // c value has changed, so render the set again.
}
// Press "r" to stop changing c
if (IsKeyPressed(KEY_R))
{
incrementSpeed = 0;
}
// Toggle whether or not to show controls
if (IsKeyPressed(KEY_H))
{
showControls = !showControls;
}
// Scroll to change c increment speed.
int mouseMv = GetMouseWheelMove(); // Get the amount the mouse has moved this frame
if (mouseMv != 0)
{
if (IsKeyDown(KEY_LEFT_SHIFT))
{
incrementSpeed += mouseMv * 10;
}
else
{
incrementSpeed += mouseMv;
}
rendered = false;
}
if (incrementSpeed != 0)
{
float amount = GetFrameTime() * incrementSpeed * AUTO_SPEED;
c[0] += amount;
c[1] += amount;
// Update the c value in the shader.
SetShaderValue(shader, cLoc, c, UNIFORM_VEC2);
rendered = false;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(BLACK); // Clear the screen of the previous frame.
// If the c value has changed, redraw the julia set using the shader, onto the render texture.
if (!rendered)
{
BeginTextureMode(target); // Enable drawing to texture
ClearBackground(BLACK); // Clear the last frame drawn on the texture.
// Draw a rectangle in shader mode. This acts as a canvas for the shader to draw on.
BeginShaderMode(shader);
DrawRectangle(0, 0, screenWidth, screenHeight, BLACK);
EndShaderMode();
EndTextureMode();
rendered = true; // The set is now rendered, so do not compute it again until it next changes.
}
// Draw the saved texture (rendered julia set).
DrawTextureRec(target.texture, (Rectangle){ 0, 0, target.texture.width, target.texture.height }, (Vector2){ 0, 0 }, WHITE);
// Print information.
DrawText( FormatText("cx: %f\ncy: %f\nspeed: %d", c[0], c[1], incrementSpeed), 10, 10, 20, RAYWHITE );
if (showControls)
{
DrawText("Press keys 1 - 6 to change point of interest.", 10, screenHeight - 88, 20, RAYWHITE);
DrawText("Use the scroll wheel to auto increment the c value. Hold shift while scrolling to increase speed by 10.", 10, screenHeight - 66, 20, RAYWHITE);
DrawText("Press 'r' to reset speed.", 10, screenHeight - 44, 20, RAYWHITE);
DrawText("Press 'h' to hide these controls.", 10, screenHeight - 22, 20, RAYWHITE);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shader); // Unload shader
UnloadRenderTexture(target); // Unload render texture
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB