Here’s a quick-and-dirty GL2.0 mandelbrot/julia explorer that I wrote a while back using GLUT:
//
// psmand - gl2.0 mandelbrot/julia shader demo
//
// Author: Edward Hutchins, Jan 2006
// License: Public Domain
//
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glut.h>
static const GLint SCREEN_W = 600;
static const GLint SCREEN_H = 600;
static const GLfloat NEAR_W = 0.1;
static const GLfloat FAR_W = 10.0;
static GLint screen_w = SCREEN_W;
static GLint screen_h = SCREEN_H;
// mandelbrot center
static GLfloat cx = 0;
static GLfloat cy = -0.5;
// zoom to edges of rect
static GLfloat zoom = 1.5;
// mandelbrot shader
static GLuint hMandProg = 0;
// julia shader
static GLuint hJuliaProg = 0;
// uniform seed
static GLint julia_c_pos = 0;
static bool bDragging = false;
static int nDragPt[2];
static bool bJuliaing = false;
static int nJuliaPt[2];
#define FAIL(x) do { printf x; printf( "
" ); exit( 1 ); } while (0)
void display()
{
glClearColor( 0, 0, 0, 1 );
glClear( GL_COLOR_BUFFER_BIT );
glColor4f( 1, 0, 0, 1 );
GLfloat left = cx - zoom;
GLfloat right = cx + zoom;
GLfloat top = cy - zoom;
GLfloat bottom = cy + zoom;
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( left, bottom );
glVertex2f( -1, -1 );
glTexCoord2f( right, bottom );
glVertex2f( 1, -1 );
glTexCoord2f( left, top );
glVertex2f( -1, 1 );
glTexCoord2f( right, top );
glVertex2f( 1, 1 );
glEnd();
glutSwapBuffers();
}
void make_tex()
{
const int TEX_SIZE = 1024;
GLfloat tex[TEX_SIZE][4];
// create a rainbow lookup table for coloring the iteration count
// (could be any function you like)
for (int x = 0; x < TEX_SIZE - 1; ++x)
{
float f = 3.1415927 * x / TEX_SIZE;
float c = cos( f );
float s = sin( f );
tex[x][0] = 1 - c;
tex[x][1] = s * s;
tex[x][2] = c;
tex[x][3] = 1;
}
tex[TEX_SIZE - 1][0] = tex[TEX_SIZE - 1][1] = tex[TEX_SIZE - 1][2] = tex[TEX_SIZE - 1][3] = 0;
glTexImage1D( GL_TEXTURE_1D, 0, GL_RGBA, TEX_SIZE, 0, GL_RGBA, GL_FLOAT, tex );
}
GLuint make_frag_prog( const GLchar *pcszShader )
{
GLuint hShader = glCreateShader( GL_FRAGMENT_SHADER );
if (!hShader) FAIL(( "Can't create shader!" ));
const GLchar *ppcszShader[1] = { pcszShader };
glShaderSource( hShader, 1, ppcszShader, NULL );
glCompileShader( hShader );
GLint nStatus;
glGetShaderiv( hShader, GL_COMPILE_STATUS, &nStatus );
if (!nStatus)
{
char szBuff[10240];
glGetShaderInfoLog( hShader, sizeof(szBuff), NULL, szBuff );
FAIL(( "Shader failed to compile:
%s", szBuff ));
}
else printf( "Shader compiled okay!
" );
GLuint hProgram = glCreateProgram();
if (!hProgram) FAIL(( "Can\'t create program!" ));
glAttachShader( hProgram, hShader );
glLinkProgram( hProgram );
glGetProgramiv( hProgram, GL_LINK_STATUS, &nStatus );
if (!nStatus)
{
char szBuff[10240];
glGetProgramInfoLog( hProgram, sizeof(szBuff), NULL, szBuff );
FAIL(( "Program failed to link:
%s", szBuff ));
}
else printf( "Program linked okay!
" );
return( hProgram );
}
void init()
{
make_tex();
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glEnable( GL_TEXTURE_1D );
//glAlphaFunc( GL_GEQUAL, 0.5 );
//glEnable( GL_ALPHA_TEST );
hMandProg = make_frag_prog(
"uniform sampler1D myTexture;
"
"void main( void )
"
"{
"
" // swap x,y on texcoords to get nice orientation (should be via viewport)
"
" vec2 p; p.x = p.y = 0;
"
" vec2 c = vec2( gl_TexCoord[0].yx );
"
" float iters = 0;
"
" while ((iters < 1) && (dot( p, p ) < 4))
"
" {
"
" float nx = p.x * p.x - p.y * p.y + c.x;
"
" float ny = 2 * p.x * p.y + c.y;
"
" p.x = nx; p.y = ny;
"
" iters += 1 / 255.0;
"
" }
"
" gl_FragColor = texture1D( myTexture, iters );
"
"}
"
);
hJuliaProg = make_frag_prog(
"uniform sampler1D myTexture;
"
"uniform vec2 c;
"
"void main( void )
"
"{
"
" // swap x,y on texcoords to get nice orientation (should be via viewport)
"
" vec2 p = vec2( gl_TexCoord[0].yx );
"
" float iters = 0;
"
" while ((iters < 1) && (dot( p, p ) < 4))
"
" {
"
" float nx = p.x * p.x - p.y * p.y + c.x;
"
" float ny = 2 * p.x * p.y + c.y;
"
" p.x = nx; p.y = ny;
"
" iters += 1 / 255.0;
"
" }
"
" gl_FragColor = texture1D( myTexture, iters );
"
"}
"
);
julia_c_pos = glGetUniformLocation( hJuliaProg, "c" );
if (julia_c_pos < 0)
{
FAIL(( "Failed find julia shader \"c\" uniform!
" ));
}
printf( "julia_c_pos = %d!
", julia_c_pos );
glUseProgram( hMandProg );
}
void reshape( int w, int h )
{
screen_w = w;
screen_h = h;
glViewport( 0, 0, w, h );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( -1, 1, -1, 1, -1, 1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
static void motion( int x, int y )
{
if (bDragging)
{
int dx = x - nDragPt[0];
int dy = y - nDragPt[1];
cx -= (2 * zoom / screen_w) * dx;
cy -= (2 * zoom / screen_h) * dy;
nDragPt[0] = x;
nDragPt[1] = y;
glutPostRedisplay();
}
if (bJuliaing)
{
int dx = x - nJuliaPt[0];
int dy = y - nJuliaPt[1];
GLfloat jx = cx + (2 * zoom / screen_w) * dx;
GLfloat jy = cy + (2 * zoom / screen_h) * dy;
// backwards, yech
//glUniform2f( julia_c_pos, jx, jy );
glUniform2f( julia_c_pos, jy, jx );
glutPostRedisplay();
}
}
static void mouse( int button, int state, int x, int y )
{
switch (button)
{
case GLUT_LEFT_BUTTON:
bDragging = (state == GLUT_DOWN);
nDragPt[0] = x;
nDragPt[1] = y;
if (bDragging) glutPostRedisplay();
break;
case GLUT_RIGHT_BUTTON:
if (state == GLUT_DOWN)
{
bJuliaing = true;
glUseProgram( hJuliaProg );
// first point is at current center
nJuliaPt[0] = screen_w / 2;
nJuliaPt[1] = screen_h / 2;
}
else if (state != GLUT_DOWN)
{
bJuliaing = false;
glUseProgram( hMandProg );
}
glutPostRedisplay();
break;
case 3:
if (state == GLUT_DOWN)
{
zoom *= 0.9;
if (zoom < 1e-9) zoom = 1e-9;
glutPostRedisplay();
}
break;
case 4:
if (state == GLUT_DOWN)
{
zoom *= 1.1;
if (zoom > 2) zoom = 2;
glutPostRedisplay();
}
break;
default:
printf( "button %d %s
", button, (state == GLUT_DOWN) ? "down" : "up" );
break;
}
}
static void keyboard( unsigned char c, int x, int y )
{
switch (c)
{
case 27:
exit( 0 );
break;
}
}
int main( int argc, char *argv[] )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( SCREEN_W, SCREEN_H );
glutCreateWindow( "GL 2.0 test" );
glutReshapeFunc( reshape );
glutDisplayFunc( display );
glutKeyboardFunc( keyboard );
glutMouseFunc( mouse );
glutMotionFunc( motion );
init();
glutMainLoop();
return( 0 );
}
The Makefile that works for me under Linux:
all: psmand
%: %.cpp
g++ -o $@ $< -L/usr/X11R6/lib -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm