Setting up support multiple shaders. Adding menu settings.
This commit is contained in:
179
main.cpp
179
main.cpp
@@ -43,17 +43,24 @@ std::vector<std::string> string_expand(const std::string& input, char delimiter)
|
||||
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
enum class Fractal {
|
||||
MandelbrotSet, JuliaSet
|
||||
};
|
||||
|
||||
class ReShaderProgram : public ReWindow::OpenGLWindow {
|
||||
class FractalInspectorApp : public ReWindow::OpenGLWindow {
|
||||
public:
|
||||
JUI::Scene *scene;
|
||||
JUI::UtilityBar* toolbar;
|
||||
JUI::CommandLine *console;
|
||||
JUI::Window* info_dialog;
|
||||
JUI::TextRect* fps_label;
|
||||
|
||||
JUI::Window* mandelbrotset_dialog;
|
||||
JUI::Window* juliaset_dialog;
|
||||
JUI::Window* colorpicker_window;
|
||||
|
||||
JGL::Shader shader;
|
||||
JGL::Shader mandelbrot_shader;
|
||||
JGL::Shader julia_shader;
|
||||
JGL::RenderTarget *canvas;
|
||||
float u_time;
|
||||
float u_scale = 2.f;
|
||||
@@ -62,42 +69,35 @@ public:
|
||||
Color4 u_rgb_2 = Colors::Reds::LightCoral;
|
||||
Color4 u_rgb_3 = Colors::Yellow;
|
||||
Color4 u_rgb_4 = Colors::Green;
|
||||
std::string frag_name;
|
||||
std::string vert_name;
|
||||
Vector2 u_julia_set {0,0};
|
||||
Vector2 u_julia_value {0,0};
|
||||
|
||||
ReShaderProgram();
|
||||
Fractal current_fractal = Fractal::MandelbrotSet;
|
||||
|
||||
FractalInspectorApp();
|
||||
|
||||
/// Returns a full-path file name for a GLSL vertex shader, from a given prefix name.
|
||||
static std::filesystem::path VertexShaderFilepathFromPrefixName(const std::string &name)
|
||||
{
|
||||
return "../shaders/" + name + ".vert.glsl";
|
||||
}
|
||||
|
||||
/// Returns a full-path file name for a GLSL fragment shader, from a given prefix name.
|
||||
static std::filesystem::path FragmentShaderFilepathFromPrefixName(const std::string &name)
|
||||
{
|
||||
return "../shaders/" + name + ".frag.glsl";
|
||||
}
|
||||
|
||||
void LoadShader(const std::string& name)
|
||||
{
|
||||
frag_name = name;
|
||||
vert_name = name;
|
||||
shader = Shader(VertexShaderFilepathFromPrefixName(name), FragmentShaderFilepathFromPrefixName(name));
|
||||
}
|
||||
void LoadShaders() {
|
||||
|
||||
void LoadShader(const std::string& vertex_name, const std::string& fragment_name)
|
||||
{
|
||||
this->vert_name = vertex_name;
|
||||
this->frag_name = fragment_name;
|
||||
shader = Shader(VertexShaderFilepathFromPrefixName(vert_name), FragmentShaderFilepathFromPrefixName(frag_name));
|
||||
}
|
||||
// TODO: Come up with a decent way to bake the shader program sources into the executable, when compiling a release build!
|
||||
|
||||
void LoadShaderWithDefaultVertex(const std::string& name) {
|
||||
this->frag_name = name;
|
||||
shader = Shader(VertexShaderFilepathFromPrefixName(vert_name), FragmentShaderFilepathFromPrefixName(name));
|
||||
mandelbrot_shader = Shader(VertexShaderFilepathFromPrefixName("test"), FragmentShaderFilepathFromPrefixName("mandelbrot"));
|
||||
julia_shader = Shader(VertexShaderFilepathFromPrefixName("test" ), FragmentShaderFilepathFromPrefixName("julia"));
|
||||
}
|
||||
|
||||
void ReloadShader() {
|
||||
LoadShader(vert_name, frag_name);
|
||||
LoadShaders();
|
||||
}
|
||||
|
||||
using CmdArgs = std::vector<std::string>;
|
||||
@@ -132,22 +132,6 @@ public:
|
||||
|
||||
if (cmd == "r" || cmd == "reload") {
|
||||
ReloadShader();
|
||||
} else if (cmd == "lf") {
|
||||
if (tokens.size() > 1) {
|
||||
std::string fragment = tokens[1];
|
||||
// TODO: Check for file in question.
|
||||
LoadShaderWithDefaultVertex(fragment);
|
||||
return;
|
||||
}
|
||||
console->Log("Error: Command syntax is lf <fragment-shader-name>", Colors::Red);
|
||||
} else if (cmd == "ls") {
|
||||
if (tokens.size() > 1) {
|
||||
std::string shader = tokens[1];
|
||||
// TODO: Check for file in question.
|
||||
LoadShader(shader);
|
||||
return;
|
||||
}
|
||||
console->Log("Error: Command syntax is ls <shader-name>", Colors::Red);
|
||||
} else if (cmd == "pos") {
|
||||
return PositionCmd(tokens);
|
||||
} else {
|
||||
@@ -155,11 +139,49 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LoadJulia() {
|
||||
juliaset_dialog->Open();
|
||||
}
|
||||
|
||||
void UnloadJulia() {
|
||||
juliaset_dialog->Close();
|
||||
}
|
||||
|
||||
void CreateMenu() {
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
toolbar = new JUI::UtilityBar(scene);
|
||||
toolbar->AddSubmenu("File"); toolbar->AddSubmenu("Edit");
|
||||
auto* fractals_list = toolbar->AddSubmenu("Fractals");
|
||||
|
||||
fractals_list->AddButton("Mandelbrot set", [this]{
|
||||
UnloadJulia();
|
||||
current_fractal = Fractal::MandelbrotSet;
|
||||
});
|
||||
fractals_list->AddButton("Julia set", [this]{
|
||||
LoadJulia();
|
||||
current_fractal = Fractal::JuliaSet;
|
||||
});
|
||||
|
||||
auto* view = toolbar->AddSubmenu("View"); {
|
||||
view->AddButton("Color Picker Dialog", [this] {
|
||||
colorpicker_window->Toggle();
|
||||
});
|
||||
|
||||
view->AddButton("Console", [this] {
|
||||
console->Toggle();
|
||||
});
|
||||
|
||||
view->AddButton("README", [this] {
|
||||
info_dialog->Toggle();
|
||||
});
|
||||
|
||||
view->AddSeparator();
|
||||
view->AddButton("Reset Viewport");
|
||||
view->AddButton("Reset Colors");
|
||||
view->AddButton("Zoom In");
|
||||
view->AddButton("Zoom Out");
|
||||
}
|
||||
auto* btn_toggle_console = toolbar->AddButton("Console");
|
||||
|
||||
btn_toggle_console->OnClickEvent += [this] (auto a, auto b) mutable {
|
||||
@@ -176,6 +198,12 @@ public:
|
||||
info_dialog->Toggle();
|
||||
};
|
||||
|
||||
fps_label = new JUI::TextRect(toolbar);
|
||||
fps_label->Position({100_percent - 200_px, 0_px});
|
||||
fps_label->Size({200_px, 100_percent});
|
||||
fps_label->SetTextColor(Colors::Black);
|
||||
fps_label->SetContent("60 FPS");
|
||||
|
||||
auto* layout = new JUI::VerticalListLayout(info_dialog->ViewportInstance());
|
||||
layout->Padding(0_px);
|
||||
|
||||
@@ -200,13 +228,13 @@ public:
|
||||
line_item("This program is written in C++, from scratch.");
|
||||
|
||||
// TODO: Utilize for things later.
|
||||
auto* wind = new JUI::Window(scene);
|
||||
wind->SetTitle("Color-Pickers");
|
||||
wind->MinSize({600, 150});
|
||||
wind->Size({600_px, 150_px});
|
||||
wind->Position({50_px, 50_px});
|
||||
colorpicker_window = new JUI::Window(scene);
|
||||
colorpicker_window->SetTitle("Color-Pickers");
|
||||
colorpicker_window->MinSize({600, 150});
|
||||
colorpicker_window->Size({600_px, 150_px});
|
||||
colorpicker_window->Position({50_px, 50_px});
|
||||
|
||||
auto* col_layout = new JUI::HorizontalListLayout(wind->Content());
|
||||
auto* col_layout = new JUI::HorizontalListLayout(colorpicker_window->Content());
|
||||
//wind->Close();
|
||||
|
||||
JUI::ColorPicker* pp = new JUI::ColorPicker(col_layout);
|
||||
@@ -252,11 +280,11 @@ public:
|
||||
|
||||
mandelbrotset_dialog = new JUI::Window(scene);
|
||||
mandelbrotset_dialog->Position({20_px, 300_px});
|
||||
mandelbrotset_dialog->Close();
|
||||
|
||||
auto* mandelbrotset_layout = new JUI::VerticalListLayout(mandelbrotset_dialog->ViewportInstance());
|
||||
mandelbrotset_layout->LayoutOrder(JUI::LayoutOrder::V::TOP);
|
||||
|
||||
|
||||
int idx = 999;
|
||||
|
||||
auto mb_line_item = [&] (const std::string& text, int size = 20, const Color4& color = Colors::White) {
|
||||
@@ -297,6 +325,30 @@ public:
|
||||
mb_line_item("Color-code the 'escape velocity' of the input numbers that do escape, and you get a result like the one you see.");
|
||||
|
||||
|
||||
juliaset_dialog = new JUI::Window(scene);
|
||||
juliaset_dialog->SetTitle("Julia Set Inputs");
|
||||
juliaset_dialog->Position({20_px, 300_px});
|
||||
juliaset_dialog->Size({900_px, 70_px});
|
||||
|
||||
auto* julia_x_slider = new JUI::Slider(juliaset_dialog->Content());
|
||||
|
||||
julia_x_slider->Size({900_px, 25_px});
|
||||
julia_x_slider->Interval(1e-5f); julia_x_slider->Minimum(0); julia_x_slider->Maximum(1);
|
||||
julia_x_slider->ValueChanged += [this] (double value) {
|
||||
value = value * 2;
|
||||
value = value - 1;
|
||||
u_julia_set.x = value;
|
||||
};
|
||||
|
||||
auto* julia_y_slider = new JUI::Slider(juliaset_dialog->Content());
|
||||
julia_y_slider->Size({900_px, 25_px});
|
||||
julia_y_slider->Position({0_px, 30_px});
|
||||
julia_y_slider->Interval(1e-5f); julia_y_slider->Minimum(0); julia_y_slider->Maximum(1);
|
||||
julia_y_slider->ValueChanged += [this] (double value) {
|
||||
value = value * 2;
|
||||
value = value - 1;
|
||||
u_julia_set.y = value;
|
||||
};
|
||||
}
|
||||
|
||||
bool Open() override
|
||||
@@ -324,7 +376,7 @@ public:
|
||||
CreateMenu();
|
||||
|
||||
canvas = new RenderTarget(vec_size);
|
||||
LoadShader("test", "mandelbrot");
|
||||
LoadShaders();
|
||||
|
||||
console->Log(std::format("OpenGL Renderer: {}", GetGLRenderer()));
|
||||
console->Log(std::format("OpenGL Vendor: {}", GetGLVendor()));
|
||||
@@ -395,11 +447,33 @@ public:
|
||||
|
||||
u_time += elapsed;
|
||||
|
||||
u_julia_value = Vector2::Lerp(u_julia_value, u_julia_set, 0.001f);
|
||||
|
||||
UpdateShaderUniforms(elapsed);
|
||||
|
||||
int fps = 1.f / elapsed;
|
||||
|
||||
std::string fps_text = std::format("FPS: {}", fps);
|
||||
|
||||
fps_label->SetContent(fps_text);
|
||||
}
|
||||
|
||||
void UpdateShaderUniforms(float elapsed) {
|
||||
Vector2 u_res = Vector2(GetSize().x, GetSize().y);
|
||||
|
||||
// TODO: Why do we need to multiply X by 1.5 here?
|
||||
Vector2 u_mouse = Vector2(GetMouseCoordinates().x*1.5f, GetSize().y-GetMouseCoordinates().y);
|
||||
|
||||
JGL::Shader shader;
|
||||
if (current_fractal == Fractal::JuliaSet) {
|
||||
shader = julia_shader;
|
||||
shader.SetVector2("u_julia_set", u_julia_value);
|
||||
}
|
||||
|
||||
if (current_fractal == Fractal::MandelbrotSet) {
|
||||
shader = mandelbrot_shader;
|
||||
}
|
||||
|
||||
shader.SetVector2("u_resolution", u_res);
|
||||
shader.SetFloat("u_time", u_time);
|
||||
shader.SetVector2("u_mouse", u_mouse);
|
||||
@@ -410,6 +484,7 @@ public:
|
||||
shader.SetFloat("u_scale", u_scale);
|
||||
shader.SetVector2("u_translation", u_translation);
|
||||
}
|
||||
|
||||
void Draw() {
|
||||
|
||||
Shader::UseDefault();
|
||||
@@ -427,7 +502,13 @@ public:
|
||||
scene->Draw();
|
||||
|
||||
JGL::J2D::Begin(canvas, true);
|
||||
shader.Use();
|
||||
if (current_fractal == Fractal::JuliaSet) {
|
||||
julia_shader.Use();
|
||||
}
|
||||
|
||||
if (current_fractal == Fractal::MandelbrotSet) {
|
||||
mandelbrot_shader.Use();
|
||||
}
|
||||
JGL::J2D::FillRect(Colors::Black, {0, 0}, Vector2(GetSize().x, GetSize().y));
|
||||
JGL::J2D::End();
|
||||
|
||||
@@ -473,7 +554,7 @@ protected:
|
||||
private:
|
||||
};
|
||||
|
||||
ReShaderProgram::ReShaderProgram(): ReWindow::OpenGLWindow("ReShader", 1800, 1000, 2, 1) {
|
||||
FractalInspectorApp::FractalInspectorApp(): ReWindow::OpenGLWindow("ReShader", 1800, 1000, 2, 1) {
|
||||
Shader::OnCompilationErrorMessage += [this](std::string type, std::string infoLog) {
|
||||
auto log_lines = string_expand(infoLog, '\n');
|
||||
console->Log(type, Colors::Red);
|
||||
@@ -490,7 +571,7 @@ ReShaderProgram::ReShaderProgram(): ReWindow::OpenGLWindow("ReShader", 1800, 100
|
||||
int main(int argc, char** argv) {
|
||||
ReWindow::Logger::Debug.EnableConsole(false);
|
||||
|
||||
auto* program = new ReShaderProgram();
|
||||
auto* program = new FractalInspectorApp();
|
||||
|
||||
program->Open();
|
||||
program->SetFullscreen(false);
|
||||
|
60
shaders/julia.frag.glsl
Normal file
60
shaders/julia.frag.glsl
Normal file
@@ -0,0 +1,60 @@
|
||||
#version 120
|
||||
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
#define PI 3.14159265359
|
||||
|
||||
vec3 pal(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
|
||||
return a + b*cos(6.28318*(c*t+d));
|
||||
}
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform vec2 u_mouse;
|
||||
uniform float u_time;
|
||||
|
||||
uniform vec2 u_translation;
|
||||
uniform float u_scale;
|
||||
|
||||
uniform vec3 u_rgb_1;
|
||||
uniform vec3 u_rgb_2;
|
||||
uniform vec3 u_rgb_3;
|
||||
uniform vec3 u_rgb_4;
|
||||
|
||||
uniform vec2 u_julia_set;
|
||||
|
||||
#define N 256. // Number of iterations?
|
||||
#define B 4. // What does B mean?
|
||||
|
||||
float iterate_julia(vec2 p, vec2 c)
|
||||
{
|
||||
vec2 z = p;
|
||||
|
||||
float i;
|
||||
|
||||
for (i = 0.; i < N; i++) {
|
||||
z = mat2(z, -z.y, z.x) * z + c;
|
||||
|
||||
if (dot(z, z) > B*B) break;
|
||||
}
|
||||
|
||||
if (p.y < 0.f) return i;
|
||||
|
||||
return i - log(log(dot(z, z)) / log(B)) / log(2.);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 R = u_resolution.xy;
|
||||
|
||||
|
||||
vec2 uv = (u_scale * gl_FragCoord.xy - R - 1.) / R.y - u_translation;
|
||||
|
||||
float n = iterate_julia(uv, u_julia_set) / N;
|
||||
|
||||
vec3 col = pal(fract(n + 0.5), u_rgb_1, u_rgb_2, u_rgb_3, u_rgb_4);
|
||||
|
||||
//if (n == 1.) {n = 0; }
|
||||
gl_FragColor = vec4(n == 1. ? vec3(0) : col, 1.);
|
||||
|
||||
}
|
@@ -6,6 +6,11 @@ precision highp float;
|
||||
|
||||
#define PI 3.14159265359
|
||||
|
||||
vec3 pal(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
|
||||
return a + b*cos(6.28318*(c*t+d));
|
||||
}
|
||||
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform vec2 u_mouse;
|
||||
uniform float u_time;
|
||||
@@ -21,88 +26,55 @@ uniform vec3 u_rgb_4;
|
||||
#define N 256. // Number of iterations?
|
||||
#define B 4. // What does B mean?
|
||||
|
||||
|
||||
vec3 pal(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
|
||||
return a + b*cos(6.28318*(c*t+d));
|
||||
}
|
||||
|
||||
// The mandelbrot set is a set of complex numbers c for which the function:
|
||||
// f(z) = z*z + c
|
||||
// stays bounded between a certain range of values when iterated from z = 0.
|
||||
|
||||
#define MAX_ITERATIONS 100.
|
||||
|
||||
vec2 square_imaginary(vec2 number) {
|
||||
return vec2(
|
||||
pow(number.x, 2) - pow(number.y, 2),
|
||||
2*number.x*number.y
|
||||
);
|
||||
}
|
||||
|
||||
float iterate_mandelbrot(vec2 coord) {
|
||||
vec2 z = vec2(0,0);
|
||||
for (int i = 0; i < MAX_ITERATIONS; i++) {
|
||||
z = square_imaginary(z) + coord;
|
||||
if (length(z) > 2) return i / MAX_ITERATIONS;
|
||||
}
|
||||
return MAX_ITERATIONS;
|
||||
}
|
||||
|
||||
float iterate(vec2 p) {
|
||||
float iterate_mandelbrot(vec2 p) {
|
||||
// Initializes the complex number z to 0, and the constant c to the input coordinate p.
|
||||
vec2 z = vec2(0), c = p;
|
||||
float i;
|
||||
float i; // Track iterations.
|
||||
for (i=0.; i < N; i++ ) {
|
||||
// This line performs the core Mandelbrot iteration: z = z^2 + c.
|
||||
// It is done using a matrix multiplication to perform the complex number squaring.
|
||||
// If z = x + iy, then z&2 = (i+iy)(x+iy) = x^2 - y ^ 2 + 2ixy.
|
||||
// The matrix [x, -y; y, x] multiplied by [x, y] gives [x*x - y&y, y*x + x*y] = [Re(z^2) Im(z^2)].
|
||||
// Then we add the constant complex number c (represented by the input vec2).
|
||||
z = mat2(z, -z.y, z.x) * z + c;
|
||||
|
||||
// Check if the magnitude squared of z (distance from the origin squared) exceeds a certain bound (B*B).
|
||||
// If it does, the point is likely to escape to infinity, so we break out of the loop.
|
||||
if (dot(z, z) > B*B) break;
|
||||
}
|
||||
|
||||
//return i;
|
||||
// Compute the iteration smoothly, instead of in integers.
|
||||
// The following section calculates a smooth iteration count for better visual results.
|
||||
|
||||
// This condition seems to handle a specific case, possibly related to symmetry.
|
||||
// If the y-component of the input point p is negative, it returns the integer iteration count.
|
||||
if (p.y < 0.f) return i;
|
||||
return i - log(log(dot(z, z)) / log(B)) / log(2.);;
|
||||
|
||||
// If the point didn't escape within the maximum iterations or if p.y is non-negative,
|
||||
// this line calculates a more precise, floating-point iteration count.
|
||||
// It uses the logarithm of the magnitude squared of z to estimate how far "outside" the
|
||||
// Mandelbrot set the point is. This allows for smoother color gradients in the fractal.
|
||||
// - log(log(dot(z, z)) / log(B)) / log(2.) is a common formula for this smoothing.
|
||||
return i - log(log(dot(z, z)) / log(B)) / log(2.);
|
||||
|
||||
// The commented-out line would simply return the integer number of iterations.
|
||||
// return i;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 R = u_resolution.xy;
|
||||
|
||||
//float translate_h = .4;
|
||||
//float translate_v = 0.0;
|
||||
//float scale = 2.;
|
||||
|
||||
//vec2 translation = vec2(translate_h, translate_v);
|
||||
|
||||
vec2 uv = (u_scale * gl_FragCoord.xy - R - 1.) / R.y - u_translation;
|
||||
//vec2 z = vec2(0), c = uv;
|
||||
//float i;
|
||||
|
||||
//for (i = 0.; i < N; i++) {
|
||||
// z = mat2(z, -z.y, z.x) * z + c;
|
||||
// if (dot(z, z) > B*B) break;
|
||||
//}
|
||||
|
||||
float n = iterate(uv) / N;
|
||||
float n = iterate_mandelbrot(uv) / N;
|
||||
|
||||
vec3 col = pal(fract(n + 0.5), u_rgb_1, u_rgb_2, u_rgb_3, u_rgb_4);
|
||||
|
||||
//if (n == 1.) {n = 0; }
|
||||
gl_FragColor = vec4(n == 1. ? vec3(0) : col, 1.);
|
||||
|
||||
//vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||
//vec3 color = vec3(0.0);
|
||||
|
||||
//vec3 pct = vec3(st.x);
|
||||
|
||||
// pct.r = smoothstep(0.0,1.0, st.x);
|
||||
// pct.g = sin(st.x*PI);
|
||||
// pct.b = pow(st.x,0.5);
|
||||
|
||||
//color = mix(colorA, colorB, pct);
|
||||
|
||||
// Plot transition lines for each channel
|
||||
// color = mix(color,vec3(1.0,0.0,0.0),plot(st,pct.r));
|
||||
// color = mix(color,vec3(0.0,1.0,0.0),plot(st,pct.g));
|
||||
// color = mix(color,vec3(0.0,0.0,1.0),plot(st,pct.b));
|
||||
|
||||
// gl_FragColor = vec4(color,1.0);
|
||||
}
|
||||
|
Reference in New Issue
Block a user