Problems creating simple fractal (2D fractal tree) in opengl.

Hi there.

I have been trying to implement 2D fractal tree to my engine which runs on modern opengl for the past month and without any success at all.
I have asked some questions regarding the issues which i am having and tried to implement given answers to my program and still could not make a working prototype.

To begin with i have the most trouble with iterating the tree.
The problem is that my fractal is actually an object which has different variables that define the fractal which is

Fractal_Tree tree1(depth, branchangle, point1, point2, multiplier,branchcount);

So from previous question i have been suggested to use transform feedback in which shaders are an necessity.
Consequently i got another problem which is that i use orthographic projection space vertex shader to have a manageable “2D” space that is also in another shader program which is called the WorldShader but also i am striving for program to manage and render various fractal objects so the question is how it is possible to connect two different shader programs while having modularity ?

In addition i also got confused which shaders should i be using for calculating and iterating new branching point coordinates in my transform feedback, because it seems like the geometry shader is the best choice for the shader but the problem is that i have 2 points which in fact make trunk of the tree and geometry shader aplies to all points.

So there are some formulas which i have to apply to have a fractal tree
take into consideration that each value here have the x and y axis.

midbranch = (point1 - point2) * multiplier;

//bangle is abbreviation for branch angle
rightbranch.x=midbranch.x * cos(bangle * (M_PI / 180)) + midbranch.y * sin(bangle *(M_PI / 180)) + point2.x;
leftbranch.x=midbranch.x * cos(-bangle * (M_PI / 180)) + midbranch.y * sin(-bangle *(M_PI / 180)) + point2.x;
rightbranch.y = -midbranch.x * sin(bangle * (M_PI / 180)) + midbranch.y * cos(bangle *(M_PI / 180)) + point2.y;
leftbranch.y = -midbranch.x * sin(-angle * (M_PI / 180)) + midbranch.y * cos(-bangle *(M_PI / 180)) + point2.y;

So there’s some source code:
main.cpp


#include "Window/Core/Window.h"
#include "Shaders/ShaderUtil/ShaderProgram.hpp"
#include "Shaders/ShaderUtil/addshader.h"
#include "Drawing//TREE/Fractal_Tree.h"
#include "Maths/Math.hpp"







float CAMangle = (CAMangle = 0) * M_PI / 180;

extern "C" {
	_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}


int id;
long double CAMx = 0, CAMy = 0, CAMz = 1;
long double zoom = 1;
int width = 800;
int height = 640;
glm::vec2 point1;
glm::vec2 point2;
int bangle = 90;
GLint objectID;



