Learning opengl

#opengl #reference #guide

OpenGL has always been a fascination of mine, but I've always found information on the topic were either littered with poor confusing abstractions or too comprehensive to be useful for a quick start.

So, coming back to graphics programming after a relatively long hiatus, I was in a situation where I already had a rough idea of the pipeline and I just wanted a quick reference for setting up the pipeline.

As such, I bit the bullet, read through the OpenGL superbible and have constructed the following cheat-sheet like reference for the core opengl functions for drawing.

I'm publishing it here in the hope that others may find it useful, but I'll warn you ahead of time that I haven't spent too much time in ensuring the documents are presented in the nicest way.

OpenGl

Clearing colour

  • Run glClearBufferfv

    glClearBufferfv (buffer, draw_buffer, value)
    

clears the buffer where

  • buffer is the buffer to set - i.e glCOLOR
  • drawbuffer is which buffer to clear (if there are multiple)
    • for colour there is only one
    • value is the value to set - i.e vector or something
  • run glClearColor
glClearColor

but this does not immediately clear the window, but sets colour when cleared later using

  • run clear
glClear (bit)

Clear the buffer to the values set previously - bit specifies which buffer to clear, can be something like glColor_buffer_bit to clear the color

Overall pipeline

          .............................
          .       Application code      .
          .............................
          .         Open GL           .
          .............................
Texture data .     |Uniform           | Attributes
             .     |                  |
             .     |--------->+-----------+
             .     |          |  Vertex   |
             ................>|  Shader   |
             .     |            +-----------+
             .     |              |        | Out
             .     |              |        |
             .     |            ..............
             .     +--------->.  Fragment  .
             .                  .  Shader    .
             ................>..............
  • Attributes are values that change per vertex - i.e position,
Always stored as four components
2nd,3rd components default to 0 if not given
4th components defaults to 1 if not given
  • Uniforms are constant over multiple calls

Creating shaders

glCreateShader type
will create a shader of the required type
glShaderSource id shader
will buffer the source to the shader object
  • can be called multiple times to add multiple sources
  • this can be used to construct a set of common set of functions across multiple shaders
  • a shader object (compiled once) would define the core functions
  • a second string would be the include directive which lists the prototypes for each of the functions
  • new shader programs would then create shaders while compiling with the header string, then the resulting program would also attach the library files
glCompileShader id
will compile the shader
glGetShaderiv shader COMPILE_STATUS
will retrieve the compilation status of the file
glDeleteShader
to delete the shader
  • Can be called as soon as the program the shader is used in has been linked

Creating programs

glCreateProgram
will create a program object - this is effectively the actual shader that is used
glAttachShader prog shader
will attach a shader to the program - more than one instance of the same type can be attached - if done, then they can share implementations if compiled with suitable prototypes
glBindAttribLocation prog num name
binds one of the vertex array attributes to a particular variable in the program
attribute variables in the shader have a specific type, although when a draw call is made, the only requirement is that the types align - i.e there may be multiple copies of the data, different slices, etc
Must be called before linking
glLinkProgram prog
will link the program
glDeleteShader prog
to delete the shaders used to link the program

Setting up Opengl

We'll go through the steps in more detail later, but at a high level, these are the core steps in setting up an opengl program

  • create shaders
vid = glCreateShader GL_VERTEX_SHADER;
glShaderSource vid "..."; // set shader source (can be called multiple times)
glCompileShader vid; // compile the shader

if(glGetShaderiv vid GL_COMPILE_STATUS) // if there was an error
 do_error_handling ();
  • compile program
pid = glCreateProgram ();
glAttachShader prog vid; // attach shader
glBindAttribLocation prog 0 "variable3"; // bind variable 3 in program to vertex attribute 0
uid = glGetUniformLocation prog "variable"; // get the location id for the given variable
  • upload data
// generate buffers to store data
vbo = glGenBuffer ();

glBindBuffer GL_ARRAY_BUFFER vbo; // attach vbo to the ARRAY_BUFFER entry point
glBufferData GL_ARRAY_BUFFER a b c data; // buffer data into the vbo
glBindBuffer GL_ARRAY_BUFFER 0; // unbind the vbo for safety
  • automate binding calls
vao = glGenVertexArrays (); // create a vertex array

glBindVertexArray vao ; // bind the vao

// bind attrib 0 of vao
glBindBuffer GL_ARRAY_BUFFER vbo; // attach vbo to the ARRAY_BUFFER entry point
glVertexAttribPointer 0 sz typ normd stride 0; // bind current vbo to vertex attribute 0
glBindBuffer GL_ARRAY_BUFFER 0; // unbind vbo for safety

// enable attrib 0 of vao - could be called anywhere
glEnableVertexAttribArray 0; // enable index 0 in the vao

glBindVertexArray 0; // unbind vbo for safety
  • create texture buffers
tid = glGenTextures (); // create texture
glBindTexture GL_TEXTURE_2D tid; // do an initial bind to cast it to the right type
glBindTexture GL_TEXTURE_2D 0; // unbind for safety
  • upload texture data
