OpenGL Birthday Card Example Nov 5th 2020 Words: 1k

Basic structure

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <GL/glew.h>
#include <GL/glut.h>

#define APP_WIDTH 720
#define APP_HEIGHT 960

#define ORTHO_L -100.0
#define ORTHO_R 100.0
#define ORTHO_B -100.0
#define ORTHO_T 100.0

void display();

void reshape_handler(int, int);

void timer_handler(int);

int main(int argc, char *argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(APP_WIDTH, APP_HEIGHT); // set window size
glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH) - APP_WIDTH) / 2,
(glutGet(GLUT_SCREEN_HEIGHT) - APP_HEIGHT) / 2); // center the window
glutCreateWindow("Happy Birthday!"); // window title

// set callbacks
glutDisplayFunc(display);
glutTimerFunc(0, timer_handler, 0);

glutMainLoop();
return 0;
}

void display() {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();

// draw something here

glutSwapBuffers();
}


void reshape_handler(int w, int h) {
glViewport(0, 0, w, h); // set viewport
glMatrixMode(GL_PROJECTION); // change matrix mode
glLoadIdentity(); // reset projection matrix
gluOrtho2D(ORTHO_L, ORTHO_R, ORTHO_B, ORTHO_T); // use custom coordination system
glMatrixMode(GL_MODELVIEW);
}

void timer_handler(int) {
glutPostRedisplay(); // redraw content
glutTimerFunc(1000 / 60, timer_handler, 0);
}

Lock aspect ratio

1
2
3
4
5
6
7
8
9
10
void reshape_handler(int w, int h) {
int new_w = w, new_h = h;
if ((float) w / (float) h > (float) APP_WIDTH / (float) APP_HEIGHT) {
new_w = (int) ((float) h * (float) APP_WIDTH / (float) APP_HEIGHT);
} else if ((float) w / (float) h < (float) APP_WIDTH / (float) APP_HEIGHT) {
new_h = (int) ((float) w / (float) APP_WIDTH * (float) APP_HEIGHT);
}
glViewport((w - new_w) / 2, (h - new_h) / 2, new_w, new_h); // set viewport
glutReshapeWindow(new_w, new_h); // force shape window to specified ratio
}

Multiple coordination system support

1
2
3
4
5
6
7
8
9
10
11
12
13
void use_absolute_cs() {
glMatrixMode(GL_PROJECTION); // change matrix mode
glLoadIdentity(); // reset projection matrix
gluOrtho2D(0, APP_WIDTH, APP_HEIGHT, 0); // use custom coordination system
glMatrixMode(GL_MODELVIEW);
}

void use_percentage_cs() {
glMatrixMode(GL_PROJECTION); // change matrix mode
glLoadIdentity(); // reset projection matrix
gluOrtho2D(0, 1, 1, 0); // use custom coordination system
glMatrixMode(GL_MODELVIEW);
}

Draw background

Gradient background

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(int argc, char *argv[]) {
// ...

// enable blend (rgba support)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glutMainLoop();
return 0;
}

void draw_background() {
glBegin(GL_POLYGON);
glColor4f(255 / 255.0, 196 / 255.0, 216 / 255.0, 1); // pink
glVertex2f(-100, -100);
glVertex2f(100, -100);
glColor4f(255 / 255.0, 220 / 255.0, 231 / 255.0, 1); // light pink
glVertex2f(100, 100);
glVertex2f(-100, 100);
glEnd();
}

Floral decoration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
typedef struct {
GLfloat x, y;
} point;

typedef struct {
point coordinator;
float size;
float rotation;
float color[3];
float alpha;
enum shape {
TRIANGLE, CIRCLE, SQUARE
} shape;
} floral;

void draw_floral(floral *f) {
glPushMatrix(); // save previous cs
glTranslatef(f->coordinator.x, f->coordinator.y, 0); // move to new cs
glRotatef(f->rotation, 0, 0, 1); // rotate cs

glColor4f(f->color[0], f->color[1], f->color[2], f->alpha);
use_absolute_cs();
switch (f->shape) {
case floral::SQUARE:
glRectf(-f->size / 2, -f->size / 2, f->size / 2, f->size / 2);
break;
case floral::TRIANGLE:
glBegin(GL_TRIANGLES);
glVertex2f(-f->size * std::sqrt(3) / 2, -f->size / 2);
glVertex2f(f->size * std::sqrt(3) / 2, -f->size / 2);
glVertex2f(0, f->size);
break;
case floral::CIRCLE:
glBegin(GL_POLYGON);
for (float i = 0; i < 2 * PI; i += PI / 360) {
glVertex2f(f->size / 2 * cosf(i), f->size / 2 * sinf(i));
}
break;
}
glEnd();

glPopMatrix(); // restore previous cs
}

