OpenGL Programming/Modern OpenGL Tutorial drafts
Optimization
editIn addition we can set up back-face culling:
- glEnable(GL_CULL_FACE); // disabled by default
- Behavior specified by glCullFace(GL_BACK) and glFrontFace(GL_CCW) by default
Covered in GLSL_Programming/GLUT/Cutaways.
Exemple: show a rotating cube with one of the facet missing - can we see inside the cube? What are the possible work-arounds?
Sound input
edit- Use OpenAL to capture sound from microphone, upload data unmodified to graphics card, display oscilloscope signal.
Image enhancement techniques
edit- Contrast and brightness adjustments.
- Gamma correction.
- Sharpening / blurring using small convolution matrices.
- Using multiple texture units to perform darkfield and flatfield correction of CCD/CMOS images.
- Combine multiple exposures into a single LDR image.
Technique: I will start with loading three textures, I, D and F, binding them to separate texture units, and calculating (I - D) * F in the fragment shader. For things like gamma correction and small convolutions I will use a single texture, so it can be applied either something that has been uploaded previously or the contents of a FBO.
Depth sorting
editUseful if your depth buffer has low precision, you want to do transparency, or for some shadow techniques
Vertex water effect
editOne can achieve a "under water" effect by adding a random noise for each vertex:
GLint attribute_v_rand;
attribute_name = "v_rand";
attribute_v_rand = glGetAttribLocation(program, attribute_name);
if (attribute_v_rand == -1) {
fprintf(stderr, "Could not bind attribute %s\n", attribute_name);
return 0;
}
mesh.rand.resize(mesh.vertices.size());
srand(glutGet(GLUT_ELAPSED_TIME) / 100);
for (int i = 0; i < mesh.rand.size(); i++)
mesh.rand[i] = 1.0f*rand()/RAND_MAX * 0.1;
glEnableVertexAttribArray(attribute_v_rand);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(
attribute_v_rand, // attribute
1, // number of elements per vertex
GL_FLOAT, // the type of each element
GL_FALSE, // take our values as-is
0, // no extra data between each position
mesh.rand.data() // offset of first element
);
srand is modified every 1/10 second, so the animation is not too frenzy.
attribute float v_rand;
gl_Position = mvp * v_coord;
gl_Position.x += v_rand;
gl_Position.y += v_rand;
I was trying a get a per-pixel postfx but this could be used for morphing effects and such.
2D Scaling as shaders?
editPaul sends me:
- I just realised that there are a ton of upscaling algorithms for bitmap
- artwork and that it might be an interesting exercise to implement some
- or all of them using GPU shaders and modern OpenGL.
- http://research.microsoft.com/en-us/um/people/kopf/pixelart/supplementary/index.html
- http://scale2x.sourceforge.net/
Sounds like a good idea!
multiple textures and
shader changed for different triangles
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <SOIL/SOIL.h>
#include <cstring>
const char* f_shader = ""
"varying vec2 f_texcoord;"
"uniform sampler2D mytexture;"
"void main(void) {"
" vec2 flipped_texcoord = vec2(f_texcoord.x, 1.0 - f_texcoord.y);"
" gl_FragColor = texture2D(mytexture, flipped_texcoord);"
"}";
const char* v_shader = ""
"attribute vec3 coord3d;"
"attribute vec2 texcoord;"
"varying vec2 f_texcoord;"
"uniform mat4 mvp;"
"uniform mat4 projMat;"
"void main(void) {"
" gl_Position = projMat * mvp * vec4(coord3d, 1.0);"
" f_texcoord = texcoord;"
"}";
typedef struct {
int width;
int height;
unsigned char* data;
}Image;
Image* images[2];
int screen_width=800, screen_height=600;
GLuint program;
GLuint texture_id[2];
GLint attribute_coord3d, attribute_texcoord;
GLint uniform_mvp, uniform_mytexture;
GLuint vbo_cube_vertices[2], vbo_cube_texcoords[2];
GLuint ibo_cube_elements[2];
GLuint create_shader(const char* source, GLenum type){
GLuint res = glCreateShader(type);
const GLchar* sources[] = {
#ifdef GL_ES_VERSION_2_0
"#version 100\n"
#else
"#version 120\n"
#endif
,
#ifdef GL_ES_VERSION_2_0
(type == GL_FRAGMENT_SHADER) ?
"#ifdef GL_FRAGMENT_PRECISION_HIGH \n"
"precision highp float; \n"
"#else \n"
"precision mediump float; \n"
"#endif \n"
: ""
#else
"#define lowp \n"
"#define mediump\n"
"#define highp \n"
#endif
,
source };
glShaderSource(res, 3, sources, NULL);
glCompileShader(res);
GLint compile_ok = GL_FALSE;
glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
if (compile_ok == GL_FALSE) {
glDeleteShader(res);
return 0;
}
return res;
}
Image* loadImage(const char* filename){
Image* image = (Image*) malloc(sizeof(Image));
int width;
image->data = SOIL_load_image(filename, &(image->width), &(image->height), NULL, 0);
return image;
}
void applyImage(Image* image, int index){
glBindTexture(GL_TEXTURE_2D, texture_id[index]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->width, image->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->data);
}
int init_resources(){
GLfloat cube_vertices[] = {
// front
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
};
glGenBuffers(1, &vbo_cube_vertices[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_vertices[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW);
GLfloat cube_texcoords[2*4*6] = {
// front
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
};
//for (int i = 1; i < 6; i++)
// memcpy(&cube_texcoords[i*4*2], &cube_texcoords[0], 2*4*sizeof(GLfloat));
glGenBuffers(1, &vbo_cube_texcoords[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_texcoords[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_texcoords), cube_texcoords, GL_STATIC_DRAW);
GLushort cube_elements[] = {
// front
0, 1, 2,
2, 3, 0,
};
glGenBuffers(1, &ibo_cube_elements[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_cube_elements[0]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_elements), cube_elements, GL_STATIC_DRAW);
glGenTextures(1, &texture_id[0]);
images[0] = loadImage("image1.png");
applyImage(images[0], 0);
glGenTextures(1, &texture_id[1]);
images[1] = loadImage("image2.png");
applyImage(images[1], 1);
GLint link_ok = GL_FALSE;
GLuint vs, fs;
if ((vs = create_shader(v_shader, GL_VERTEX_SHADER)) == 0) return 0;
if ((fs = create_shader(f_shader, GL_FRAGMENT_SHADER)) == 0) return 0;
program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
const char* attribute_name;
attribute_name = "coord3d";
attribute_coord3d = glGetAttribLocation(program, attribute_name);
attribute_name = "texcoord";
attribute_texcoord = glGetAttribLocation(program, attribute_name);
const char* uniform_name;
uniform_name = "mvp";
uniform_mvp = glGetUniformLocation(program, uniform_name);
uniform_name = "mytexture";
uniform_mytexture = glGetUniformLocation(program, uniform_name);
GLfloat cube_vertices2[] = {
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
-1.0, 1.0, -1.0,
};
glGenBuffers(1, &vbo_cube_vertices[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_vertices[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices2), cube_vertices2, GL_STATIC_DRAW);
GLfloat cube_texcoords2[2*4*6] = {
// front
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
};
glGenBuffers(1, &vbo_cube_texcoords[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_texcoords[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_texcoords2), cube_texcoords2, GL_STATIC_DRAW);
GLushort cube_elements2[] = {
0, 1, 2,
2, 3, 0,
};
glGenBuffers(1, &ibo_cube_elements[1]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_cube_elements[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_elements2), cube_elements2, GL_STATIC_DRAW);
return 1;
}
void setTexture(int id){
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_id[id]);
glUniform1i(uniform_mytexture, 0);
}
void resetShader(){
unsigned int loc12 = glGetUniformLocation(program,"projMat");
glm::mat4 empty = glm::mat4(1.0);
glUniformMatrix4fv(loc12, 1, GL_FALSE, glm::value_ptr(empty));
}
void setShader(){
GLfloat angle = glutGet(GLUT_ELAPSED_TIME) / 1000.0 * 15;
glm::mat4 anim =
glm::rotate(glm::mat4(1.0f), angle*1.0f, glm::vec3(1, 0, 0)) *
glm::rotate(glm::mat4(1.0f), angle*1.0f, glm::vec3(0, 1, 0)) *
glm::rotate(glm::mat4(1.0f), angle*1.0f, glm::vec3(0, 0, 1)) ;
glm::mat4 schrink = glm::scale(glm::mat4(1.0f),glm::vec3(0.5f));
glm::mat4 final = anim;
glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(final));
}
void setScale() {
unsigned int loc1 = glGetUniformLocation(program,"projMat");
glm::mat4 schrink2 = glm::scale(glm::mat4(1.0f),glm::vec3(0.5f));
glUniformMatrix4fv(loc1, 1, GL_FALSE, glm::value_ptr(schrink2));
}
void setScale2() {
unsigned int loc1 = glGetUniformLocation(program,"projMat");
glm::mat4 schrink2 = glm::scale(glm::mat4(1.0f),glm::vec3(0.2f));
glUniformMatrix4fv(loc1, 1, GL_FALSE, glm::value_ptr(schrink2));
}
void onDisplay(){
setShader();
setScale();
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glUseProgram(program);
setTexture(0);
// /*
glEnableVertexAttribArray(attribute_coord3d);
glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_vertices[0]);
glVertexAttribPointer(attribute_coord3d, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(attribute_texcoord);
glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_texcoords[0]);
glVertexAttribPointer( attribute_texcoord, 2, GL_FLOAT, GL_FALSE, 0, 0 );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_cube_elements[0]);
int size; glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
glDrawElements(GL_TRIANGLES, size/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
// */
resetShader();
setShader();
setScale2();
setTexture(1);
// /*
glEnableVertexAttribArray(attribute_coord3d);
glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_vertices[1]);
glVertexAttribPointer(attribute_coord3d, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(attribute_texcoord);
glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_texcoords[1]);
glVertexAttribPointer( attribute_texcoord, 2, GL_FLOAT, GL_FALSE, 0, 0 );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_cube_elements[1]);
int size2; glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size2);
glDrawElements(GL_TRIANGLES, size2/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
// */
glDisableVertexAttribArray(attribute_coord3d);
glDisableVertexAttribArray(attribute_texcoord);
glutSwapBuffers();
glutPostRedisplay();
}
void onReshape(int width, int height) {
screen_width = width;
screen_height = height;
glViewport(0, 0, screen_width, screen_height);
}
void free_resources(){
glDeleteProgram(program);
glDeleteBuffers(1, &vbo_cube_vertices[0]);
glDeleteBuffers(1, &vbo_cube_texcoords[0]);
glDeleteBuffers(1, &ibo_cube_elements[0]);
glDeleteTextures(1, &texture_id[0]);
glDeleteTextures(1, &texture_id[1]);
}
int main(int argc, char* argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA|GLUT_ALPHA|GLUT_DOUBLE|GLUT_DEPTH);
glutInitWindowSize(screen_width, screen_height);
glutCreateWindow("My Textured Cube");
GLenum glew_status = glewInit();
if (init_resources() ) {
glutDisplayFunc(onDisplay);
glutReshapeFunc(onReshape);
// glutIdleFunc(onIdle);
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glutMainLoop();
}
free_resources();
return 0;
}