int main()
{
	Window Window(width, height, "FRTI ENGINE", NULL); // Width,Height,,Title,MonitorState
	glClearColor(1.0, 1.0, 1.0, 1.0);									   // Clear color rgba
	point1.x = 0.0;
	point1.y = 0.0;
	point2.x = 0.0;
	point2.y = 2;

	//ShaderReadBasic ReadShaders("Source/Shaders/Files/Vertex/Basic.vert","Source/Shaders/Files/Fragment/Basic.frag");//reading of .vert ,geom,.frag files.
	//ReadShaders.enable(); //enabling shaders
	
	// main shader loader

	ShaderLoader vertexmain(GL_VERTEX_SHADER);
	vertexmain.fileloader("Source/Shaders/Files/Vertex/main.vert");
	vertexmain.compile();
	ShaderLoader fragmentmain(GL_FRAGMENT_SHADER);
	fragmentmain.fileloader("Source/Shaders/Files/Fragment/main.frag");
	fragmentmain.compile();

	

	ShaderProgram WorldShader;
	WorldShader.attachShader(vertexmain);
	WorldShader.attachShader(fragmentmain);
	WorldShader.linkProgram();
	

	WorldShader.addUniform("pro_matrix");
	WorldShader.addUniform("model_matrix");
	WorldShader.addUniform("view_matrix");
	WorldShader.addUniform("scale_matrix");
	WorldShader.addUniform("OutColor");
	//ReadShaders.addShader("geometry", "Source/Drawing/TREE/TREE.geom");
	glm::mat4 ortho = glm::ortho(-16.0*(width / height), 16.0*(width / height), -16.0, 16.0);
	float multiplier = 0.5;
	int depth = 5;

	while (!Window.closed())
	{		
		
		Fractal_Tree tree1(depth, bangle, point1, point2, multiplier,4);

		Window.clear();
		WorldShader.use();
		//Camera
		glUniformMatrix4fv(WorldShader.uniform("pro_matrix"), 1, GL_FALSE, glm::value_ptr(ortho));
		glUniformMatrix4fv(WorldShader.uniform("model_matrix"), 1, GL_FALSE, glm::value_ptr(glm::translate(glm::mat4(1.0), glm::vec3(CAMx, CAMy, CAMz))));
		glUniformMatrix4fv(WorldShader.uniform("view_matrix"), 1, GL_FALSE, glm::value_ptr(glm::rotate(glm::mat4(1.0),CAMangle,glm::vec3(0,0,1))));
		glUniformMatrix4fv(WorldShader.uniform("scale_matrix"), 1, GL_FALSE, glm::value_ptr(glm::scale(glm::mat4(1.0), glm::vec3(zoom, zoom, 1))));

		//ReadShaders.setUniforMat4f("pro_matrix", ortho);
		//ReadShaders.setUniforMat4f("model_matrix", glm::translate(glm::mat4(1.0), glm::vec3(CAMx, CAMy,CAMz)));
		//ReadShaders.setUniforMat4f("view_matrix", glm::rotate(glm::mat4(1.0), CAMangle, glm::vec3(0, 0,1)));
		//ReadShaders.setUniforMat4f("scale_matrix", glm::scale(glm::mat4(1.0), glm::vec3(zoom,zoom,1)));
		// Coloring

		double timeValue = glfwGetTime();
		double greenValue = cos(timeValue) / 2.0 + 0.5;
		glUniform4f(WorldShader.uniform("OutColor"), 0.0,greenValue,0.0,1.0);
		//ReadShaders.setUniform4f("OutColor", glm::vec4(0.0,greenValue,0.0,1.0));
		//input.
		if (Window.KeyPressed(GLFW_KEY_Q))
		{
			CAMangle += 0.1;
		}
		if (Window.KeyPressed(GLFW_KEY_E))
		{
			CAMangle -= 0.1;
			
		}
		if (Window.KeyPressed(GLFW_KEY_W))
		{
			CAMy -= 0.5/zoom;
			if (Window.KeyPressed(GLFW_KEY_LEFT_SHIFT))
			{
				CAMy -= 1.0 / zoom;
			}
		}
		if (Window.KeyPressed(GLFW_KEY_S))
		{
			CAMy += 0.5 / zoom;
			if (Window.KeyPressed(GLFW_KEY_LEFT_SHIFT))
			{
				CAMy += 1.0 / zoom;
			}
		}
		if (Window.KeyPressed(GLFW_KEY_A))
		{
			CAMx += 0.5/zoom;
			if (Window.KeyPressed(GLFW_KEY_LEFT_SHIFT))
			{
				CAMx += 1.0 / zoom;
			}
		}
		if (Window.KeyPressed(GLFW_KEY_D))
		{
			CAMx -= 0.5/zoom;
			if (Window.KeyPressed(GLFW_KEY_LEFT_SHIFT))
			{
				CAMx -= 1.0 / zoom;
			}

		}
		if (Window.KeyPressed(GLFW_KEY_P))
		{
			zoom += 0.01*zoom;
			if (zoom >= DBL_MAX)
			{
				zoom = DBL_MAX;
			}
		}
		if (Window.KeyPressed(GLFW_KEY_O))
		{
			zoom -= 0.01*zoom;
			if (zoom < 0)
			{
				zoom = 0;
			}
		}
		if (Window.KeyPressed(GLFW_KEY_ESCAPE))
		{	
			glfwTerminate();
			return false;
		}
		//if (Window.KeyPressed(GLFW_KEY_F11))

		if (Window.KeyPressed(GLFW_KEY_M))
		{
			bangle++;
		}
		//drawing
		tree1.DrawTree();

		WorldShader.disable();
		Window.update();
	}
	return 0;
}

