OpenGL Programming/Modern OpenGL Tutorial drafts

Optimization edit

In 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 edit

Useful if your depth buffer has low precision, you want to do transparency, or for some shadow techniques

Vertex water effect edit

One 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? edit

Paul 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!

< OpenGL Programming

Browse & download complete code  

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;
}