Learning opengl
#opengl #reference #guideOpenGL 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
GL_ARRAY_BUFFER
- for buffering vertex data
GL_ELEMENT_ARRAY_BUFFER
- for buffering index data
GL_COPY_READ_BUFFER
GL_COPY_WRITE_BUFFER
GL_PIXEL_PACK_BUFFER
- for working with pix buffersGL_PIXEL_UNPACK_BUFFER
GL_TRANSFORM_FEEDBACK
- for feeding back results from the vertex shader back into itself - really complex stuffGL_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
- To bind the buffer run
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
- i.e
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 dataSTATIC_[DRAW,READ,COPY]
for infrequently changed, frequently used dataDYNAMIC_[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 bufferGL_ELEMENT_ARRAY_BUFFER
allows uploading indices
- then unbind the port to prevent errors
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
Uploading texture data
- Bind buffer to GLTEXTURE2D
glActiveTexture
- activates the texture slotglTexImage2D GL_TEXTURE2D data
- uploads the dataglTexParami GL_TEXTURE2D option value
- sets parameters of the uploaded data
Aside: Compression
- it is possible to compress textures on loading to improve memory usage
Aside: SDL Surfaces
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
Delete
glDeleteVertexArray
- deletes arrays
Drawing
Normal Drawing
glDrawElements
once a program, and vao/vbos (usingglBindBuffer
andglVertexAttribArray
) have been bound, data can be drawnglDrawElements 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
- inside the shader,
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
orGL_READ_FRAME_BUFFER
depending on the goal (reading or writing)
- where port is
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
thenglRenderBufferStorage format w h
- attach to FBO with
glBindFrameBuffer
- then,
glFrameBufferRenderbuffer DRAW/READ_FRAME_BUFFER port name
- to output in shader, write to
gl_frag_data[n]
which binds toCOLOR_ATTACHMENT[n]
glDrawBuffers
allows mapping fbos to elements ofgl_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.