Code 7

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Shader.h"
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"

const char* vertex_fractal = "#version 450 core\n"
"layout(location = 0) in vec3 aPos;\n"
"out vec3 FragPos;\n"
"void main()\n"
"{\n"
"	gl_Position = vec4(aPos, 1.0f);\n"
"	FragPos = aPos;\n"
"}\0";
const char* fragment_fractal = "#version 450 core\n"
"in vec3 FragPos;\n"
"uniform vec4 transform;\n"
"uniform int max_iter;\n"
"uniform float color_offset;\n"
"uniform float color_repeat;\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"	vec2 z;\n"
"	vec2 c = vec2(FragPos) * transform.zw + transform.xy;\n"
"	float r = 20.0;\n"
"	float iter;\n"
"	for(iter = 0; iter < max_iter; iter++)\n"
"	{\n"
"		z = vec2(z.x*z.x - z.y*z.y, 2*z.x*z.y) + c;\n"
"		if(length(z) > r) break;\n"
"	}\n"
"	if(iter == max_iter) discard;\n"
"	float dist = length(z);\n"
"	float frac_iter = log(log(dist) / log(r)) / log(2);\n"
"	float m = sqrt((iter - frac_iter) / max_iter);\n"
"	FragColor = sin(vec4(0.2, 0.4, 0.6, 1) * m * color_repeat + color_offset) * 0.5 + 0.5;\n"
"}\0";

const char* fragment_julia = "#version 450 core\n"
"in vec3 FragPos;\n"
"uniform vec4 transform;\n"
"uniform int max_iter;\n"
"uniform float color_offset;\n"
"uniform float color_repeat;\n"
"uniform vec2 c;\n"
"uniform bool solid;\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"	vec2 z = vec2(FragPos) * transform.zw + transform.xy;\n"
"	float r = 20.0;\n"
"	float iter;\n"
"	for(iter = 0; iter < max_iter; iter++)\n"
"	{\n"
"		z = vec2(z.x*z.x - z.y*z.y, 2*z.x*z.y) + c;\n"
"		if(length(z) > r) break;\n"
"	}\n"
"	if(iter == max_iter)\n"
"		if(solid)\n"
"			FragColor = vec4(0, 0, 0, 1);\n"
"		else\n"
"			discard;\n"
"	float dist = length(z);\n"
"	float frac_iter = log(log(dist) / log(r)) / log(2);\n"
"	float m = sqrt((iter - frac_iter) / max_iter);\n"
"	FragColor = sin(vec4(0.2, 0.4, 0.6, 1) * m * color_repeat + color_offset) * 0.5 + 0.5;\n"
"}\0";

