Some notes on OpenGL
FBO + Render-To-Texture + Depth testing
When you use and FBO to render the scene into a texture and then
another pass to apply some effect, you need to make sure that you're
not using the same depth values (gl_Position.z) because else the second
pass will not be rendered when depth testing is enabled. A simple fix for
this (but this might be a bit heavy if you do it do often) is turning on/off
depth testing using glEnable(GL_DEPTH_TEST)
and glDisable(GL_DEPTH_TEST)
Texture formats
When you use glTexImage2D()
you specify the internalFormat
, format
and the type
of your texture data. Choosing the correct values is important
when you're streaming data to the gpu as the driver might want to rearrange
the pixel data you provide which you really don't want.
internalFormat:
number of color components in the texture (GL_R, GL_RG, GL_RGB, GL_RGBA).format:
format of the pixel data triggers swizzling! (GL_BGRA, GL_YCBCR_422_APPLE)type:
data type of the pixel data (GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT)
Apple advices to use these formats
and types
Format With type -------------------------------------------------------- GL_BGRA | GL_UNSIGNED_INT_8_8_8_8_REV GL_BGRA | GL_UNSIGNED_SHORT_1_5_5_5_REV GL_YCBCR_422_APPLE | GL_UNSIGNED_SHORT_8_8_REV_APPLE --------------------------------------------------------
YUV420P (I420p) shader
Snippet to remember for video decoding on gpu
static const char* DEBUG_VS = "" "#version 150\n" "const vec2 verts[4] = vec2[] (" " vec2(-1.0, 1.0), " " vec2(-1.0, -1.0), " " vec2(1.0, 1.0), " " vec2(1.0, -1.0) " ");" "const vec2 texcoords[4] = vec2[] (" " vec2(0.0, 0.0), " " vec2(0.0, 1.0), " " vec2(1.0, 0.0), " " vec2(1.0, 1.0) " ");" "out vec2 v_texcoord;" "void main() {" " gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);" " v_texcoord = texcoords[gl_VertexID];" "}" ; static const char* DEBUG_FS = "" "#version 150\n" "uniform sampler2D y_tex;" "uniform sampler2D u_tex;" "uniform sampler2D v_tex;" "in vec2 v_texcoord;" "out vec4 fragcolor;" "const vec3 R_cf = vec3(1.164383, 0.000000, 1.596027);" "const vec3 G_cf = vec3(1.164383, -0.391762, -0.812968);" "const vec3 B_cf = vec3(1.164383, 2.017232, 0.000000);" "const vec3 offset = vec3(-0.0625, -0.5, -0.5);" "void main() {" " float y = texture(y_tex, v_texcoord).r;" " float u = texture(u_tex, v_texcoord).r;" " float v = texture(v_tex, v_texcoord).r;" " vec3 yuv = vec3(y,u,v);" " yuv += offset;" " fragcolor = vec4(0.0, 0.0, 0.0, 1.0);" " fragcolor.r = dot(yuv, R_cf);" " fragcolor.g = dot(yuv, G_cf);" " fragcolor.b = dot(yuv, B_cf);" "}";
FBOs with different sized texture attachments
With the framebuffer feature it's possible to render offscreen into what is called color attachments. Color attachments can be textures or render buffers for example.
Although the specification states that you can use different sized
color attachments, which is true, you need to be aware that the
effective FBO size, is the size of the intersection of the attachments
dimensions... aka the smallest dimension. This is important when you
e.g. need to use glBlitFramebuffer
as it will only blit the smallest
area.
See this comment "Notice that there is no restriction based on size. The effective size of the FBO is the intersection of all of the sizes of the bound images (ie: the smallest in each dimension)." on the openGL wiki
Attribute less rendering
By using the build in gl_VertexID
variable in your vertex
shader and defining a array for vertex positions and texture
coordinates you can draw basic triangle strips withouth using
a VBO. You still need to create a vertex array object but you
don't need to set any attributes or pointers. See the shader
below which uses this attribute less rendering technique.
static const char* FULLSCREEN_VS = "" "#version 150\n" "const vec2 verts[4] = vec2[] (" " vec2(-1.0, 1.0), " " vec2(-1.0, -1.0), " " vec2(1.0, 1.0), " " vec2(1.0, -1.0)" ");" "const vec2 tex[4] = vec2[] (" " vec2(0.0, 1.0), " " vec2(0.0, 0.0), " " vec2(1.0, 1.0), " " vec2(1.0, 0.0)" ");" "out vec2 v_tex; " "void main() {" " gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);" " v_tex = tex[gl_VertexID];" "}" ""; static const char* TEX_FS = "" "#version 150\n" "uniform sampler2D u_tex;" "in vec2 v_tex;" "out vec4 fragcolor;" "void main() {" " vec4 tc = texture(u_tex, v_tex);" " fragcolor.rgb = tc.rgb;" " fragcolor.a = 1.0f;" "}" "";
By using the above shader and by taking care of your model matrix in a smart way you can draw textures using an orthographic matrix in such a way that the texture can be drawn at any location, size and rotation. In the following code I'm setting up a ortho matrix. See the updated attribute less shader below too.
bool Graphics::setup() // tex_pm is a Mat4 and is the projection matrix glGenVertexArrays(1, &tex_vao); tex_pm.ortho(0.0f, settings.win_w, settings.win_h, 0.0f, 0.0f, 100.0f); tex_vs = rx_create_shader(GL_VERTEX_SHADER, TEX_VS); tex_fs = rx_create_shader(GL_FRAGMENT_SHADER, TEX_FS); tex_prog = rx_create_program(tex_vs, tex_fs); glLinkProgram(tex_prog); rx_print_shader_link_info(tex_prog); glUseProgram(tex_prog); glUniformMatrix4fv(glGetUniformLocation(tex_prog, "u_pm"), 1, GL_FALSE, tex_pm.ptr()); return true } void Graphics::drawTexture(GLuint tex, float x, float y, float w, float h) { glUseProgram(tex_prog); glActiveTexture(GL_TEXTURE0); glBindVertexArray(tex_vao); float hw = w * 0.5; float hh = h * 0.5; // use the following model matrix to draw the texture at the given (x,y) and (width,height) // here I also rotate (around the center) tex_mm.identity(); tex_mm.translate(x + hw, y + hh, 0.0f); tex_mm.rotate(45.0f * DEG_TO_RAD, 0.0f, 0.0f, 1.0f); tex_mm.scale(hw, hh, 1.0f); glUniformMatrix4fv(glGetUniformLocation(tex_prog, "u_mm"), 1, GL_FALSE, tex_mm.ptr()); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); }
And the (updated) shader:
static const char* TEX_VS = "" "#version 150\n" "uniform mat4 u_pm;" "uniform mat4 u_mm;" "const vec2 verts[4] = vec2[] (" " vec2(-1.0, 1.0), " " vec2(-1.0, -1.0), " " vec2(1.0, 1.0), " " vec2(1.0, -1.0)" ");" "const vec2 tex[4] = vec2[] (" " vec2(0.0, 1.0), " " vec2(0.0, 0.0), " " vec2(1.0, 1.0), " " vec2(1.0, 0.0)" ");" "out vec2 v_tex; " "void main() {" " vec4 vert = vec4(verts[gl_VertexID], 0.0, 1.0);" " gl_Position = vert; " " gl_Position = u_pm * vert;" " gl_Position = u_pm * u_mm * vert;" " v_tex = tex[gl_VertexID];" "}" ""; static const char* TEX_FS = "" "#version 150\n" "in vec2 v_tex;" "out vec4 fragcolor;" "void main() {" " fragcolor = vec4(1.0, 0.0, 0.0, 1.0);" "}" "";