// BindFrame creates a framebuffer object with an associated texture. // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/ // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/ func (gc *opengl) BindFrame(buf int, fbo, tid, db *uint32) (err error) { size := int32(1024) gl.GenFramebuffers(1, fbo) gl.BindFramebuffer(gl.FRAMEBUFFER, *fbo) // Create a texture specifically for the framebuffer. gl.GenTextures(1, tid) gl.BindTexture(gl.TEXTURE_2D, *tid) switch buf { case IMAGE_BUFF: gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Pointer(nil)) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) // Add a depth buffer to mimic the normal framebuffer behaviour for 3D objects. gl.GenRenderbuffers(1, db) gl.BindRenderbuffer(gl.RENDERBUFFER, *db) gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT, size, size) gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, *db) // Associate the texture with the framebuffer. gl.FramebufferTexture(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, *tid, 0) buffType := uint32(gl.COLOR_ATTACHMENT0) gl.DrawBuffers(1, &buffType) case DEPTH_BUFF: gl.TexImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT16, size, size, 0, gl.DEPTH_COMPONENT, gl.FLOAT, gl.Pointer(nil)) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC, gl.LEQUAL) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE) // Associate the texture with the framebuffer. gl.FramebufferTexture(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, *tid, 0) gl.DrawBuffer(gl.NONE) default: return fmt.Errorf("BindFrame unrecognized buffer type.") } // Report any problems. glerr := gl.CheckFramebufferStatus(gl.FRAMEBUFFER) if glerr != gl.FRAMEBUFFER_COMPLETE { return fmt.Errorf("BindFrame error %X", glerr) } if glerr := gl.GetError(); glerr != gl.NO_ERROR { err = fmt.Errorf("Failed binding framebuffer %X", glerr) } gl.BindFramebuffer(gl.FRAMEBUFFER, 0) // clean up by resetting to default framebuffer. return err }
// Render implementation. // FUTURE: all kinds of possible optimizations that would need to be // profiled before implementing. // • group by vao to avoid switching vao's. // • group by texture to avoid switching textures. // • use interleaved vertex data. // • uniform buffers http://www.opengl.org/wiki/Uniform_Buffer_Object. // • ... lots more possiblities... leave your fav here. func (gc *opengl) Render(dr Draw) { d, ok := dr.(*draw) if !ok || d == nil { return } // switch state only if necessary. if gc.depthTest != d.depth { if d.depth { gl.Enable(gl.DEPTH_TEST) } else { gl.Disable(gl.DEPTH_TEST) } gc.depthTest = d.depth } // switch render framebuffer only if necessary. The framebuffer // is used to render to a texture associated with a framebuffer. if gc.fbo != d.fbo { gl.BindFramebuffer(gl.FRAMEBUFFER, d.fbo) if d.fbo == 0 { gl.Viewport(0, 0, gc.vw, gc.vh) } else { gl.Clear(gl.DEPTH_BUFFER_BIT) gl.Viewport(0, 0, 1024, 1024) // size convention for framebuffer texture. } gc.fbo = d.fbo } // switch shaders only if necessary. if gc.shader != d.shader { gl.UseProgram(d.shader) gc.shader = d.shader } // Ask the model to bind its provisioned uniforms. // FUTURE: only need to bind uniforms that have changed. gc.bindUniforms(d) // bind the data buffers and render. gl.BindVertexArray(d.vao) switch d.mode { case LINES: gl.PolygonMode(gl.FRONT_AND_BACK, gl.LINE) gl.DrawElements(gl.LINES, d.numFaces, gl.UNSIGNED_SHORT, 0) gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL) case POINTS: gl.Enable(gl.PROGRAM_POINT_SIZE) gl.DrawArrays(gl.POINTS, 0, d.numVerts) gl.Disable(gl.PROGRAM_POINT_SIZE) case TRIANGLES: if len(d.texs) > 1 && d.texs[0].fn > 0 { // Multiple textures on one model specify which verticies they apply to. for _, tex := range d.texs { // Use the same texture unit and sampler. Just update which // image is being sampled. gl.BindTexture(gl.TEXTURE_2D, tex.tid) // fn is the number of triangles, 3 indicies per triangle. // f0 is the offset in triangles where each triangle has 3 indicies // of 2 bytes (uShort) each. gl.DrawElements(gl.TRIANGLES, tex.fn*3, gl.UNSIGNED_SHORT, int64(3*2*tex.f0)) } } else { // Single textures are handled with a standard bindUniforms gl.DrawElements(gl.TRIANGLES, d.numFaces, gl.UNSIGNED_SHORT, 0) } } }