// drawScene renders the 3D models consisting of one VAO func (tag *trtag) drawScene() { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) tag.checkError("gl.Clear") gl.UseProgram(tag.shaders) tag.checkError("gl.UseProgram") gl.BindVertexArray(tag.vao) tag.checkError("gl.BindVertexArray") // Use a modelview matrix and quaternion to rotate the 3D object. tag.mvp64.SetQ(lin.NewQ().SetAa(0, 1, 0, lin.Rad(-tag.rotateAngle))) tag.mvp64.TranslateMT(0, 0, -4) tag.mvp.Set(tag.mvp64.Mult(tag.mvp64, tag.persp)) gl.UniformMatrix4fv(tag.mvpRef, 1, false, tag.mvp.Pointer()) if err := gl.GetError(); err != 0 { fmt.Printf("gl.UniformMatrix error %d\n", err) } gl.DrawElements(gl.TRIANGLES, int32(len(tag.faces)), gl.UNSIGNED_BYTE, 0) if err := gl.GetError(); err != 0 { fmt.Printf("gl.DrawElements error %d\n", err) } // cleanup gl.UseProgram(0) tag.checkError("gl.UseProgram-0") gl.BindVertexArray(0) tag.checkError("gl.BindVertexArray-0") // rotate based on time... not on how fast the computer runs. if time.Now().Sub(tag.lastTime).Seconds() > 0.01 { tag.rotateAngle += 1 tag.lastTime = time.Now() } }
// drawScene renders the shader-only scene. func (sf *sftag) drawScene() { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.UseProgram(sf.shaders) gl.BindVertexArray(sf.vao) timeSinceStart := time.Since(sf.sTime).Seconds() gl.Uniform1f(sf.gTime, float32(timeSinceStart)) gl.Uniform2f(sf.sizes, 500, 500) gl.UniformMatrix4fv(sf.mvpref, 1, false, sf.mvp.Pointer()) gl.DrawElements(gl.TRIANGLES, int32(len(sf.faces)), gl.UNSIGNED_BYTE, 0) // cleanup gl.UseProgram(0) gl.BindVertexArray(0) }
// drawScene renders the single texture on the quad. func (rt *rtrace) drawScene() { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.UseProgram(rt.shaders) gl.Uniform1i(rt.tex2D, 0) gl.ActiveTexture(gl.TEXTURE0 + 0) gl.BindVertexArray(rt.vao) gl.UniformMatrix4fv(rt.mvpId, 1, false, rt.mvp.Pointer()) gl.DrawElements(gl.TRIANGLES, int32(len(rt.faces)), gl.UNSIGNED_BYTE, 0) // cleanup gl.ActiveTexture(0) gl.UseProgram(0) gl.BindVertexArray(0) }
// render draws the scene consisting of one VAO func (ld *ldtag) render() { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.UseProgram(ld.shaders) gl.BindVertexArray(ld.vao) // use a model-view-projection matrix ld.mvp64.Set(lin.M4I).ScaleSM(0.5, 0.5, 0.5).TranslateMT(0, 0, -2) ld.mvp.Set(ld.mvp64.Mult(ld.mvp64, ld.persp)) gl.UniformMatrix4fv(ld.mvpref, 1, false, ld.mvp.Pointer()) gl.DrawElements(gl.TRIANGLES, ld.faceCount, gl.UNSIGNED_SHORT, 0) // cleanup gl.UseProgram(0) gl.BindVertexArray(0) }
// Renderer implementation. // BindMesh copies the given mesh data to the GPU // and initializes the vao and buffer references. func (gc *opengl) BindMesh(vao *uint32, vdata map[uint32]Data, fdata Data) error { if glerr := gl.GetError(); glerr != gl.NO_ERROR { return fmt.Errorf("BindMesh needs to find and fix prior error %X", glerr) } // Reuse existing vao's. if *vao == 0 { gl.GenVertexArrays(1, vao) } gl.BindVertexArray(*vao) for _, vbuff := range vdata { vd, ok := vbuff.(*vertexData) if ok && vd.rebind { gc.bindVertexBuffer(vd) vd.rebind = false } } if glerr := gl.GetError(); glerr != gl.NO_ERROR { return fmt.Errorf("BindMesh failed to bind vb %X", glerr) } if fd, ok := fdata.(*faceData); ok { if fd.rebind { gc.bindFaceBuffer(fd) fd.rebind = false } } if glerr := gl.GetError(); glerr != gl.NO_ERROR { return fmt.Errorf("BindMesh failed to bind fb %X", glerr) } return nil }
// 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 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) } } }
// initScene is one time initialization that creates a single VAO func (tag *trtag) initScene() { tag.mvp64 = lin.NewM4() tag.persp = lin.NewM4().Persp(60, float64(600)/float64(600), 0.1, 50) tag.mvp = render.NewMvp() tag.initData() // Bind the OpenGL calls and dump some version info. gl.Init() fmt.Printf("%s %s", gl.GetString(gl.RENDERER), gl.GetString(gl.VERSION)) fmt.Printf(" GLSL %s\n", gl.GetString(gl.SHADING_LANGUAGE_VERSION)) // Gather the one scene into this one vertex array object. gl.GenVertexArrays(1, &tag.vao) gl.BindVertexArray(tag.vao) // create shaders tag.initShader() gl.UseProgram(tag.shaders) // vertex data. var vbuff uint32 gl.GenBuffers(1, &vbuff) gl.BindBuffer(gl.ARRAY_BUFFER, vbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(tag.verticies)*4), gl.Pointer(&(tag.verticies[0])), gl.STATIC_DRAW) vattr := uint32(gl.GetAttribLocation(tag.shaders, "in_v")) gl.EnableVertexAttribArray(vattr) gl.VertexAttribPointer(vattr, 3, gl.FLOAT, false, 0, 0) // colour data. var cbuff uint32 gl.GenBuffers(1, &cbuff) gl.BindBuffer(gl.ARRAY_BUFFER, cbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(tag.colour)*4), gl.Pointer(&(tag.colour[0])), gl.STATIC_DRAW) cattr := uint32(gl.GetAttribLocation(tag.shaders, "in_c")) gl.EnableVertexAttribArray(cattr) gl.VertexAttribPointer(cattr, 4, gl.FLOAT, false, 0, 0) // faces data. var ebuff uint32 gl.GenBuffers(1, &ebuff) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebuff) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int64(len(tag.faces)), gl.Pointer(&(tag.faces[0])), gl.STATIC_DRAW) // set some state that doesn't need to change during drawing. gl.ClearColor(0.0, 0.0, 0.0, 1.0) gl.Enable(gl.CULL_FACE) gl.CullFace(gl.BACK) }
// initScene is one time initialization that creates a single VAO func (sf *sftag) initScene() { sf.sTime = time.Now() sf.initData() // Bind the OpenGL calls and dump some version info. gl.Init() fmt.Printf("%s %s", gl.GetString(gl.RENDERER), gl.GetString(gl.VERSION)) fmt.Printf(" GLSL %s\n", gl.GetString(gl.SHADING_LANGUAGE_VERSION)) gl.GenVertexArrays(1, &sf.vao) gl.BindVertexArray(sf.vao) // vertex data. var vbuff uint32 gl.GenBuffers(1, &vbuff) gl.BindBuffer(gl.ARRAY_BUFFER, vbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(sf.verticies)*4), gl.Pointer(&(sf.verticies[0])), gl.STATIC_DRAW) gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(0) // faces data. var ebuff uint32 gl.GenBuffers(1, &ebuff) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebuff) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int64(len(sf.faces)), gl.Pointer(&(sf.faces[0])), gl.STATIC_DRAW) // create texture and shaders after all the data has been set up. // renderer := render.New() shader := "fire" loader := load.NewLoader() vsrc, verr := loader.Vsh(shader) fsrc, ferr := loader.Fsh(shader) if verr == nil && ferr == nil { sf.shaders = gl.CreateProgram() if err := gl.BindProgram(sf.shaders, vsrc, fsrc); err != nil { fmt.Printf("Failed to create program: %s\n", err) } sf.mvpref = gl.GetUniformLocation(sf.shaders, "mvpm") sf.gTime = gl.GetUniformLocation(sf.shaders, "time") sf.sizes = gl.GetUniformLocation(sf.shaders, "screen") sf.mvp = render.NewMvp().Set(lin.NewM4().Ortho(0, 4, 0, 4, 0, 10)) // set some state that doesn't need to change during drawing. gl.ClearColor(0.0, 0.0, 0.0, 1.0) } }
// initScene is called once on startup to load the 3D data. func (ld *ldtag) initScene() { ld.persp = lin.NewM4() ld.mvp64 = lin.NewM4() ld.mvp = render.NewMvp() gl.Init() ldr := load.NewLoader() meshes, _ := ldr.Obj("monkey") mesh := meshes[0] ld.faceCount = int32(len(mesh.F)) // Gather the one scene into this one vertex array object. gl.GenVertexArrays(1, &ld.vao) gl.BindVertexArray(ld.vao) // vertex data. var vbuff uint32 gl.GenBuffers(1, &vbuff) gl.BindBuffer(gl.ARRAY_BUFFER, vbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(mesh.V)*4), gl.Pointer(&(mesh.V[0])), gl.STATIC_DRAW) gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(0) // normal data. var nbuff uint32 gl.GenBuffers(1, &nbuff) gl.BindBuffer(gl.ARRAY_BUFFER, nbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(mesh.N)*4), gl.Pointer(&(mesh.N[0])), gl.STATIC_DRAW) gl.VertexAttribPointer(1, 3, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(1) // faces data, uint32 in this case, so 4 bytes per element. var fbuff uint32 gl.GenBuffers(1, &fbuff) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, fbuff) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int64(len(mesh.F)*2), gl.Pointer(&(mesh.F[0])), gl.STATIC_DRAW) ld.initShader() gl.ClearColor(0.2, 0.2, 0.2, 1.0) gl.Enable(gl.DEPTH_TEST) gl.Enable(gl.CULL_FACE) // set the initial perspetive matrix. ld.resize(0, 0, 800, 600) }
// initRender is one time initialization that creates a single VAO // to display a single ray trace generated texture. func (rt *rtrace) initRender() { rt.verts = []float32{ // four verticies for a quad. 0, 0, 0, 4, 0, 0, 0, 4, 0, 4, 4, 0, } rt.faces = []uint8{ // create quad from 2 triangles. 0, 2, 1, 1, 2, 3, } rt.uvs = []float32{ // texture coordinates to sample the image. 0, 0, 1, 0, 0, 1, 1, 1, } // Start up OpenGL and create a single vertex array object. gl.Init() gl.GenVertexArrays(1, &rt.vao) gl.BindVertexArray(rt.vao) // vertex data. var vbuff uint32 gl.GenBuffers(1, &vbuff) gl.BindBuffer(gl.ARRAY_BUFFER, vbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(rt.verts)*4), gl.Pointer(&(rt.verts[0])), gl.STATIC_DRAW) gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(0) // faces data. var ebuff uint32 gl.GenBuffers(1, &ebuff) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebuff) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int64(len(rt.faces)), gl.Pointer(&(rt.faces[0])), gl.STATIC_DRAW) // texture coordatinates var tbuff uint32 gl.GenBuffers(1, &tbuff) gl.BindBuffer(gl.ARRAY_BUFFER, tbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(rt.uvs)*4), gl.Pointer(&(rt.uvs[0])), gl.STATIC_DRAW) var tattr uint32 = 2 gl.VertexAttribPointer(tattr, 2, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(tattr) // use ray trace generated texture image. bounds := rt.img.Bounds() width, height := int32(bounds.Dx()), int32(bounds.Dy()) ptr := gl.Pointer(&(rt.img.Pix[0])) gl.GenTextures(1, &rt.texId) gl.BindTexture(gl.TEXTURE_2D, rt.texId) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, ptr) // texture sampling shader. loader := load.NewLoader() shader := "tuv" vsrc, verr := loader.Vsh(shader) fsrc, ferr := loader.Fsh(shader) if verr != nil || ferr != nil { log.Fatalf("Failed to load shaders %s %s\n", verr, ferr) } rt.shaders = gl.CreateProgram() if err := gl.BindProgram(rt.shaders, vsrc, fsrc); err != nil { log.Fatalf("Failed to create program: %s\n", err) } rt.mvpId = gl.GetUniformLocation(rt.shaders, "mvpm") rt.tex2D = gl.GetUniformLocation(rt.shaders, "sampler2D") rt.mvp = render.NewMvp().Set(lin.NewM4().Ortho(0, 4, 0, 4, 0, 10)) // set some state that doesn't need to change during drawing. gl.ClearColor(0.0, 0.0, 0.0, 1.0) }