Fractal_Tree.h


#pragma once

#ifndef FRACTALTREE_H
#define FRACTALTREE_H

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "../../Shaders/ShaderUtil/ShaderProgram.hpp"
#include "../../Maths/Math.hpp"

class Fractal_Tree
{
private:
	double b_angle;
	double b_mult;
	ShaderProgram TreeShader;
	int b_depth;
	glm::vec2 stemstart;
	glm::vec2 stemend;
	
public:
	Fractal_Tree(int n, int angle, glm::vec2 p1, glm::vec2 p2, double mult,int branchcount);
	~Fractal_Tree();
	void DrawTree();
	
};

#endif


Fractal_Tree.cpp



#include "Fractal_Tree.h"

//ShaderProgram TreeShader;

//Tree Definition
Fractal_Tree::Fractal_Tree(int n, int angle, glm::vec2 p1, glm::vec2 p2, double mult,int branchcount)
{	
	

	b_angle = angle;
	b_depth = n;
	b_mult = mult;
	stemstart.x = p1.x;
	stemstart.y = p1.y;
	stemend.x = p2.x;
	stemend.y = p2.y;
	
}


Fractal_Tree::~Fractal_Tree()
{
	
}

void Fractal_Tree::DrawTree()
{


	unsigned long long int sumofpoints = pow(2, b_depth + 1) - 1;

	glm::vec2 midbranch;

	midbranch.x = (stemend.x - stemstart.x)* b_mult;
	midbranch.y = (stemend.y - stemstart.y)* b_mult;

	
	//test
	double x3r = midbranch.x * cos(b_angle * (M_PI / 180)) + midbranch.y * sin(b_angle *(M_PI / 180)) + stemend.x;
	double y3r = -midbranch.x * sin(b_angle * (M_PI / 180)) + midbranch.y * cos(b_angle *(M_PI / 180)) + stemend.y;
	double x3l = midbranch.x * cos(-b_angle * (M_PI / 180)) + midbranch.y * sin(-b_angle * (M_PI / 180)) + stemend.x;
	double y3l = -midbranch.x * sin(-b_angle * (M_PI / 180)) + midbranch.y * cos(-b_angle * (M_PI / 180)) + stemend.y;

	//stem coords
	GLdouble stem[] =
	{
		stemstart.x,stemstart.y,0,
		stemend.x,stemend.y,0,
	};


	GLuint root_VBO;
	glGenBuffers(1, &root_VBO);
	glBindBuffer(GL_ARRAY_BUFFER, root_VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(stem), &stem, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	glBindVertexArray(0);

	glDrawArrays(GL_LINE_STRIP, 0, 2);


	

	/* GLfloat test[] =
	{
		3,3,0,
		6,6,0,
	};


	GLuint test_VBO;
	glGenBuffers(1, &test_VBO);
	glBindBuffer(GL_ARRAY_BUFFER, test_VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(test), &test, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
	glBindVertexArray(0);

	glDrawArrays(GL_LINES, 0, 4);
	*/


	//glGenTransformFeedbacks(1, &branch_tff);
	//lBindTransformFeedback(GL_TRANSFORM_FEEDBACK, branch_tff);
	//glBeginTransformFeedback(GL_LINES);
	//glDrawArrays(GL_LINE_STRIP,false,b_depth);
	//glEndTransformFeedback();
	//glFlush();

}
	

Take into consideration that branchcount is experimental value and can be discarded if needed.

The questions are:
1.Is it possible to have multiple shader programs ?
2.Which shader should i use to calculate coordinates of the new branch points?

The problems which i am facing are:
1.Unability to render fractal.
2.Confusion between which type of shader should i use for this particular task.
3.Lack of experience with modern openGL

And also i would like to ask how can i implement transform feedback in my code ?

A couple of things:

It looks like you’ve got just a vertex and fragment shader, which is great, and yes, you can have a fractal engine with the CPU doing all the work, so you don’t NEED any other shaders necessarily.

Having said that… you will probably want to learn more about the geometry shader (possibly tessellation shaders) and transform feedback, but I’ve been studying opengl for 2 years now, and those, in my humble opinion, are advanced topics (at least well into the intermediate topic range anyway…). I’m just now beginning to approach these topics…

The other thing is this: What have you been able to render so far? The reason I ask is… you may want to try a simpler project first, perhaps a bunch of simpler projects, leading you up to your final destination, the fractal engine that you want. Judging from your code so far, it looks like you are well on your way. The main reason I recommend simple projects building to a larger one, is because: starting with a simple project puts something on the screen… then you can add/change it at will, so you’re setting your clay in position, then molding it slowly over time.

Also, if would be good to see your shader code, as it is not listed here.

Just a few observations and a suggestion,

Jeff

P.S. Check out my youtube channel, dedicated to opengl stuff:

https://www.youtube.com/channel/UCzx8alrxVELz5h1dfCdkdfg/videos?view_as=subscriber

[QUOTE=OceanJeff40;1293257]A couple of things:

It looks like you’ve got just a vertex and fragment shader, which is great, and yes, you can have a fractal engine with the CPU doing all the work, so you don’t NEED any other shaders necessarily.

Having said that… you will probably want to learn more about the geometry shader (possibly tessellation shaders) and transform feedback, but I’ve been studying opengl for 2 years now, and those, in my humble opinion, are advanced topics (at least well into the intermediate topic range anyway…). I’m just now beginning to approach these topics…

The other thing is this: What have you been able to render so far? The reason I ask is… you may want to try a simpler project first, perhaps a bunch of simpler projects, leading you up to your final destination, the fractal engine that you want. Judging from your code so far, it looks like you are well on your way. The main reason I recommend simple projects building to a larger one, is because: starting with a simple project puts something on the screen… then you can add/change it at will, so you’re setting your clay in position, then molding it slowly over time.

Also, if would be good to see your shader code, as it is not listed here.

Just a few observations and a suggestion,

Jeff

P.S. Check out my youtube channel, dedicated to opengl stuff:

https://www.youtube.com/channel/UCzx8alrxVELz5h1dfCdkdfg/videos?view_as=subscriber[/QUOTE]

Thank you for the response @OceanJeff40

Well for now the project only renders a line which you can rotate,move to,and zoom into it while it also pulsates the green color in sin interval.

As for simpler project suggestion I may try to code one particular fractal object centered program as Mandelbrot or Sierpinski ,but the most problematic question is while I am doing the multiple fractal object program how should I create my shaders ? Because the problem is that if I remove the main vertex shader I lose the capability to zoom and move to the object which are crucial for any self - respecting fractal object.Also while thinking how I should tackle previously mentioned problem because i only could see 2 solutions which is either:

  1. Have an separate vertex shader files for each fractal.
  2. Have an uniform int id in vertex shader to identify fractals and apply formulas individually while still keeping and executing orthographic projection matrices.

Furthermore I don’t want to make my fractal engine to be CPU-centered because my most yearned goal for this project to make it have the best performance i can get.
In addition which shader could create new coordinate points in the space?

So there are some shader code for you to look up to.
They are extremely simple.

main.vert


#version 460 core

uniform int id;

layout(location = 0)
in vec4 position;


uniform mat4 pro_matrix; 
uniform mat4 view_matrix = mat4(1.0);
uniform mat4 model_matrix = mat4(1.0);
uniform mat4 scale_matrix = mat4(1.0);


void main()
{
	
	gl_Position = pro_matrix * (model_matrix * view_matrix * scale_matrix) * position;

	if(id == 1)
	{
		

	}
	if(id == 2)
	{
		
	}
	if(id == 3)
	{
		
	}
	if(id == 4)
	{
		
	}

}

frag.main


#version 460 core



out vec4 color;
uniform vec4 OutColor ;


void main()
{
	color = OutColor;
}

Take into consideration that i am using the latest OpenGL version (4.60).

I agree, you should have two separate shader programs, if that’s what you require, but be sure to glUseProgram(<your Shader>), before each draw call, so that you are using each shader program for each draw call.

Anyways, I was saying that you could use the CPU for computing, until you understand what shaders will perform what calculations for you, and what those results will be. For now, getting everything running on the CPU side, and pushing the vertices to the GPU for drawing, may be the way to go. Then you can move more stuff over to the shaders later, after you know what they will do. And what transform feedback will give you.

The geometry shader takes your vertices as input, and you can create many, many vertices (vertex amplification) with it. You can also just transform geometry with it, for example: triangles into points. I did this, in my latest video example, it was a very simple demo of a geometry shader from OpenGL Superbible.

Have you seen this website?

https://www.shadertoy.com/

Anyways, it shows code along with shaders, some more advanced than others, but all of them cool!

Alright dude, stay motivated and excited,

Jeff

UPDATE:

As from now i have been researching and trying to render fractal (binary) tree in my program and the closest i got to render my object was with the use of geometry shader and for loop in the main of geometry shader.
But i think i got the problem which could concern me until i will get to the more suitable and proper method to render my binary fractal tree.

The problem is that model matrix distorts fractal object when i move my camera.
For example.
Before:
Screenshot%20(3)

After:
Screenshot%20(4)

The question is what could i do to prevent this distortion in my 2D fractal object.

Also there is my main.geom

#version 460 core

layout (points) in;
layout (line_strip) out;
layout (max_vertices = 256) out;

uniform mat4 pro_matrix; 
uniform mat4 view_matrix = mat4(1.0);
uniform mat4 model_matrix = mat4(1.0);
uniform mat4 scale_matrix = mat4(1.0);
uniform vec2 stemstartuni;
uniform vec2 stemenduni;
uniform int angle;
uniform int depth;
vec2 stemstart = stemstartuni;
vec2 stemend = stemenduni;


void main()
{
	mat4 mvs = model_matrix * view_matrix * scale_matrix;
	mat4 mvsp = pro_matrix * mvs;

	vec2 stemstart = stemstartuni;
	vec2 stemend = stemenduni;


	gl_Position = gl_in[0].gl_Position + vec4(stemstart, 0.0, 0.0) * mvsp;
    EmitVertex();

    gl_Position = gl_in[0].gl_Position + vec4(stemend, 0.0, 0.0) * mvsp;
    EmitVertex();

	for(int i = 0; i < depth; i++)
	{

	vec2 rightbranch;
	vec2 midbranch;
	

	midbranch.x = (stemend.x - stemstart.x)*0.7;
	midbranch.y = (stemend.y - stemstart.y)*0.7;
	
	rightbranch.x = midbranch.x*cos(radians(angle)) + midbranch.y*sin(radians(angle)) + stemend.x;
	rightbranch.y = -midbranch.x*sin(radians(angle)) + midbranch.y*cos(radians(angle)) + stemend.y;

	gl_Position = gl_in[0].gl_Position + vec4(rightbranch,0.0,0.0) * mvsp;
	EmitVertex();

	stemstart = stemend;
	stemend = rightbranch;

	}

    EndPrimitive();


	

}

PS:
main.geom is a prototype shader.
Edit:
And also only the model matrix doesn’t work.
So that means I can rotate and scale my object without problem but moving to particular point of the object is impossible without distortion.