Random floral in the background

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#define FLORAL_COUNT 100
#define FLORAL_MARGIN 10
#define FLORAL_SIZE_MIN 3
#define FLORAL_SIZE_MAX 8

floral bk_decorations[FLORAL_COUNT];

void draw_background() {
// ...

floral *ptr = bk_decorations;
floral *end_ptr = bk_decorations + sizeof(bk_decorations) / sizeof(bk_decorations[0]);
while (ptr < end_ptr) {
draw_floral(ptr);
ptr++;
}

}

void init_floral() {
floral *ptr = bk_decorations;
floral *end_ptr = bk_decorations + sizeof(bk_decorations) / sizeof(bk_decorations[0]);
while (ptr < end_ptr) {
// random position
ptr->coordinator.x = random_int(FLORAL_MARGIN, APP_WIDTH);
ptr->coordinator.y = random_int(FLORAL_MARGIN, APP_HEIGHT);
// random size
ptr->size = random_float(FLORAL_SIZE_MIN, FLORAL_SIZE_MAX);
// random rotation
ptr->rotation = random_float(-180, 180);
// random color
ptr->color[0] = random_float(0, 1);
ptr->color[1] = random_float(0, 1);
ptr->color[2] = random_float(0, 1);
// random alpha
ptr->alpha = random_float(.2, 1);
// random Shape
ptr->shape = static_cast<floral::Shape>(rand() % floral::SQUARE);
ptr++;
}
}

int random_int(int min, int max) {
return rand() % (max - min + 1) + min;
}

float random_float(float min, float max) {
return min + (float) rand() / (float) (RAND_MAX) * max;
}

int main(int argc, char *argv[]) {
// ...

// init global vars
init_floral();

glutMainLoop(); // m4k3 17 r41n
return 0;
}

Floral animation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void timer_handler(int) {
floral_animate();
glutPostRedisplay(); // redraw content
glutTimerFunc(1000 / 60, timer_handler, 0);
}

bool random_bool() {
std::random_device device;
std::mt19937 generator(device());
std::uniform_int_distribution<int> distribution(0, 1);
return distribution(generator);
}

void floral_animate() {
floral *ptr = bk_decorations;
floral *end_ptr = bk_decorations + sizeof(bk_decorations) / sizeof(bk_decorations[0]);
while (ptr < end_ptr) {
if (ptr->fading) {
ptr->alpha -= FLORAL_ANIMATION_SPEED;
if (ptr->alpha < 0) {
ptr->alpha = 0;
ptr->fading = false;
// random position on faded
ptr->position.x = random_int(FLORAL_MARGIN, APP_WIDTH - FLORAL_MARGIN);
ptr->position.y = random_int(FLORAL_MARGIN, APP_HEIGHT - FLORAL_MARGIN);
}
} else {
ptr->alpha += FLORAL_ANIMATION_SPEED;
if (ptr->alpha > 1) {
ptr->alpha = 1;
ptr->fading = true;
}
}
ptr++;
}
}

Egg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void draw_egg() {
use_absolute_cs();
glPushMatrix();
glTranslatef(APP_WIDTH / 2.0, APP_HEIGHT / 2.0 + 50, 0);

// draw egg
glColor4f(237 / 255.0, 196 / 255.0, 166 / 255.0, 1);
glBegin(GL_POLYGON);
for (int y = -EGG_A; y < EGG_A + 1; y++) {
glVertex2f(sqrt((1 - y * y / EGG_A / EGG_A) * EGG_B * EGG_B / (1 - EGG_K * y)), y);
}
for (int y = EGG_A; y > -EGG_A - 1; y--) {
glVertex2f(-sqrt((1 - y * y / EGG_A / EGG_A) * EGG_B * EGG_B / (1 - EGG_K * y)), y);
}
glEnd();

// draw outline
glColor4f(204 / 255.0, 140 / 255.0, 94 / 255.0, 1);
glLineWidth(1.5); // must call before glBegin
glEnable(GL_LINE_SMOOTH); // smooth
glBegin(GL_LINE_STRIP);
for (int y = -EGG_A; y < EGG_A + 1; y++) {
glVertex2f(sqrt((1 - y * y / EGG_A / EGG_A) * EGG_B * EGG_B / (1 - EGG_K * y)), y);
}
for (int y = EGG_A; y > -EGG_A - 1; y--) {
glVertex2f(-sqrt((1 - y * y / EGG_A / EGG_A) * EGG_B * EGG_B / (1 - EGG_K * y)), y);
}
glEnd();
glDisable(GL_LINE_SMOOTH);

glPopMatrix();
}