Is it possible to draw 2D fractals in Modern Opengl ? (Primitive based)

Well i’ve got a problem while making my Fractal graphics engine.
Which is made entirely on Modern OpenGL because this engine has to fit from simplest of 2D fractals to the most complex 3D fractals in Real-Time.
The problem is that in Old graphics pipeline there would be

glBegin(x,y),glEnd()

.With these commands i could repeat them recursively.
But in New Pipeline there is only array,shader and vbo based drawing with

 glDrawArray 

.
How can i recurse or even iterate array coordinates to “infinity” ?
For now i am trying to implement 2D Fractal Tree.
Next objects would be Sierpinski Triangle,Fern,Koch snowflake.
2D Objects in Modern engine would be shown in Orthographic projection and 3D Objects would be in Perspective projection.
And all of them use Fragment and Vertex shaders.For color managment and interpolation.

There is old code of my Fractal Tree made with old graphics Pipeline.


#include<iostream>
#include<cmath>
#include <GL/GLUT.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

double rtd=M_PI/180; //RADTODEG
// cosine
// cos atsako uz x
// sin atsako uz y


void sakosr(float px1,float py1,float px2,float py2,int kamp,int n,double mult){
	glBegin(GL_LINE_LOOP); // Linijų tipas
	glVertex2f(px2,py2);
	float px3=(px2-px1)*mult;
	float py3=(py2-py1)*mult;
	GLfloat px3r=px3*cos(kamp*rtd)+py3*sin(kamp*rtd)+px2;
	GLfloat py3r=-px3*sin(kamp*rtd)+py3*cos(kamp*rtd)+py2;
	glVertex2f(px3r,py3r);
	glVertex2f(px2,py2);
	GLfloat px3l=px3*cos(-kamp*rtd)+py3*sin(-kamp*rtd)+px2;
	GLfloat py3l=-px3*sin(-kamp*rtd)+py3*cos(-kamp*rtd)+py2;
	glVertex2f(px3l,py3l);
	px1=px2;
	py1=py2;
	px2=px3r;
	py2=py3r;
	glEnd();
	if(n>0){
		sakosr(px1,py1,px2,py2,kamp,n-1,mult);
		px2=px3l;
		py2=py3l;
		sakosr(px1,py1,px2,py2,kamp,n-1,mult);
	}
}

With glTransformfeedbackvaryings, that are shader-varyings (like i.e. gl_vertex).

I’ve just lerned it myself with help of this forum, so i take the time to anwer you. Mybe this helps a bit:

On my system, there was a flag to be set before ibitializing OpenGL. Since i init with:


    glewExperimental= TRUE;        // needed for glew version under 1.3  -  before glewInit()                 
    GLenum err      = glewInit();                                                                
    if (GLEW_OK    != err) {    wxMessageBox("FATAL ERROR:
"+wxString(glewGetErrorString(err)));           exit(0);    } 

…glTransformfeedbackvaryings doesn’t crash my program anyomre.

You don’t need a fragment-shader (you want to retrieve / calculate) vertices. Call glDisgardrastarizer(GL_TRUE); in order to do this.

On my systen (there were some discussions about this point), a fragement-shader has to be used anyway to prevent the shader-compiler to remove varyings that are not used.

The basic method for iterating one(!) array is this:

  • use i.e. “in float tff_in[8];” and “out float tff_out[8];” in your vertex-shader.
  • compile it and attach it to the program
  • call glTransformfeedbackvaryings(“tff_out”);
  • call glDiscardrastarizer(GL_TRUE); - might also be done later, i think
    (-attach maybe fragment-shader - on my system no result without “in float tff_out[8];
    out vec4 color;
    void main(){
    color[0]=tff_out[0];
    };”)
  • link program

After this, you have to think about the input, which is as simple as tricky. The point is that you have (in my example) an array eight floats named “tff_in”.
What yo’ll have to do is to apply at first the existence on an input named “tff_in” and after this a pointer to each of the array-items. Here is my code doing this:


    GLint tffInput          = glGetAttribLocation (curPRG, (char*)list[3]);             CheckGL("glGetAttribLocation");  
    if   (tffInput < 0)       return;

    for  (int i = 0; i < numAttribs; i++) {   
        glEnableVertexAttribArray (tffInput + i);                                       CheckGL("glEnableVertexAttribArray"); 
        glVertexAttribPointer     (tffInput + i, 1, GL_FLOAT, GL_FALSE, byteStride, (GLvoid*) (i * sizeof(GLfloat)) );     CheckGL("glVertexAttribPointer"); 
    }

This was preparation. Now i to do someting like this:


void clsVAO::Refeed () {                                                                CheckGL("Errors left in cue");

// Create destination
    GLuint dst;
    glGenBuffers            (1, &dst);
    glBindBuffer            (GL_ARRAY_BUFFER, dst);
    glBufferData            (GL_ARRAY_BUFFER, bufBytes, NULL, GL_STATIC_DRAW);          CheckGL("TBO-Creation");  
        
// PROCESS:                                                                         
    glEnable                (GL_RASTERIZER_DISCARD);                                    CheckGL("glEnable(GL_RASTERIZER_DISCARD)"); 
    glBindBufferBase        (GL_TRANSFORM_FEEDBACK_BUFFER, 0, dst);                     CheckGL("glBindBufferBase"); 

    glBeginTransformFeedback(GL_POINTS);                                                CheckGL("glBeginTransformFeedback");              
        glDrawArrays        (GL_POINTS, 0, NumV);                                       CheckGL("glDrawArrays"); 
    glEndTransformFeedback  ();                                                         CheckGL("glEndTransformFeedback"); 

    glFlush                 ();                                                         CheckGL("glFlush"); 
   
// Swap    
    GLuint tmp = VBO;       VBO = dst; 
    glDeleteBuffers         (1,  &tmp);
    
// Clear
    glBindBuffer            (GL_ARRAY_BUFFER, 0);
    glDisable               (GL_RASTERIZER_DISCARD);                                    CheckGL("glEnable(GL_RASTERIZER_DISCARD)"); 
    glUseProgram            (0);        
}

What results is an vertex-array-feedback. So, each of the inputs aof your fractal (like x,y,r,i with Mandebrodt), can be iterated by the shader-program doing someting with the incoming values and write the result to output in the same order (tff_in[3]->tff_out[3]).

Also you may look here: https://open.gl/feedback, which is short and helped a lot…

This was a path a to the start first steps, but it’s in the end simple. But note: Evrything that is not copy-pasted is wrote out of my head (just lerned some days ago). So no guaraty, for anything.

Best,
Frank

PS. Good idea. I’ll progam a Mandebrodt-vertex-shader now - just for testing, what i leaned the last days…

[QUOTE=art-ganseforth;1292707]With glTransformfeedbackvaryings, that are shader-varyings (like i.e. gl_vertex).

I’ve just lerned it myself with help of this forum, so i take the time to anwer you. Mybe this helps a bit:

On my system, there was a flag to be set before ibitializing OpenGL. Since i init with:


    glewExperimental= TRUE;        // needed for glew version under 1.3  -  before glewInit()                 
    GLenum err      = glewInit();                                                                
    if (GLEW_OK    != err) {    wxMessageBox("FATAL ERROR:
"+wxString(glewGetErrorString(err)));           exit(0);    } 

…glTransformfeedbackvaryings doesn’t crash my program anyomre.

You don’t need a fragment-shader (you want to retrieve / calculate) vertices. Call glDisgardrastarizer(GL_TRUE); in order to do this.

On my systen (there were some discussions about this point), a fragement-shader has to be used anyway to prevent the shader-compiler to remove varyings that are not used.

The basic method for iterating one(!) array is this:

  • use i.e. “in float tff_in[8];” and “out float tff_out[8];” in your vertex-shader.
  • compile it and attach it to the program
  • call glTransformfeedbackvaryings(“tff_out”);
  • call glDiscardrastarizer(GL_TRUE); - might also be done later, i think
    (-attach maybe fragment-shader - on my system no result without “in float tff_out[8];
    out vec4 color;
    void main(){
    color[0]=tff_out[0];
    };”)
  • link program

After this, you have to think about the input, which is as simple as tricky. The point is that you have (in my example) an array eight floats named “tff_in”.
What yo’ll have to do is to apply at first the existence on an input named “tff_in” and after this a pointer to each of the array-items. Here is my code doing this:


    GLint tffInput          = glGetAttribLocation (curPRG, (char*)list[3]);             CheckGL("glGetAttribLocation");  
    if   (tffInput < 0)       return;

    for  (int i = 0; i < numAttribs; i++) {   
        glEnableVertexAttribArray (tffInput + i);                                       CheckGL("glEnableVertexAttribArray"); 
        glVertexAttribPointer     (tffInput + i, 1, GL_FLOAT, GL_FALSE, byteStride, (GLvoid*) (i * sizeof(GLfloat)) );     CheckGL("glVertexAttribPointer"); 
    }

This was preparation. Now i to do someting like this:


void clsVAO::Refeed () {                                                                CheckGL("Errors left in cue");

// Create destination
    GLuint dst;
    glGenBuffers            (1, &dst);
    glBindBuffer            (GL_ARRAY_BUFFER, dst);
    glBufferData            (GL_ARRAY_BUFFER, bufBytes, NULL, GL_STATIC_DRAW);          CheckGL("TBO-Creation");  
        
// PROCESS:                                                                         
    glEnable                (GL_RASTERIZER_DISCARD);                                    CheckGL("glEnable(GL_RASTERIZER_DISCARD)"); 
    glBindBufferBase        (GL_TRANSFORM_FEEDBACK_BUFFER, 0, dst);                     CheckGL("glBindBufferBase"); 

    glBeginTransformFeedback(GL_POINTS);                                                CheckGL("glBeginTransformFeedback");              
        glDrawArrays        (GL_POINTS, 0, NumV);                                       CheckGL("glDrawArrays"); 
    glEndTransformFeedback  ();                                                         CheckGL("glEndTransformFeedback"); 

    glFlush                 ();                                                         CheckGL("glFlush"); 
   
// Swap    
    GLuint tmp = VBO;       VBO = dst; 
    glDeleteBuffers         (1,  &tmp);
    
// Clear
    glBindBuffer            (GL_ARRAY_BUFFER, 0);
    glDisable               (GL_RASTERIZER_DISCARD);                                    CheckGL("glEnable(GL_RASTERIZER_DISCARD)"); 
    glUseProgram            (0);        
}

What results is an vertex-array-feedback. So, each of the inputs aof your fractal (like x,y,r,i with Mandebrodt), can be iterated by the shader-program doing someting with the incoming values and write the result to output in the same order (tff_in[3]->tff_out[3]).

Also you may look here: https://open.gl/feedback, which is short and helped a lot…

This was a path a to the start first steps, but it’s in the end simple. But note: Evrything that is not copy-pasted is wrote out of my head (just lerned some days ago). So no guaraty, for anything.

Best,
Frank

PS. Good idea. I’ll progam a Mandebrodt-vertex-shader now - just for testing, what i leaned the last days…[/QUOTE]

Hi Frank thanks for quick reply but your code is viable only for iterative fractals (Mandelbrot .etc)
The problem is that I want to draw recursive fractals like fractal tree fern…

Multiple passes with transform feedback. Start with a set of vertex arrays containing a single identity matrix. Each pass uses instanced rendering to generate N copies of the data, each copy transformed by a different matrix. After k passes, you’ll have N^k matrices. Use a separate pass for rendering.

Optionally use a geometry shader to discard matrices whose determinant is below a given threshold, so the leaf nodes have roughly constant size rather than constant depth in the tree. In this case, the vertex arrays should contain each matrix’ determinant in addition to the matrix, and compute determinants as |AB|=|A||B|.

GClements thank you for reply im going to try out your method of transform feedback to have n branches on the fractal tree.

It would be good if you (Or Somebody) could give me extensive transform feedback example or even geometry shader one to analyze, because this is the first time i’m using this method.
And yes I’m still a newbie in OpenGL even more so in Modern OpenGL