void Framebuffer_Size_Callback(GLFWwindow* window, int width, int height);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos);
void GetInput(GLFWwindow* window);
unsigned int Screen_Width, Screen_Height;
const bool full_screen = false;
//past = previous time, lag = difference in time, scale = zoom amount, (xpos, ypos) = pos, zvel = zoom velocity,
//acc = acceleration, frict = frictional or oppposing force, max = maximum position
float past, lag, aspect, scale, xscale, yscale, xpos, ypos, xvel, yvel, zvel;
const float xacc = 0.1f, yacc = 0.1f, zacc = 0.01f, xfrict = 0.95f, yfrict = 0.95f, zfrict = 0.95f, xmin = -2.0f, xmax = 2.0f, ymin = -2.0f, ymax = 2.0f;
glm::vec2 julia_pos;
glm::vec2 mouse_pos;
glm::vec4 transform;
int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	GLFWwindow* window;
	if (full_screen)
	{
		const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
		Screen_Width = mode->width;
		Screen_Height = mode->height;
		window = glfwCreateWindow(mode->width, mode->height, "Voxel Engine", glfwGetPrimaryMonitor(), NULL);
	}
	else
	{
		Screen_Width = 1000;
		Screen_Height = 800;
		window = glfwCreateWindow(Screen_Width, Screen_Height, "OpenGL1", NULL, NULL);
	}
	if (window == NULL)
	{
		std::cout << "GLFW window creation failed" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, Framebuffer_Size_Callback);
	glfwSetScrollCallback(window, scroll_callback);
	glfwSetCursorPosCallback(window, cursor_position_callback);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Glad initialization failed" << std::endl;
		glfwTerminate();
		return -1;
	}
	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	ImGui::StyleColorsDark();
	ImGui_ImplGlfw_InitForOpenGL(window, true);
	ImGui_ImplOpenGL3_Init();

	//these are the vertices used to draw a rectangle covering the whole screen.
	float fractal_vertices[] =
	{
		-1.0f, -1.0f,  0.0f,
		 1.0f,  1.0f,  0.0f,
		 1.0f, -1.0f,  0.0f,

		 1.0f,  1.0f,  0.0f,
		-1.0f, -1.0f,  0.0f,
		-1.0f,  1.0f,  0.0f,
	};
	//this code will generate the vaos and vbos that the graphics card will use.
	//the "Shader" class is a custom class.
	Shader shader_fractal(vertex_fractal, fragment_fractal);
	unsigned int fractal_vao, fractal_vbo;
	glGenVertexArrays(1, &fractal_vao);
	glGenBuffers(1, &fractal_vbo);
	glBindVertexArray(fractal_vao);
	glBindBuffer(GL_ARRAY_BUFFER, fractal_vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(fractal_vertices), fractal_vertices, GL_STATIC_DRAW);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

	glm::vec2 julia_ratio = glm::vec2(0.5, 1.0);
	float julia_vertices[] =
	{
		1.0f - 2 * julia_ratio.x, julia_ratio.y,  0.0f,
		 1.0f, -julia_ratio.y,  0.0f,
		 1.0f, julia_ratio.y,  0.0f,

		 1.0f, -julia_ratio.y,  0.0f,
		1.0f - 2 * julia_ratio.x, julia_ratio.y,  0.0f,
		1.0f - 2 * julia_ratio.x, -julia_ratio.y,  0.0f,
	};

	Shader shader_julia(vertex_fractal, fragment_julia);

	unsigned int julia_vao, julia_vbo;
	glGenVertexArrays(1, &julia_vao);
	glGenBuffers(1, &julia_vbo);
	glBindVertexArray(julia_vao);
	glBindBuffer(GL_ARRAY_BUFFER, julia_vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(julia_vertices), julia_vertices, GL_STATIC_DRAW);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

	aspect = (float)Screen_Width / (float)Screen_Height;
	scale = 1.0f;
	xscale = scale;
	yscale = scale;
	xpos = 0.0f;
	ypos = 0.0f;
	if (aspect > 1.0f) yscale /= aspect;
	else xscale *= aspect;
	static float color_speed = 1.0f, color_repeat = 20.0f;
	static int max_iter = 100;
	bool julia_solid = true, show_julia = true, show_fractal = true;
	while (!glfwWindowShouldClose(window))
	{
		GetInput(window);

		ImGui_ImplOpenGL3_NewFrame();
		ImGui_ImplGlfw_NewFrame();
		ImGui::NewFrame();

		float time = (float)glfwGetTime();
		lag = time - past;
		past = time;
		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		xvel *= xfrict;
		yvel *= yfrict;
		zvel *= zfrict;
		xpos += xvel;
		ypos += yvel;
		if (xpos < xmin)
		{
			xpos = xmin;
			xvel = 0;
		}
		if (xpos > xmax)
		{
			xpos = xmax;
			xvel = 0;
		}
		if (ypos < ymin)
		{
			ypos = ymin;
			yvel = 0;
		}
		if (ypos > ymax)
		{
			ypos = ymax;
			yvel = 0;
		}
		xscale *= 1 - zvel;
		yscale *= 1 - zvel;
		transform = glm::vec4(xpos, ypos, xscale, yscale);
		if (show_fractal)
		{
			shader_fractal.Use();
			shader_fractal.SetVec4("transform", transform);
			shader_fractal.Setf("color_offset", time * color_speed);
			shader_fractal.Setf("color_repeat", color_repeat);
			shader_fractal.SetInt("max_iter", max_iter);
			glBindVertexArray(fractal_vao);
			glDrawArrays(GL_TRIANGLES, 0, 6);
		}

		if (show_julia)
		{
			shader_julia.Use();
			shader_julia.SetVec4("transform", transform);
			shader_julia.Setf("color_offset", time * color_speed);
			shader_julia.Setf("color_repeat", color_repeat);
			shader_julia.SetInt("max_iter", max_iter);
			shader_julia.SetVec2("c", julia_pos);
			shader_julia.SetInt("solid", julia_solid);
			if (show_fractal)
			{
				glBindVertexArray(julia_vao);
			}
			else
			{
				glBindVertexArray(fractal_vao);
			}
			glDrawArrays(GL_TRIANGLES, 0, 6);
		}

		ImGui::Begin("Settings");
		if (ImGui::Button("Exit")) glfwSetWindowShouldClose(window, true);
		ImGui::SliderInt("Iterations", &max_iter, 0, 1000);
		ImGui::SliderFloat("Color Speed", &color_speed, 0.0f, 100.0f);
		ImGui::SliderFloat("Color Repeat", &color_repeat, 1.0f, 1000.0f);
		ImGui::Checkbox("Show Mandelbrot Set", &show_fractal);
		ImGui::Checkbox("Show Julia Set", &show_julia);
		ImGui::Checkbox("Solid", &julia_solid);
		ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
		ImGui::End();
		ImGui::Render();
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	glDeleteVertexArrays(1, &fractal_vao);
	glDeleteBuffers(1, &fractal_vbo);
	ImGui_ImplOpenGL3_Shutdown();
	ImGui_ImplGlfw_Shutdown();
	ImGui::DestroyContext();
	glfwTerminate();
	return 0;
}
void Framebuffer_Size_Callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}
void GetInput(GLFWwindow* window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		yvel -= yacc * yscale * lag;
	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		yvel += yacc * yscale * lag;
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		xvel -= xacc * xscale * lag;
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		xvel += xacc * xscale * lag;
	//if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)
	if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
	{
		julia_pos.x = mouse_pos.x / Screen_Width * 2 - 1;
		julia_pos.y = -mouse_pos.y / Screen_Height * 2 + 1;
		julia_pos.x *= transform.z;
		julia_pos.y *= transform.w;
		julia_pos.x += transform.x;
		julia_pos.y += transform.y;
	}
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
	zvel += yoffset * zacc;
}

static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
	mouse_pos.x = (float)xpos;
	mouse_pos.y = (float)ypos;
}