how to convert simple opengl line plotting code to use vertex array - c
I wrote a simple opengl application in C which plots sin(x). This is my current draw function which runs very slow. How do I have to convert this code to make use of the faster 'vertex array' mode?
list of variables and functions used:
N = total number of points
x1 = min(x)
x2 = max(x)
y1 = min(y)
y2 = max(y)
func(x) = sin(x)
and here's the entire code:
/* to compile, do:
$ gcc -o out simple.c -lglut
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>
#include <time.h>
float xmin = -10, xmax = 10, ymin = -5, ymax = 5;
int nPoints = 3000;
/* function to calculate each data point */
float func(float x)
{
return sin(x);
}
/* plotting function - very slow */
void draw(float (* func)(float x), float x1, float x2, float y1, float y2, int N)
{
float x, dx = 1.0/N;
glPushMatrix();
glScalef(1.0 / (x2 - x1), 1.0 / (y2 - y1), 1.0);
glTranslatef(-x1, -y1, 0.0);
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_STRIP);
for(x = x1; x < x2; x += dx)
{
glVertex2f(x, func(x));
}
glEnd();
glPopMatrix();
};
/* Redrawing func */
void redraw(void)
{
clock_t start = clock();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// -x, +x, -y, +y, number points
draw(func, xmin, xmax, ymin, ymax, nPoints);
glutSwapBuffers();
printf("Time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);
};
/* Idle proc. Redisplays, if called. */
void idle(void)
{
// shift 'xmin' & 'xmax' by one.
xmin++;
xmax++;
glutPostRedisplay();
};
/* Key press processing */
void key(unsigned char c, int x, int y)
{
if(c == 27) exit(0);
};
/* Window reashape */
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
};
/* Main function */
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutCreateWindow("Graph plotter");
glutReshapeWindow(1024, 800);
glutPostRedisplay(); // This call may or may not be necessary
/* Register GLUT callbacks. */
glutDisplayFunc(redraw);
glutKeyboardFunc(key);
glutReshapeFunc(reshape);
glutIdleFunc(idle);
/* Init the GL state */
glLineWidth(2.0);
/* Main loop */
glutMainLoop();
return 0;
}
In terms of LodePNG's C-style vector struct/functions:
// shared
vector pts;
vector_init( &pts, sizeof( float ) );
// whenever x1, x2, or N changes
vector_cleanup( &pts );
float x, dx = 1.0/N;
for(x = x1; x < x2; x += dx)
{
vector_resize( &pts, pts.size + 2 );
*(float*)vector_get( &pts, pts.size-2 ) = x;
*(float*)vector_get( &pts, pts.size-1 ) = func(x);
}
// whenever you want to draw
glPushMatrix();
glScalef(1.0 / (x2 - x1), 1.0 / (y2 - y1), 1.0);
glTranslatef(-x1, -y1, 0.0);
glColor3f(1.0, 1.0, 1.0);
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, (float*)pts.data );
glDrawArrays( GL_LINE_STRIP, 0, pts.size / 2 );
glDisableClientState( GL_VERTEX_ARRAY );
glPopMatrix();
EDIT: Complete code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>
#include <time.h>
typedef struct vector /*dynamic vector of void* pointers. This one is used only by the deflate compressor*/
{
void* data;
size_t size; /*in groups of bytes depending on type*/
size_t allocsize; /*in bytes*/
unsigned typesize; /*sizeof the type you store in data*/
} vector;
static unsigned vector_resize(vector* p, size_t size) /*returns 1 if success, 0 if failure ==> nothing done*/
{
if(size * p->typesize > p->allocsize)
{
size_t newsize = size * p->typesize * 2;
void* data = realloc(p->data, newsize);
if(data)
{
p->allocsize = newsize;
p->data = data;
p->size = size;
}
else return 0;
}
else p->size = size;
return 1;
}
static void vector_cleanup(void* p)
{
((vector*)p)->size = ((vector*)p)->allocsize = 0;
free(((vector*)p)->data);
((vector*)p)->data = NULL;
}
static void vector_init(vector* p, unsigned typesize)
{
p->data = NULL;
p->size = p->allocsize = 0;
p->typesize = typesize;
}
static void* vector_get(vector* p, size_t index)
{
return &((char*)p->data)[index * p->typesize];
}
float xmin = -10, xmax = 10, ymin = -5, ymax = 5;
int nPoints = 3000;
vector pts;
/* function to calculate each data point */
float func(float x)
{
return sin(x);
}
void update(float (* func)(float x), float x1, float x2, int N)
{
float x, dx = 1.0/N;
vector_cleanup( &pts );
for(x = x1; x < x2; x += dx)
{
vector_resize( &pts, pts.size + 2 );
*(float*)vector_get( &pts, pts.size-2 ) = x;
*(float*)vector_get( &pts, pts.size-1 ) = func(x);
}
}
/* plotting function - very slow */
void draw(float x1, float x2, float y1, float y2)
{
glPushMatrix();
glScalef(1.0 / (x2 - x1), 1.0 / (y2 - y1), 1.0);
glTranslatef(-x1, -y1, 0.0);
glColor3f(1.0, 1.0, 1.0);
if( pts.size > 0 )
{
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, (float*)pts.data );
glDrawArrays( GL_LINE_STRIP, 0, pts.size / 2 );
glDisableClientState( GL_VERTEX_ARRAY );
}
glPopMatrix();
};
/* Redrawing func */
void redraw(void)
{
clock_t start = clock();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// -x, +x, -y, +y, number points
draw(xmin, xmax, ymin, ymax);
glutSwapBuffers();
printf("Time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);
};
/* Idle proc. Redisplays, if called. */
void idle(void)
{
// shift 'xmin' & 'xmax' by one.
xmin++;
xmax++;
update(func, xmin, xmax, nPoints);
glutPostRedisplay();
};
/* Key press processing */
void key(unsigned char c, int x, int y)
{
if(c == 27) exit(0);
};
/* Window reashape */
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
};
/* Main function */
int main(int argc, char **argv)
{
vector_init( &pts, sizeof( float ) );
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutCreateWindow("Graph plotter");
glutReshapeWindow(1024, 800);
glutPostRedisplay(); // This call may or may not be necessary
/* Register GLUT callbacks. */
glutDisplayFunc(redraw);
glutKeyboardFunc(key);
glutReshapeFunc(reshape);
glutIdleFunc(idle);
/* Init the GL state */
glLineWidth(2.0);
/* Main loop */
glutMainLoop();
return 0;
}
Related
OpenGL: The roll rotation has an unwanted impact on other rotations
I wrote a simple program in the C language using the GLEW, SDL and cglm libraries to draw a triangle with OpenGL 3.3. I would like to be able to rotate the camera around the 3 axis: Move the mouse to the left or right to rotate around the Y axis (the yaw rotation) Move the mouse to the down or up to rotate around the X axis (the pitch rotation) Press the Q or E key to rotate around the Z axis (the roll rotation) The problem: If I do a positive roll rotation (I press the E key) then a positive yaw rotation (move the mouse to the right) then this last rotation is rotating to the bottom of the screen instead of to the right of the screen. So, the roll rotation has an unwanted impact on other rotations. Do you know how I can fix that? The code in "main.c": #include <GL/glew.h> /* for glClear */ #include <SDL.h> /* for SDL_Init */ #include <cglm/cglm.h> /* for vec3 */ #include <fcntl.h> /* for open */ #include <unistd.h> /* for read */ #define BUF_SIZE 512 static int prog_create_shader(GLuint *shader, const char *spath, GLenum stype) { int fd = -1; int nbytes = -1; GLchar sbuf[BUF_SIZE]; const GLchar *ssrc = NULL; GLint ret = -1; GLchar log[BUF_SIZE]; /* read the shader source */ fd = open(spath, O_RDONLY); if(fd == -1) { SDL_Log("Error: open"); return -1; } nbytes = read(fd, &sbuf, BUF_SIZE); if(nbytes == -1) { SDL_Log("Error: read"); return -1; } sbuf[nbytes] = '\0'; ssrc = sbuf; if(close(fd) == -1) { SDL_Log("Error: close"); return -1; } /* create the shader */ *shader = glCreateShader(stype); glShaderSource(*shader, 1, &ssrc, NULL); glCompileShader(*shader); glGetShaderiv(*shader, GL_COMPILE_STATUS, &ret); if(ret == GL_FALSE) { glGetShaderInfoLog(*shader, BUF_SIZE, NULL, log); SDL_Log("Error: glGetShaderiv: %s", log); return -1; } return 0; } static int prog_create(GLuint *prog, const char *vspath, const char *fspath) { GLuint vshader = -1; GLuint fshader = -1; GLint ret = -1; GLchar log[BUF_SIZE]; /* create the vertex shader */ if(prog_create_shader(&vshader, vspath, GL_VERTEX_SHADER) == -1) { SDL_Log("Error: prog_create_shader (%s)", vspath); return -1; } /* create the fragment shader */ if(prog_create_shader(&fshader, fspath, GL_FRAGMENT_SHADER) == -1) { SDL_Log("Error: prog_create_shader (%s)", fspath); return -1; } /* create the program */ *prog = glCreateProgram(); glAttachShader(*prog, vshader); glAttachShader(*prog, fshader); glLinkProgram(*prog); glGetProgramiv(*prog, GL_LINK_STATUS, &ret); if(ret == GL_FALSE) { glGetProgramInfoLog(*prog, BUF_SIZE, NULL, log); SDL_Log("Error: glGetShaderiv: %s", log); return -1; } glDeleteShader(fshader); glDeleteShader(vshader); glUseProgram(*prog); return 0; } static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_front, vec3 camera_up) { float xoffset = xrel; float yoffset = yrel; float zoffset = zrel; float sensitivity = 0.1; xoffset *= sensitivity; yoffset *= sensitivity; zoffset *= sensitivity; *yaw += xoffset; *pitch += -yoffset; *roll += zoffset; vec3 front = { cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)), sin(glm_rad(*pitch)), sin(glm_rad(*yaw)) * cos(glm_rad(*pitch)) }; glm_vec3_normalize_to(front, camera_front); vec3 up = { cos(glm_rad(*roll)), sin(glm_rad(*roll)), 0 }; glm_vec3_normalize_to(up, camera_up); } static void input(int *loop, vec3 camera_position, vec3 camera_front, vec3 camera_up, float *yaw, float *pitch, float *roll) { SDL_Event event = {0}; float cameraSpeed = 0.01; vec3 tmp = {0}; const Uint8* keystates = NULL; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_MOUSEMOTION: input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_front, camera_up); break; case SDL_KEYDOWN: switch(event.key.keysym.sym) { case SDLK_ESCAPE: *loop = 0; break; } break; case SDL_WINDOWEVENT: switch(event.window.event) { case SDL_WINDOWEVENT_CLOSE: *loop = 0; break; case SDL_WINDOWEVENT_RESIZED: glViewport(0, 0, event.window.data1, event.window.data2); break; } break; } } keystates = SDL_GetKeyboardState(NULL); if(keystates[SDL_SCANCODE_Q]) { input_mouse(0, 0, -1, yaw, pitch, roll, camera_front, camera_up); } if(keystates[SDL_SCANCODE_E]) { input_mouse(0, 0, 1, yaw, pitch, roll, camera_front, camera_up); } if(keystates[SDL_SCANCODE_W]) { glm_vec3_scale(camera_front, cameraSpeed, tmp); glm_vec3_add(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_S]) { glm_vec3_scale(camera_front, cameraSpeed, tmp); glm_vec3_sub(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_A]) { glm_vec3_cross(camera_front, camera_up, tmp); glm_vec3_normalize(tmp); glm_vec3_scale(tmp, cameraSpeed, tmp); glm_vec3_sub(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_D]) { glm_vec3_cross(camera_front, camera_up, tmp); glm_vec3_normalize(tmp); glm_vec3_scale(tmp, cameraSpeed, tmp); glm_vec3_add(camera_position, tmp, camera_position); } } static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_front, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao) { /* declare variables */ mat4 view = GLM_MAT4_IDENTITY_INIT; mat4 projection = GLM_MAT4_IDENTITY_INIT; mat4 model = GLM_MAT4_IDENTITY_INIT; GLuint uniform = 0; vec3 tmp = {0}; int width = 0; int height = 0; /* clear */ glClearColor(1, 1, 1, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* update the view matrix */ glm_vec3_add(camera_position, camera_front, tmp); glm_lookat(camera_position, tmp, camera_up, view); uniform = glGetUniformLocation(prog, "view"); glUniformMatrix4fv(uniform, 1, GL_FALSE, view[0]); /* update the projection matrix */ SDL_GetWindowSize(window, &width, &height); glm_perspective(glm_rad(45), width / (float)height, 0.1, 100, projection); uniform = glGetUniformLocation(prog, "projection"); glUniformMatrix4fv(uniform, 1, GL_FALSE, projection[0]); /* update the model matrix */ glm_translate(model, triangle_position); uniform = glGetUniformLocation(prog, "model"); glUniformMatrix4fv(uniform, 1, GL_FALSE, model[0]); /* draw the triangle */ glBindVertexArray(triangle_vao); glDrawArrays(GL_TRIANGLES, 0, 3); SDL_GL_SwapWindow(window); } int main() { /* declare variables */ int loop = 1; SDL_DisplayMode dm = {0}; SDL_Window *window = NULL; SDL_GLContext glcontext = NULL; GLuint prog = 0; GLuint triangle_vao = 0; GLuint triangle_vbo = 0; vec3 camera_position = {0, 0, 3}; vec3 camera_front = {0, 0, -1}; vec3 camera_up = {0, 1, 0}; float yaw = -90; float pitch = 0; float roll = 90; float triangle_vertices[] = { -1, -1, +0, +0, +1, +0, +1, -1, +0 }; vec3 triangle_position = {0, 0, 0}; /* initialize SDL */ if(SDL_Init(SDL_INIT_VIDEO) != 0) { SDL_Log("Error: SDL_Init: %s", SDL_GetError()); return 1; } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); if(SDL_GetCurrentDisplayMode(0, &dm) < 0) { SDL_Log("Error: SDL_GetCurrentDisplayMode"); return 1; } window = SDL_CreateWindow("sdl2 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w, dm.h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if(window == NULL) { SDL_Log("Error: SDL_CreateWindow"); return 1; } glcontext = SDL_GL_CreateContext(window); SDL_SetRelativeMouseMode(SDL_TRUE); /* initialize Glew */ if(glewInit() != GLEW_OK) { SDL_Log("Error: glewInit"); return 1; } /* print information */ printf("screen width: %d\n", dm.w); printf("screen height: %d\n", dm.h); printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR)); printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER)); printf("GL_VERSION: %s\n", glGetString(GL_VERSION)); printf("GL_SHADING_LANGUAGE_VERSION: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); /* create the program */ if(prog_create(&prog, "vs.sha", "fs.sha") == -1) { SDL_Log("Error: prog_create"); return 1; } /* enable features */ glEnable(GL_DEPTH_TEST); /* create the vertex buffer object */ glGenBuffers(1, &triangle_vbo); glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW); /* create the vertex array object */ glGenVertexArrays(1, &triangle_vao); glBindVertexArray(triangle_vao); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); /* loop */ while(loop) { input(&loop, camera_position, camera_front, camera_up, &yaw, &pitch, &roll); display(window, prog, camera_position, camera_front, camera_up, triangle_position, triangle_vao); } /* free */ glDeleteBuffers(1, &triangle_vbo); glDeleteVertexArrays(1, &triangle_vao); glDeleteProgram(prog); SDL_GL_DeleteContext(glcontext); SDL_Quit(); return 0; } The code in "vs.sha" (the vertex shader): #version 330 core layout (location = 0) in vec3 pos; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(pos, 1); } The code in "fs.sha" (the fragment shader): #version 330 core out vec4 color; void main() { color = vec4(1, 0.5, 0, 1); } The result (video of the SDL window): https://youtu.be/XIfA6pRNi24 PATCH #1 I tried to apply the suggestion of RedRuin. Here's the patch: $ cat main.c.patch --- old/main.c 2022-07-25 06:36:48.165479193 +0200 +++ new/main.c 2022-07-26 01:53:55.221830761 +0200 ## -4,6 +4,21 ## #include <fcntl.h> /* for open */ #include <unistd.h> /* for read */ #define BUF_SIZE 512 +void rotate_camera_x(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right) +{ + glm_vec3_rotate(camera_forward, angle, camera_right); + glm_vec3_rotate(camera_up, angle, camera_right); +} +void rotate_camera_y(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right) +{ + glm_vec3_rotate(camera_forward, angle, camera_up); + glm_vec3_rotate(camera_right, angle, camera_up); +} +void rotate_camera_z(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right) +{ + glm_vec3_rotate(camera_up, angle, camera_forward); + glm_vec3_rotate(camera_right, angle, camera_forward); +} static int prog_create_shader(GLuint *shader, const char *spath, GLenum stype) { int fd = -1; ## -80,7 +95,7 ## glUseProgram(*prog); return 0; } -static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_front, vec3 camera_up) +static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_forward, vec3 camera_up, vec3 camera_right) { float xoffset = xrel; float yoffset = yrel; ## -92,22 +107,14 ## *yaw += xoffset; *pitch += -yoffset; *roll += zoffset; - vec3 front = - { - cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)), - sin(glm_rad(*pitch)), - sin(glm_rad(*yaw)) * cos(glm_rad(*pitch)) - }; - glm_vec3_normalize_to(front, camera_front); - vec3 up = - { - cos(glm_rad(*roll)), - sin(glm_rad(*roll)), - 0 - }; - glm_vec3_normalize_to(up, camera_up); + if(xrel != 0) + rotate_camera_y(*yaw, camera_forward, camera_up, camera_right); + if(yrel != 0) + rotate_camera_x(*pitch, camera_forward, camera_up, camera_right); + if(zrel != 0) + rotate_camera_z(*roll, camera_forward, camera_up, camera_right); } -static void input(int *loop, vec3 camera_position, vec3 camera_front, vec3 camera_up, float *yaw, float *pitch, float *roll) +static void input(int *loop, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 camera_right, float *yaw, float *pitch, float *roll) { SDL_Event event = {0}; float cameraSpeed = 0.01; ## -118,7 +125,7 ## switch(event.type) { case SDL_MOUSEMOTION: - input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_front, camera_up); + input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_forward, camera_up, camera_right); break; case SDL_KEYDOWN: switch(event.key.keysym.sym) ## -144,38 +151,38 ## keystates = SDL_GetKeyboardState(NULL); if(keystates[SDL_SCANCODE_Q]) { - input_mouse(0, 0, -1, yaw, pitch, roll, camera_front, camera_up); + input_mouse(0, 0, -1, yaw, pitch, roll, camera_forward, camera_up, camera_right); } if(keystates[SDL_SCANCODE_E]) { - input_mouse(0, 0, 1, yaw, pitch, roll, camera_front, camera_up); + input_mouse(0, 0, 1, yaw, pitch, roll, camera_forward, camera_up, camera_right); } if(keystates[SDL_SCANCODE_W]) { - glm_vec3_scale(camera_front, cameraSpeed, tmp); + glm_vec3_scale(camera_forward, cameraSpeed, tmp); glm_vec3_add(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_S]) { - glm_vec3_scale(camera_front, cameraSpeed, tmp); + glm_vec3_scale(camera_forward, cameraSpeed, tmp); glm_vec3_sub(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_A]) { - glm_vec3_cross(camera_front, camera_up, tmp); + glm_vec3_cross(camera_forward, camera_up, tmp); glm_vec3_normalize(tmp); glm_vec3_scale(tmp, cameraSpeed, tmp); glm_vec3_sub(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_D]) { - glm_vec3_cross(camera_front, camera_up, tmp); + glm_vec3_cross(camera_forward, camera_up, tmp); glm_vec3_normalize(tmp); glm_vec3_scale(tmp, cameraSpeed, tmp); glm_vec3_add(camera_position, tmp, camera_position); } } -static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_front, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao) +static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao) { /* declare variables */ mat4 view = GLM_MAT4_IDENTITY_INIT; ## -189,7 +196,7 ## glClearColor(1, 1, 1, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* update the view matrix */ - glm_vec3_add(camera_position, camera_front, tmp); + glm_vec3_add(camera_position, camera_forward, tmp); glm_lookat(camera_position, tmp, camera_up, view); uniform = glGetUniformLocation(prog, "view"); glUniformMatrix4fv(uniform, 1, GL_FALSE, view[0]); ## -218,8 +225,9 ## GLuint triangle_vao = 0; GLuint triangle_vbo = 0; vec3 camera_position = {0, 0, 3}; - vec3 camera_front = {0, 0, -1}; + vec3 camera_forward = {0, 0, -1}; vec3 camera_up = {0, 1, 0}; + vec3 camera_right = {1, 0, 0}; float yaw = -90; float pitch = 0; float roll = 90; ## -285,8 +293,8 ## /* loop */ while(loop) { - input(&loop, camera_position, camera_front, camera_up, &yaw, &pitch, &roll); - display(window, prog, camera_position, camera_front, camera_up, triangle_position, triangle_vao); + input(&loop, camera_position, camera_forward, camera_up, camera_right, &yaw, &pitch, &roll); + display(window, prog, camera_position, camera_forward, camera_up, triangle_position, triangle_vao); } /* free */ glDeleteBuffers(1, &triangle_vbo); $ patch -p1 < main.c.patch $ gcc -std=c99 -pedantic -Wall -Werror -g `pkg-config sdl2 --cflags` `pkg-config glew --cflags` -o main.out main.c `pkg-config sdl2 --libs-only-L` `pkg-config glew --libs-only-L` `pkg-config sdl2 --libs-only-l` `pkg-config glew --libs-only-l` -lm $ ./main.out screen width: 1920 screen height: 1080 GL_VENDOR: AMD GL_RENDERER: AMD Radeon RX 580 Series (polaris10, LLVM 11.0.0, DRM 3.46, 5.18.10-desktop) GL_VERSION: 4.6 (Core Profile) Mesa 22.1.3 GL_SHADING_LANGUAGE_VERSION: 4.60 But the rotation is now doing weird things when I move the mouse or when I press the Q or E key. PATCH #2 I tried to apply the new suggestions of RedRuin. Here's the patch: $ cat main.c.patch --- 20220725/main.c 2022-07-25 06:36:48.165479193 +0200 +++ 20220727/main.c 2022-07-27 22:55:56.805646313 +0200 ## -4,6 +4,21 ## #include <fcntl.h> /* for open */ #include <unistd.h> /* for read */ #define BUF_SIZE 512 +void rotate_camera_y(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right) +{ + printf("rotate_camera_y: [1] angle(%+.3f deg, %+.3f rad), camera_forward(%+.3f, %+.3f, %+.3f), camera_right(%+.3f, %+.3f, %+.3f)\n", + angle, + glm_rad(angle), + camera_forward[0], camera_forward[1], camera_forward[2], + camera_up[0], camera_up[1], camera_up[2]); + glm_vec3_rotate(camera_forward, glm_rad(angle), camera_up); + glm_vec3_rotate(camera_right, glm_rad(angle), camera_up); + printf("rotate_camera_y: [2] angle(%+.3f deg, %+.3f rad), camera_forward(%+.3f, %+.3f, %+.3f), camera_right(%+.3f, %+.3f, %+.3f)\n", + angle, + glm_rad(angle), + camera_forward[0], camera_forward[1], camera_forward[2], + camera_up[0], camera_up[1], camera_up[2]); +} static int prog_create_shader(GLuint *shader, const char *spath, GLenum stype) { int fd = -1; ## -80,7 +95,7 ## glUseProgram(*prog); return 0; } -static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_front, vec3 camera_up) +static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_forward, vec3 camera_up, vec3 camera_right) { float xoffset = xrel; float yoffset = yrel; ## -92,22 +107,10 ## *yaw += xoffset; *pitch += -yoffset; *roll += zoffset; - vec3 front = - { - cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)), - sin(glm_rad(*pitch)), - sin(glm_rad(*yaw)) * cos(glm_rad(*pitch)) - }; - glm_vec3_normalize_to(front, camera_front); - vec3 up = - { - cos(glm_rad(*roll)), - sin(glm_rad(*roll)), - 0 - }; - glm_vec3_normalize_to(up, camera_up); + if(xrel != 0) + rotate_camera_y(45, camera_forward, camera_up, camera_right); } -static void input(int *loop, vec3 camera_position, vec3 camera_front, vec3 camera_up, float *yaw, float *pitch, float *roll) +static void input(int *loop, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 camera_right, float *yaw, float *pitch, float *roll) { SDL_Event event = {0}; float cameraSpeed = 0.01; ## -118,7 +121,7 ## switch(event.type) { case SDL_MOUSEMOTION: - input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_front, camera_up); + input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_forward, camera_up, camera_right); break; case SDL_KEYDOWN: switch(event.key.keysym.sym) ## -144,38 +147,38 ## keystates = SDL_GetKeyboardState(NULL); if(keystates[SDL_SCANCODE_Q]) { - input_mouse(0, 0, -1, yaw, pitch, roll, camera_front, camera_up); + input_mouse(0, 0, -1, yaw, pitch, roll, camera_forward, camera_up, camera_right); } if(keystates[SDL_SCANCODE_E]) { - input_mouse(0, 0, 1, yaw, pitch, roll, camera_front, camera_up); + input_mouse(0, 0, 1, yaw, pitch, roll, camera_forward, camera_up, camera_right); } if(keystates[SDL_SCANCODE_W]) { - glm_vec3_scale(camera_front, cameraSpeed, tmp); + glm_vec3_scale(camera_forward, cameraSpeed, tmp); glm_vec3_add(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_S]) { - glm_vec3_scale(camera_front, cameraSpeed, tmp); + glm_vec3_scale(camera_forward, cameraSpeed, tmp); glm_vec3_sub(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_A]) { - glm_vec3_cross(camera_front, camera_up, tmp); + glm_vec3_cross(camera_forward, camera_up, tmp); glm_vec3_normalize(tmp); glm_vec3_scale(tmp, cameraSpeed, tmp); glm_vec3_sub(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_D]) { - glm_vec3_cross(camera_front, camera_up, tmp); + glm_vec3_cross(camera_forward, camera_up, tmp); glm_vec3_normalize(tmp); glm_vec3_scale(tmp, cameraSpeed, tmp); glm_vec3_add(camera_position, tmp, camera_position); } } -static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_front, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao) +static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao) { /* declare variables */ mat4 view = GLM_MAT4_IDENTITY_INIT; ## -189,7 +192,7 ## glClearColor(1, 1, 1, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* update the view matrix */ - glm_vec3_add(camera_position, camera_front, tmp); + glm_vec3_add(camera_position, camera_forward, tmp); glm_lookat(camera_position, tmp, camera_up, view); uniform = glGetUniformLocation(prog, "view"); glUniformMatrix4fv(uniform, 1, GL_FALSE, view[0]); ## -218,8 +221,9 ## GLuint triangle_vao = 0; GLuint triangle_vbo = 0; vec3 camera_position = {0, 0, 3}; - vec3 camera_front = {0, 0, -1}; + vec3 camera_forward = {0, 0, -1}; vec3 camera_up = {0, 1, 0}; + vec3 camera_right = {1, 0, 0}; float yaw = -90; float pitch = 0; float roll = 90; ## -285,8 +289,8 ## /* loop */ while(loop) { - input(&loop, camera_position, camera_front, camera_up, &yaw, &pitch, &roll); - display(window, prog, camera_position, camera_front, camera_up, triangle_position, triangle_vao); + input(&loop, camera_position, camera_forward, camera_up, camera_right, &yaw, &pitch, &roll); + display(window, prog, camera_position, camera_forward, camera_up, triangle_position, triangle_vao); } /* free */ glDeleteBuffers(1, &triangle_vbo); So it seems that the printed values are the good ones. In the patch #1, when I said that "the rotation is now doing weird things", I mean, it's hard to describe but it's like it's rotating too fast but on the good axis. PATCH #3 I finally understood what was going wrong with the rotation that was too fast. In the original code (not patched) I was using absolute values for the rotation angle, it was a big value stored into the variables pitch, yaw and roll. Now, in the patched code, I have to use relative values for the rotation angle, it is a small value and I don't need anymore to store it into the variables pitch, yaw and roll. I tried to apply this. Here's the patch: $ cat main.c.patch --- 20220725/main.c 2022-07-25 06:36:48.165479193 +0200 +++ 20220728/main.c 2022-07-28 04:05:37.677937509 +0200 ## -4,6 +4,21 ## #include <fcntl.h> /* for open */ #include <unistd.h> /* for read */ #define BUF_SIZE 512 +void rotate_camera_x(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right) +{ + glm_vec3_rotate(camera_forward, glm_rad(angle), camera_right); + glm_vec3_rotate(camera_up, glm_rad(angle), camera_right); +} +void rotate_camera_y(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right) +{ + glm_vec3_rotate(camera_forward, glm_rad(angle), camera_up); + glm_vec3_rotate(camera_right, glm_rad(angle), camera_up); +} +void rotate_camera_z(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right) +{ + glm_vec3_rotate(camera_up, glm_rad(angle), camera_forward); + glm_vec3_rotate(camera_right, glm_rad(angle), camera_forward); +} static int prog_create_shader(GLuint *shader, const char *spath, GLenum stype) { int fd = -1; ## -80,34 +95,23 ## glUseProgram(*prog); return 0; } -static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_front, vec3 camera_up) +static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, vec3 camera_forward, vec3 camera_up, vec3 camera_right) { - float xoffset = xrel; - float yoffset = yrel; + float xoffset = -xrel; + float yoffset = -yrel; float zoffset = zrel; float sensitivity = 0.1; xoffset *= sensitivity; yoffset *= sensitivity; zoffset *= sensitivity; - *yaw += xoffset; - *pitch += -yoffset; - *roll += zoffset; - vec3 front = - { - cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)), - sin(glm_rad(*pitch)), - sin(glm_rad(*yaw)) * cos(glm_rad(*pitch)) - }; - glm_vec3_normalize_to(front, camera_front); - vec3 up = - { - cos(glm_rad(*roll)), - sin(glm_rad(*roll)), - 0 - }; - glm_vec3_normalize_to(up, camera_up); + if(xrel != 0) + rotate_camera_y(xoffset, camera_forward, camera_up, camera_right); + if(yrel != 0) + rotate_camera_x(yoffset, camera_forward, camera_up, camera_right); + if(zrel != 0) + rotate_camera_z(zoffset, camera_forward, camera_up, camera_right); } -static void input(int *loop, vec3 camera_position, vec3 camera_front, vec3 camera_up, float *yaw, float *pitch, float *roll) +static void input(int *loop, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 camera_right) { SDL_Event event = {0}; float cameraSpeed = 0.01; ## -118,7 +122,7 ## switch(event.type) { case SDL_MOUSEMOTION: - input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_front, camera_up); + input_mouse(event.motion.xrel, event.motion.yrel, 0, camera_forward, camera_up, camera_right); break; case SDL_KEYDOWN: switch(event.key.keysym.sym) ## -144,38 +148,38 ## keystates = SDL_GetKeyboardState(NULL); if(keystates[SDL_SCANCODE_Q]) { - input_mouse(0, 0, -1, yaw, pitch, roll, camera_front, camera_up); + input_mouse(0, 0, -1, camera_forward, camera_up, camera_right); } if(keystates[SDL_SCANCODE_E]) { - input_mouse(0, 0, 1, yaw, pitch, roll, camera_front, camera_up); + input_mouse(0, 0, 1, camera_forward, camera_up, camera_right); } if(keystates[SDL_SCANCODE_W]) { - glm_vec3_scale(camera_front, cameraSpeed, tmp); + glm_vec3_scale(camera_forward, cameraSpeed, tmp); glm_vec3_add(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_S]) { - glm_vec3_scale(camera_front, cameraSpeed, tmp); + glm_vec3_scale(camera_forward, cameraSpeed, tmp); glm_vec3_sub(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_A]) { - glm_vec3_cross(camera_front, camera_up, tmp); + glm_vec3_cross(camera_forward, camera_up, tmp); glm_vec3_normalize(tmp); glm_vec3_scale(tmp, cameraSpeed, tmp); glm_vec3_sub(camera_position, tmp, camera_position); } if(keystates[SDL_SCANCODE_D]) { - glm_vec3_cross(camera_front, camera_up, tmp); + glm_vec3_cross(camera_forward, camera_up, tmp); glm_vec3_normalize(tmp); glm_vec3_scale(tmp, cameraSpeed, tmp); glm_vec3_add(camera_position, tmp, camera_position); } } -static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_front, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao) +static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao) { /* declare variables */ mat4 view = GLM_MAT4_IDENTITY_INIT; ## -189,7 +193,7 ## glClearColor(1, 1, 1, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* update the view matrix */ - glm_vec3_add(camera_position, camera_front, tmp); + glm_vec3_add(camera_position, camera_forward, tmp); glm_lookat(camera_position, tmp, camera_up, view); uniform = glGetUniformLocation(prog, "view"); glUniformMatrix4fv(uniform, 1, GL_FALSE, view[0]); ## -218,11 +222,9 ## GLuint triangle_vao = 0; GLuint triangle_vbo = 0; vec3 camera_position = {0, 0, 3}; - vec3 camera_front = {0, 0, -1}; + vec3 camera_forward = {0, 0, -1}; vec3 camera_up = {0, 1, 0}; - float yaw = -90; - float pitch = 0; - float roll = 90; + vec3 camera_right = {1, 0, 0}; float triangle_vertices[] = { -1, -1, +0, ## -285,8 +287,8 ## /* loop */ while(loop) { - input(&loop, camera_position, camera_front, camera_up, &yaw, &pitch, &roll); - display(window, prog, camera_position, camera_front, camera_up, triangle_position, triangle_vao); + input(&loop, camera_position, camera_forward, camera_up, camera_right); + display(window, prog, camera_position, camera_forward, camera_up, triangle_position, triangle_vao); } /* free */ glDeleteBuffers(1, &triangle_vbo); So it just works perfectly. Problem fixed. The result (video of the SDL window): https://youtu.be/w_4J_XwmnSg
Consider what this does: vec3 front = { cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)), sin(glm_rad(*pitch)), sin(glm_rad(*yaw)) * cos(glm_rad(*pitch)) }; This calculation takes into account the yaw and pitch, but not the roll. Thus, while your up vector is being rolled, your front vector has no knowledge of this, and (based on OpenGL's coordinate system) assumes that "up" is always positive Y. To this bit of code, a rotation to the "right" is always the same direction, regardless of whatever way the camera is oriented. This is why moving the mouse to the right moves the triangle up; the orientation of the camera changed but the axis the rotation is around hasn't. You could try updating your front vector to include the roll parameters (pulled from this answer): vec3 front = { -cos(glm_rad(*yaw)) * sin(glm_rad(*pitch)) * sin(glm_rad(*roll)) - sin(glm_rad(*yaw)) * cos(glm_rad(*roll)), -sin(glm_rad(*yaw)) * sin(glm_rad(*pitch)) * sin(glm_rad(*roll)) + cos(glm_rad(*yaw)) * cos(glm_rad(*roll)), cos(glm_rad(*pitch)) * sin(glm_rad(*roll)) } Unfortunately this is pretty unweildly and chances are sooner or later you'll run into Gimbal Lock. You can use a matrix or a special 4-dimensional vector called a Quaternion to completely circumvent gimbal lock, but they are hard to intuit (compared to roll-pitch-yaw) and might be overkill for a simple application such as this. I'll propose a middle ground, one I've had success with for simple cameras such as this. Keep track of your camera's 3 basis vectors that define it's reference orientation: // For example, we might start with basis vectors like this vec3 camera_forward = {0, 0, -1}; vec3 camera_up = {0, 1, 0}; vec3 camera_right = {1, 0, 0}; Then we can use the glm_vec3_rotate() function to rotate these basis vectors around each other instead of the cardinal XYZ axes: void rotate_camera_x(float angle) { // rotate around the camera's x axis, or camera_right (pitch) glm_vec3_rotate(&camera_forward, angle, camera_right) glm_vec3_rotate(&camera_up, angle, camera_right) // technically we could also rotate camera_right around camera_right, but that's redundant } void rotate_camera_y(float angle) { // rotate around the camera's y axis, or camera_up (yaw) glm_vec3_rotate(&camera_forward, angle, camera_up) glm_vec3_rotate(&camera_right, angle, camera_up) } void rotate_camera_z(float angle) { // rotate around the camera's z axis, or camera_forward (roll) glm_vec3_rotate(&camera_up, angle, camera_forward) glm_vec3_rotate(&camera_right, angle, camera_forward) } The trick with this is that the axes we rotate change along with the rotation of the camera; now when you roll, you're changing the direction of camera_right and camera_up, changing the axes the next rotations will be around. This ensures that the camera's movement will always be tied to it's orientation.
Mandelbrot set in OpenGL - C
I'm new to OpenGL and I am trying to get a mandelbrot set computed with OpenGL and GLFW. I found the code here but freeglut is broken on my system and for some reason complains about no callback being set even though it clearly is being set. It does however flash one frame and then crash, in that frame I can see the mandelbrot set so I know the math is correct. I figured this would be a good opportunity to learn more about OpenGL and GLFW, so I set to work making this happen. After double checking everything, I can see that it definitely calculates the values then switches the buffers properly. However, I think I'm missing two things: A vertex which the texture can actually be applied to EDIT: (from learnopengl.com) "Once glTexImage2D is called, the currently bound texture object now has the texture image attached to it.", so it can't be #2 not sure what's happening with the calculation but it looks like it's binding a texture named 'texture' but then calculating the values in a struct array which don't seem to be associated in any way. I bind the texture with tex (texture) and then send the struct array to glTexImage2D If someone could just point me in the right direction or confirm my suspicions that would be awesome. My code is here: #include <stdio.h> #include <stdlib.h> #include <math.h> #define GLEW_STATIC #include <GL/glew.h> #include <pthread.h> #include <GLFW/glfw3.h> #include <GL/gl.h> #define VAL 255 typedef struct { uint8_t r; uint8_t g; uint8_t b; }rgb_t; rgb_t **tex_array = 0; rgb_t *image; int gwin; int width = 640; int height = 480; int tex_w, tex_h; double scale = 1./256; double cx = -.6, cy = 0; int color_rotate = 0; int saturation = 1; int invert = 0; int max_iter = 256; int dump = 1; GLFWwindow* window; int global_iterator = 0; int conversion_iterator_x = 0; int conversion_iterator_y = 0; GLFWwindow* init_glfw(); void set_texture(GLuint tex); void framebuffer_size_callback(GLFWwindow* window, int width, int height); void render(GLuint tex); void screen_dump(); void keypress(unsigned char key, int x, int y); void hsv_to_rgb(int hue, int min, int max, rgb_t *p); void calc_mandel(rgb_t* px); void alloc_texture(); void set_texture(); void mouseclick(int button, int state, int x, int y); void resize(int w, int h); void framebuffer_size_callback(GLFWwindow* window, int width, int height); int main(int c, char **v) { GLFWwindow* win = init_glfw(); glfwSetWindowPos(win, 1000, 500); GLuint texture; glGenTextures(1, &texture); set_texture(texture); /* Loop until the user closes the window */ while (!glfwWindowShouldClose(win)) { render(texture); /* Swap front and back buffers */ glfwSwapBuffers(win); /* Poll for and process events */ glfwPollEvents(); if(glfwGetKey(win, GLFW_KEY_ESCAPE) == GLFW_PRESS){ glfwSetWindowShouldClose(win, GL_TRUE); } } return 0; } void set_texture(GLuint tex) { printf("Allocating space\n"); alloc_texture(); printf("Calculating mandel... %d\n", global_iterator); ++global_iterator; calc_mandel(image); printf("mandel calculation complete\n"); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_w, tex_h, 0, GL_RGB, GL_UNSIGNED_BYTE, tex_array[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); printf("Rendering to screen...\n"); render(tex); } void alloc_texture() { int i; int ow = tex_w; int oh = tex_h; for (tex_w = 1; tex_w < width; tex_w <<= 1); for (tex_h = 1; tex_h < height; tex_h <<= 1); if (tex_h != oh || tex_w != ow){ tex_array = realloc(tex_array, tex_h * tex_w * 3 + tex_h * sizeof(rgb_t*)); } for (tex_array[0] = (rgb_t *)(tex_array + tex_h), i = 1; i < tex_h; i++){ tex_array[i] = tex_array[i - 1] + tex_w; } } void render(GLuint tex) { double x = (double)width /tex_w, y = (double)height/tex_h; glClear(GL_COLOR_BUFFER_BIT); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBindTexture(GL_TEXTURE_2D, tex); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(0, 0); glTexCoord2f(x, 0); glVertex2i(width, 0); glTexCoord2f(x, y); glVertex2i(width, height); glTexCoord2f(0, y); glVertex2i(0, height); glEnd(); glFlush(); glFinish(); } GLFWwindow* init_glfw() { /* Initialize the library */ if (!glfwInit()){ return NULL; } /* * Configure window options here if you so desire * * i.e. */ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); //the fourth parameter of glfwCreateWindow should be NULL for windowed mode and //glfGetPrimaryMonitor() for full screen mode /* Create a windowed mode window and its OpenGL context */ window = glfwCreateWindow(width, height, "Mandelbrot", NULL, NULL); if (!window) { glfwTerminate(); return NULL; } /* Make the window's context current */ glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); /* * Initialize glew here */ glewExperimental = GL_TRUE; glewInit(); return window; } void calc_mandel(rgb_t* px) { int i, j, iter, min, max; double x, y, zx, zy, zx2, zy2; min = max_iter; max = 0; for (i = 0; i < height; i++) { px = tex_array[i]; y = (i - height/2) * scale + cy; for (j = 0; j < width; j++, px++) { x = (j - width/2) * scale + cx; iter = 0; zx = hypot(x - .25, y); if (x < zx - 2 * zx * zx + .25){ iter = max_iter; } if ((x + 1)*(x + 1) + y * y < 1/16){ iter = max_iter; } zx = zy = zx2 = zy2 = 0; for (; iter < max_iter && zx2 + zy2 < 4; iter++) { zy = 2 * zx * zy + y; zx = zx2 - zy2 + x; zx2 = zx * zx; zy2 = zy * zy; } if (iter < min){ min = iter; } if (iter > max){ max = iter; } *(unsigned short *)px = iter; } } for (i = 0; i < height; i++){ for (j = 0, px = tex_array[i]; j < width; j++, px++){ hsv_to_rgb(*(unsigned short*)px, min, max, px); } } } void hsv_to_rgb(int hue, int min, int max, rgb_t *p) { printf("Converting hsv to rbg... \n"); if (min == max){ max = min + 1; } if (invert){ hue = max - (hue - min); } if (!saturation) { p->r = p->g = p->b = 255 * (max - hue) / (max - min); printf("done! (!saturation)\n"); return; } double h = fmod(color_rotate + 1e-4 + 4.0 * (hue - min) / (max - min), 6); double c = VAL * saturation; double X = c * (1 - fabs(fmod(h, 2) - 1)); p->r = p->g = p->b = 0; switch((int)h) { case 0: p->r = c; p->g = X; break; case 1: p->r = X; p->g = c; break; case 2: p->g = c; p->b = X; break; case 3: p->g = X; p->b = c; break; case 4: p->r = X; p->b = c; break; default:p->r = c; p->b = X; break; } printf("done! (sauration)\n"); } void framebuffer_size_callback(GLFWwindow* window, int width, int height) { // make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays. glViewport(0, 0, width, height); glOrtho(0, width, 0, height, -1, 1); //set_texture(); } [1]: https://rosettacode.org/wiki/Mandelbrot_set#PPM_non_interactive
Mandelbrot Set not displayed
a Mandelbrot set fractal using C programming and OpenGL. Here is my code. It is only displaying a dot in the center right now. I cannot figure out where I am going wrong. I'm pretty sure my math is correct. Maybe I have something in the wrong loop? This picture is what Im trying to get Here is my code so far: #include <GLUT/glut.h> #include <math.h> void init(void); void display(void); const int screenWidth = 640; const int screenHeight = 480; int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(screenWidth, screenHeight); glutInitWindowPosition(0, 0); glutCreateWindow("Mandelbrot"); // glViewport(-320, -320, 320, 320); init(); glutDisplayFunc(display); glutMainLoop(); return 0; } void init(void) { glMatrixMode(GL_PROJECTION); gluOrtho2D(-500.0, screenWidth, -500.0, screenHeight); // A = screenWidth / 4.0; // B = 0.0; // C = D = screenHeight / 2.0; } void display(void) { GLdouble x, f, y, xtemp, y0, x0, iteration, maxInteration; glClearColor(1.0, 1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glPointSize(1); glColor3f(0.0, 0.0, 0.0); glEnable(GL_POINT_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); for (y0 = - 1; y0 < 1.1; y0 = y0 + 0.0025) { for (x0 = -2.5; x0 < 1.1; x0 = x0 + 0.0025) { x = 0; y = 0; iteration = 0; maxInteration = 1000; while (((x * x) + (y * y) < (2 * 2)) && iteration < maxInteration) { xtemp = (x * x) - (y * y) + x0; y = (2 * x * y) + y0; x = xtemp; iteration = iteration + 1; if (y <= 2) { glBegin(GL_POINTS); glVertex2d(x / 750, y / 750); glEnd(); } } } } glFlush(); } Here is my updated code after fixing suggestions in comments.. It results in the above image.. However, now I am trying to create the grey circles around the object??? Im attempting to do this through the else at the end... any thoughts? #include <GLUT/glut.h> #include <math.h> void init(void); void display(void); const int screenWidth = 640; const int screenHeight = 640; GLdouble A, B, C, D; int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(screenWidth, screenHeight); glutInitWindowPosition(0, 0); glutCreateWindow("Mandelbrot"); glViewport(-1, 1, -1, 1); init(); glutDisplayFunc(display); glutMainLoop(); return 0; } void init(void) { //glMatrixMode(GL_PROJECTION); gluOrtho2D(-3.0, 3.0, -3.0, 3.0); A = screenWidth / 4.0; B = 0.0; C = D = screenHeight / 2.0; } void display(void) { GLdouble x, f, y, xtemp, y0, x0, iteration, maxInteration; glClearColor(1.0, 1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glPointSize(1); glEnable(GL_POINT_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); for(y0 = -1; y0< 1.1; y0 = y0 + 0.0025){ for (x0 = -2.5; x0 < 1.1; x0 = x0 + 0.0025) { x = 0; y = 0; iteration = 0; maxInteration = 200; while(((x*x) + (y*y) <(2*2)) && iteration <maxInteration){ xtemp = (x*x) - (y*y) + x0; y = (2*x*y) +y0; x = xtemp; iteration = iteration + 1; } if(iteration >= maxInteration){ glBegin(GL_POINTS); glVertex2d(x0 , y0); glColor3f(0.0, 0.0, 0.0); glEnd(); } else{ ???? } } } glFlush(); }
First of all, here's some advices regarding to your code: When working with complex numbers or vectors i'd recommend you to use a proper fast math library so you can avoid operating with individual components, there are very fast cpu math libraries out there which can use SIMD instructions and your code will become more readable The way your drawing the mandelbrot is really a bad idea. I mean, yeah, it's alright if you just want to dump simple images and learning the basics but that's pretty much. Don't use GL_POINTS and try to render/update textures directly, or even better, use fragment shaders + glsl (recommended way) so your mandelbrot will be rendered very fast even if you're using non-optimized maths. Coordinate systems, if you still insist on using GL_POINTS the way you're doing, i'd just use directly the window coordinates and going from that space to the mandelbrot math domain ie: [0,0,w,h]<->[-1,-1,1,1] Here's a little example of what i mean: #include <GL/glut.h> #include <math.h> #include <stdio.h> const int screen_width = 640; const int screen_height = 480; float c[4]; float z[4]; float clamp(float x, float vmin, float vmax) { if (x < vmin) { return vmin; } else if (x > vmax) { return vmax; } return x; } void dc_add(float *a, float *b, float *res) { res[0] = a[0] + b[0]; res[1] = a[1] + b[1]; res[2] = a[2] + b[2]; res[3] = a[3] + b[3]; } void dc_mul(float *a, float *b, float *res) { res[0] = a[0] * b[0] - a[1] * b[1]; res[1] = a[0] * b[1] + a[1] * b[0]; res[2] = a[0] * b[2] + a[2] * b[0] - a[1] * b[3] - a[3] * b[1]; res[3] = a[0] * b[3] + a[3] * b[0] + a[2] * b[1] + a[1] * b[2]; } void dc_sqr(float *a, float *res) { res[0] = a[0] * a[0] - a[1] * a[1]; res[1] = 2.0f * a[0] * a[1]; res[2] = 2.0f * (a[0] * a[2] - a[1] * a[3]); res[3] = 2.0f * (a[0] * a[3] + a[1] * a[2]); } float dot(float x, float y) { return x * x + y * y; } void init(void) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, screen_width, 0, screen_height); } void display(void) { glClear(GL_COLOR_BUFFER_BIT); glPointSize(1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); for (int y = 0; y < screen_height; y++) { for (int x = 0; x < screen_width; x++) { float px = -1.0f + 2.0f * (float)x / (float)screen_width; float py = -1.0f + 2.0f * (float)y / (float)screen_height; px *= (float)screen_width / (float)screen_height; float tz = 0.5f; float zo = powf(1.2f, 1.2f); float m2 = 0.0f; float co = 0.0f; float temp[4]; c[0] = px * zo; c[1] = py * zo; c[2] = 1.0; c[3] = 0.0; z[0] = 0.0f; z[1] = 0.0f; z[2] = 0.0f; z[3] = 0.0f; for (int i = 0; i < 256; i++) { if (m2 > 1024.0f) continue; dc_sqr(z, temp); dc_add(temp, c, z); m2 = dot(z[0], z[1]); co += 1.0f; } float d = 0.0f; if (co < 256.0f) { d = sqrtf((dot(z[0], z[1]) / dot(z[2], z[3]))) * logf(dot(z[0], z[1])); } d = clamp(4.0f * d / zo, 0.0f, 1.0f); d = powf(d, 0.25f); glColor3f(d, d, d); glBegin(GL_POINTS); glVertex2d(x, y); glEnd(); } } glFlush(); glutSwapBuffers(); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(screen_width, screen_height); glutInitWindowPosition(0, 0); glutCreateWindow("Mandelbrot"); init(); glutDisplayFunc(display); glutIdleFunc(display); glutMainLoop(); return 0; } And here's the output: Maths of the above example are based on this shadertoy. The above code is terrible unefficient and slow but it serves the main purpose to prove you the way you shouldn't ever code a proper mandelbrot. Happy coding.
There is a simple problem in you code: you use a division instead of a multiplication to compute the pixel coordinates: change glVertex2d(x / 750, y / 750); to glVertex2d(x * 750, y * 750); However, this is not the correct method to compute the Mandelbrot set. You should instead compute the number of iterations for the squared module to exceed 4.0, and then set the color of the pixel at (x0 * 300, y0 * 300) to that number as a palette entry or a gray level.
Draw doesn't works with OpenGL while drawing sphere
I'm first in OpenGL, and i want to draw a sphere with 3D graphical view. For first step, i just want to draw a belt that contained to result sphere. But there's no result, all of my result window is cleared with white. Is there any problem in my code? Here is my Code : void init(); void display(); void drawPath(int pi, int theta); void drawQuad(int pi, int theta); int we = -80; // - νμ΄ int kyong = -180; // - μΈν int main(int argc, const char * argv[]) { glutInit(&argc, (char**)argv); glutInitWindowSize(500, 500); glutCreateWindow("Prog09 - Goo"); glutDisplayFunc(display); init(); glutMainLoop(); return 0; } void init(){ glClearColor(1, 1, 1, 1); glOrtho(0, 50, 0, 50, -50, 50); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); } void display(){ glColor3f(1.0, 1.0, 1.0); drawPath(0, 0); } void drawPath(int pi, int theta){ drawQuad(pi, theta); } void drawQuad(pi, theta){ int i; GLfloat x[4],y[4],z[4]; // theta = (theta * 3.14)/180; // pi = (pi * 3.14)/180; x[0] = sin(theta)*cos(pi); y[0] = cos(theta)*sin(pi); z[0] = sin(pi); x[1] = sin(theta)*cos(pi+20); y[1] = cos(theta)*sin(pi+20); z[1] = sin(pi+20); x[2] = sin(theta+20)*cos(pi+20); y[2] = cos(theta+20)*sin(pi+20); z[2] = sin(pi+20); x[3] = sin(theta+20)*cos(pi); y[3] = cos(theta+20)*sin(pi); z[3] = sin(pi); for (i = 0; i < 4; i++) { glBegin(GL_POLYGON); glVertex3f(x[i]*10, y[i]*10, z[i]*10); glEnd(); } glFlush(); for (i = 0; i < 4; i++) { printf("%d. %f %f %f\n",i+1, x[i], y[i], z[i]); } printf("WHY?\n"); } I know it's a basic question, but i have know idea why my codes doesn't work. Thanks for your helps.
You are drawing a number of polygons, were each polygon contains exactly one point: for (i = 0; i < 4; i++) { glBegin(GL_POLYGON); glVertex3f(x[i]*10, y[i]*10, z[i]*10); glEnd(); } If you want to draw one polygon with all the points, then you'll have to do something like: glBegin(GL_POLYGON); for (i = 0; i < 4; i++) { glVertex3f(x[i]*10, y[i]*10, z[i]*10); } glEnd();
Drawing multiple objects in OpenGL using C
I have an OpenGL program to draw a circle by mouse click. The program works fine except when I try to draw multiple circles the previous circle disappears. Here is the code: #include <GL/glut.h> #include<math.h> #include<stdio.h> struct position { float x; float y; }; typedef struct position Position; Position start; Position finish; void setPixel(int x, int y) { glBegin(GL_POINTS); glVertex2f(x, y); glEnd(); } void circle(int a0, int b0, int a1, int b1) { int i, x, y, x1, y1, r, p; x1 = (a0+a1)/2; y1 = (b0+b1)/2; r = sqrt((((a1-x1)*(a1-x1))+((b1-y1)*(b1-y1)))); p = (5/4-r); x = 0; y = r; while(x <= y) { setPixel( x+x1, y+y1); setPixel( x+x1, -y+y1); setPixel(-x+x1, -y+y1); setPixel(-x+x1, y+y1); setPixel( y+x1, x+y1); setPixel( y+x1, -x+y1); setPixel(-y+x1, -x+y1); setPixel(-y+x1, x+y1); x = x+1; if (p<0) p = p+2*x+1; else { y = y-1; p = p+2*x-2*y+1; } } } void display() { glClear(GL_COLOR_BUFFER_BIT); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glPushMatrix(); circle(start.x, start.y, finish.x, finish.y); glPopMatrix(); glutSwapBuffers(); } void reshape( int w, int h) { glViewport( 0, 0, w, h); glMatrixMode( GL_PROJECTION); glLoadIdentity(); glOrtho( 0, w, h, 0, -1, 1); } void mouse(int button, int state, int x, int y) { switch(button) { case GLUT_LEFT_BUTTON: if(state == GLUT_DOWN) { start.x = x; //x1 start.y = y; //y1 } if(state == GLUT_UP) { finish.x = x; //x2 finish.y = y; //y2 } break; glutPostRedisplay(); } } void motion( int x, int y) { finish.x = x; finish.y = y; glutPostRedisplay(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(640, 480); glutInitWindowPosition(100, 100); glutCreateWindow(""); glutMouseFunc(mouse); glutMotionFunc(motion); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; } How can I display multiple images together?
If you want to draw multiple things, then draw multiple things. You're only drawing one circle. If you change the position of that circle, it will just be drawn in a different place. However I really wouldn't recommend drawing like this in GL. Using the fixed-function pipeline to draw individual pixels as GL_POINTS is exceedingly inefficient. GL is not designed as a raster drawing API.