// Renderer implementation. // BindTexture makes the texture available on the GPU. func (gc *opengl) BindTexture(tid *uint32, img image.Image, repeat bool) (err error) { if glerr := gl.GetError(); glerr != gl.NO_ERROR { log.Printf("opengl:bindTexture need to find and fix prior error %X", glerr) } if *tid == 0 { gl.GenTextures(1, tid) } gl.BindTexture(gl.TEXTURE_2D, *tid) // FUTURE: check if RGBA, or NRGBA are alpha pre-multiplied. The docs say yes // for RGBA but the data is from PNG files which are not pre-multiplied // and the go png Decode looks like its reading values directly. var ptr gl.Pointer bounds := img.Bounds() width, height := int32(bounds.Dx()), int32(bounds.Dy()) switch imgType := img.(type) { case *image.RGBA: i := img.(*image.RGBA) ptr = gl.Pointer(&(i.Pix[0])) case *image.NRGBA: i := img.(*image.NRGBA) ptr = gl.Pointer(&(i.Pix[0])) default: return fmt.Errorf("Unsupported image format %T", imgType) } gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, ptr) gl.GenerateMipmap(gl.TEXTURE_2D) gc.setTextureMode(*tid, repeat) if glerr := gl.GetError(); glerr != gl.NO_ERROR { err = fmt.Errorf("Failed binding texture %d\n", glerr) } return err }
// 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 }
// 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) }
// bindFaceBuffer copies triangle face data from the CPU to the GPU. func (gc *opengl) bindFaceBuffer(fdata Data) { fd := fdata.(*faceData) if len(fd.data) > 0 { if fd.ref == 0 { gl.GenBuffers(1, &fd.ref) } gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, fd.ref) bytes := 2 // 2 bytes for uint16 (gl.UNSIGNED_SHORT) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int64(len(fd.data)*bytes), gl.Pointer(&(fd.data[0])), fd.usage) } }
// bindVertexBuffer copies per-vertex data from the CPU to the GPU. func (gc *opengl) bindVertexBuffer(vdata Data) { vd, ok := vdata.(*vertexData) if !ok { return } if vd.ref == 0 { gl.GenBuffers(1, &vd.ref) } bytes := 4 // 4 bytes for float32 (gl.FLOAT) switch vd.usage { case STATIC: switch { case len(vd.floats) > 0: gl.BindBuffer(gl.ARRAY_BUFFER, vd.ref) gl.BufferData(gl.ARRAY_BUFFER, int64(len(vd.floats)*bytes), gl.Pointer(&(vd.floats[0])), vd.usage) gl.VertexAttribPointer(vd.lloc, vd.span, gl.FLOAT, false, 0, 0) case len(vd.bytes) > 0: gl.BindBuffer(gl.ARRAY_BUFFER, vd.ref) gl.BufferData(gl.ARRAY_BUFFER, int64(len(vd.bytes)), gl.Pointer(&(vd.bytes[0])), vd.usage) gl.VertexAttribPointer(vd.lloc, vd.span, gl.UNSIGNED_BYTE, vd.normalize, 0, 0) } case DYNAMIC: var null gl.Pointer // zero. switch { case len(vd.floats) > 0: gl.BindBuffer(gl.ARRAY_BUFFER, vd.ref) // Buffer orphaning, a common way to improve streaming perf. See: // http://www.opengl.org/wiki/Buffer_Object_Streaming gl.BufferData(gl.ARRAY_BUFFER, int64(cap(vd.floats)*bytes), null, vd.usage) gl.BufferSubData(gl.ARRAY_BUFFER, 0, int64(len(vd.floats)*bytes), gl.Pointer(&(vd.floats[0]))) gl.VertexAttribPointer(vd.lloc, vd.span, gl.FLOAT, false, 0, 0) } } gl.EnableVertexAttribArray(vd.lloc) }
// 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) }