glBindTexture GL_TEXTURE_2D tid;
glTexImage2D GL_TEXTURE_2D data; // upload the data
glBindTexture GL_TEXTURE_2D 0; // unbind for safety

Using shader programs

glUseProgram
to bind the program
glUseProgram pid
  • then load uniform values and textures
glUniform1f uid 10; // upload 10 to uid c

// in use texture buffer 10 in texture unit 1
glActiveTexture 0;
glBindTexture GL_TEXTURE2D 10; 
  • note uniforms are stored as part of the program, thus will be retained as long as the program is alive
  • then buffer/vertex attrib values
glBindVertexArray vid; // loads data and bindings from vao vid
  • then draw frames
glDrawArrays GL_TRIANGLES 0 count; // runs the shader program on the data from vao count times.

Loading uniforms

glGetUniformLocation program name
find the locatioon of the corresponding uniform value in the program
glUniform[f,i,s,c][v[1,2,3],s]
will buffer data into the uniform
  • note uniforms are stored as part of the program, thus will be retained as long as the program is alive

Working with buffers

  • vertex buffer objects allow storing vertex data on gpu
  • VBO is a buffer object with storage for vertex data
    • created with hints to opengl on where to place data
    • as programs may use several vbos, opengl provides the conept of a VAO
Creating buffers
glGenBuffers num
creates num buffers on gpu
Binding buffers
  • Once a buffer has been constructed, it can be bound
    • I.e inserted into a slot used by the GPU
Such slots would be
  • GL_ARRAY_BUFFER - for buffering vertex data
    • special function glVertexAttribPointer works specifically with the buffer bound to this slot
  • GL_ELEMENT_ARRAY_BUFFER - for buffering index data
    • glDrawElements in contrast to glDrawArray uses these indices, accepting an additional parameter to specify the offset
  • GL_COPY_READ_BUFFER
  • GL_COPY_WRITE_BUFFER
  • GL_PIXEL_PACK_BUFFER - for working with pix buffers
  • GL_PIXEL_UNPACK_BUFFER
  • GL_TRANSFORM_FEEDBACK - for feeding back results from the vertex shader back into itself - really complex stuff
  • GL_UNIFORM_BUFFER - ???
    • To bind the buffer run glBindBuffer id buffer where id is one of the previous
    • pass in 0 as buffer to unbind buffers
Deleting buffers
  • make sure buffer is not bound
  • run glDeleteBuffer buff to delete
Uploading data
  • Bind buffer to port corresponding to data
    • i.e GL_ARRAY_BUFFER for binding vertex data
  • glBufferData port size data type to upload data from CPU to GPU into buffer bound to port
    • data can be null to pre-allocate size to the buffer
    • type is a hint to the openGL to optimize the location
  • STREAM_[DRAW,READ,COPY] for infrequently changed or used read,draw,copy data
  • STATIC_[DRAW,READ,COPY] for infrequently changed, frequently used data
  • DYNAMIC_[DRAW,READ,COPY] for frequently changed, frequently used data
    • then unbind the port to prevent errors glBindBuffer port 0
    • glBufferSubData allows updating parts of a buffer
    • GL_ELEMENT_ARRAY_BUFFER allows uploading indices
Reading Pixel Data
  • first bind a buffer to the PIXEL_BUFFER slot
  • glReadPixels x y w h buf read pixel data
Texture Data
Texturing in GLSL

In order to texture in the glsl shader, a mvp would look like:

uniform sampler2D tex;
in vec2 coord;
color=texture(tex,coord);
Creating texture buffer

To create a texture buffer, run

id = glGenTextures num;
glBindTexture GL_TEXTURE_2D id

It's generally best to bind upon creation, as the binding changes the buffer type

Uploading data

To upload data, simply run

// first bind the texture
glBindTexture GL_TEXTURE_2D id;
// then we must can upload data
glTexImage2D GL_TEXTURE_2D lod format w h 0 format2 typ data
glTexImage2D GL_TEXTURE_2D lod format w h 0 format2 typ data

where

lod
is the the level of detail for mipmaping - set to 0
format
is the internal format for the texture (GL_ALPHA, GL_LUMINANCE, GL_RGB, etc.)
w,h
are as expected
format2
is the format of the input must be the same as internal
typ
is the data type of the data
data
is the data to buffer, if null then allocates memory only
Binding texture

To bind a texture, run

glBindTexture GL_TEXTURE_2D id;

once bound, operations on the target to which it is bound will affect the texture, this persists until another texture is bound. Can be rebound as many times as needed.

Multiple textures

Glsl allows shaders to handle multiple textures - achieved through use of a texture unit.

glActiveTexture GL_TEXTURE[i]

by default this is 0. To support multiple textures, create, bind and upload texture data as usual, then run:

glActiveTexture GL_TEXTURE[i]
glBindTexture target id

to bind the buffer to the target for the ith texture unit.

Then, in the glsl shader, if you had defined

uniform sampler2D texturea;
uniform sampler2D textureb;

to map texturea to unit 0 and textureb to unit 1, run

let a_id = glGetUniformLocation prog "texturea";
let b_id = glGetUniformLocation prog "textureb";
glUniform1i(a_id, 0);
glUniform1i(b_id, 1);

and you're done.

Aside: VAOS

Unfortunately, VAOs don't store data about bound textures, so you'll need to go through the corresponding binding calls for each texture you want:

glActiveTexture GL_TEXTURE[i]
glBindTexture target id
Creating a textured buffer
  • glActiveTexture slot selects the texture slot to insert in - maps to the order of textures defined in the shader
  • where slot is one of GL_TEXTUREi
    • glBindTexture GL_TEXTURE2D tex binds a texture value
      • where tex is the output of an earlier glGenTexture
Uploading texture data
  • Bind buffer to GLTEXTURE2D
  • glActiveTexture - activates the texture slot
  • glTexImage2D GL_TEXTURE2D data - uploads the data
  • glTexParami GL_TEXTURE2D option value - sets parameters of the uploaded data
Aside: Compression
  • it is possible to compress textures on loading to improve memory usage

Vertex Array Object

Creating
glGenVertexArray
constructs a new vertex array object
(no term)
by default, all fields are disabled - use glEnableVertexArrayAttrib to enable the attributes that are used in the vao
(no term)
note: uniforms are not bound to the vertex array object, but to the program itself (after binding the vao)
Binding
glBindVertexArray
binds a vertex array
Uploading data
glBindBuffer GL_ARRAY_BUFFER buffer
to bind a buffer for uploading data
(no term)
once ARRAYBUFFER buffer bound, use any of the following to upload data:
  • glBufferData
  • glBufferSubData
  • glMapBuffer
  • glCopyBuffer
(no term)
once done, then unbind buffer from gl_ARRAY_BUFFER with 0
Pointer
  • Once a buffer has been bound to GL_ARRAY_BUFFER, we can bind it to a vao entry (even before it has been enabled)
  • glVertexAttribPointer loc size type normalized offset will bind the buffer to the vertex attrib
loc is the attrib to bind to
size is one of 1,2,3,4
type is the type of the data
normalized is whether the data is normalized
offset is the offset into the data where it begins
Delete
glDeleteVertexArray
deletes arrays

Drawing

Normal Drawing
  • glDrawElements once a program, and vao/vbos (using glBindBuffer and glVertexAttribArray) have been bound, data can be drawn
  • glDrawElements mode count type offset will run the shader using the current vertex attribus, using the primitive given by mode
    • if geometry shaders are used, the mode should match with the specification of the geometry shader
Instanced Rendering
  • instanced rendering allows rendering a single common set of data over multiple stes
  • glDrawArrayInstanced mode first count instances runs the shader instances times
    inside the shader, glInstanceID
    can be used to check which instance is currently being drawn
    glVertexAttribDivisor index divisor
    specifies how often the attribute should increment (0 - every vertex, 1 for every instance, 2 for every two instances)
    • This allows an alternative to using arrays as input data, and then using glInstanceId to index into the input

Frame buffer objects

FBOS encapsulate state for drawing framebuffer and can be rendered directly to, allowing rendering offscreen

Creating
  • Similar a normal buffer glGenFrameBuffers count
Binding
  • only one (for drawing or separately reading) can be bound at a time
  • binds a framebuffer
    • where port is GL_DRAW_FRAME_BUFFER or GL_READ_FRAME_BUFFER depending on the goal (reading or writing)
Deleting
glDeleteFrameBuffer
to delete
Rendering to a frame buffer
  • first, create an image surface for attaching fbos using glGenRenderBuffer
  • bind this buffer with glBindRenderBuffer GL_RENDER_BUFFER name
  • allocate storage for the render buffer using glBindRenderBuffer then glRenderBufferStorage format w h
  • attach to FBO with glBindFrameBuffer
  • then, glFrameBufferRenderbuffer DRAW/READ_FRAME_BUFFER port name
Choose between the draw or read options depending on what was chosen when binding
port is one of DEPTHATTACHMENT[1..] or COLORATTACHMENT[1…]
name is the render buffer
  • to output in shader, write to gl_frag_data[n] which binds to COLOR_ATTACHMENT[n]
  • glDrawBuffers allows mapping fbos to elements of gl_frag_data[n]

Geometry shaders

  • variant of shader that unlike vertex shader which operates on vertices or fragment shaders which operate on pixels, operates on primitives (i.e the vertices forming a triangle)
  • inputs are declared at the top of the shader as layout (format) in
    • where format is one of the drawing primitives - i.e triangles trianglestrip
  • outputs are also declared in a simmilar form, but also have a field layout (max_vertices = k) out specifying maximum number of output vertices
  • gl_in[n] contains glattributes for nth vertex:
    • gl_Position
    • gl_pointsize
    • gl_clipDistance
  • emitVertex() allows emitting a new vertex in the current primitive based on values of glPosition etc.
    • must be called less than the maxvertices parameter defined at the start of the program
  • endPrimitive() combines all emitted vertices into a primitive (i.e like a triangle) - if omitted, the shader will simply drop the current primitive can be called multiple times