tofu

Making something with OpenGL in Go
Log | Files | Refs

commit f6639e8a819091d515edf063f94ab061991e280b
parent 751fda512bd75b6b5e4f7fe8498e9d05270365b3
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Wed, 23 Oct 2024 08:48:43 +0900

encapsulate VBO, VAO, EBO

Diffstat:
Acmd/sample/awesomeface.png | 0
Acmd/sample/container.jpg | 0
Mcmd/sample/fragment.glsl | 5+++--
Mcmd/sample/main.go | 77+++++++++++++++++++++++++++++++++++++----------------------------------------
Mcmd/sample/vertex.glsl | 8+++++---
Ddraw.go | 19-------------------
Aobject.go | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atexture.go | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 210 insertions(+), 64 deletions(-)

diff --git a/cmd/sample/awesomeface.png b/cmd/sample/awesomeface.png Binary files differ. diff --git a/cmd/sample/container.jpg b/cmd/sample/container.jpg Binary files differ. diff --git a/cmd/sample/fragment.glsl b/cmd/sample/fragment.glsl @@ -1,8 +1,9 @@ - #version 330 core in vec3 vcol; +in vec2 texCoord; +uniform sampler2D tex; out vec4 fcol; void main() { - fcol = vec4(vcol, 1); + fcol = texture(tex, texCoord); } diff --git a/cmd/sample/main.go b/cmd/sample/main.go @@ -1,6 +1,7 @@ package main import ( + "image/color" "log" "math" "path/filepath" @@ -21,7 +22,7 @@ func init() { } func framebufferSizeCallback(w *glfw.Window, width int, height int) { - if (width > math.MaxInt32 || height > math.MaxInt32) { + if width > math.MaxInt32 || height > math.MaxInt32 { log.Fatal("framebufferSizeCallback: integer overflow") } gl.Viewport(0, 0, int32(width), int32(height)) @@ -29,19 +30,33 @@ func framebufferSizeCallback(w *glfw.Window, width int, height int) { func processInput(w *glfw.Window) { if w.GetKey(glfw.KeyQ) == glfw.Press { - w.SetShouldClose(true); + w.SetShouldClose(true) } } -var vertices = []float32{ - // positions, colors - -0.5, -0.5, 0.0, 1, 0, 0, - 0.5, -0.5, 0.0, 0, 1, 0, - 0.0, 0.5, 0.0, 0, 0, 1, -} - -var indices = []uint32{ - 0, 1, 2, +var object = draw.Object{ + Vertices: []draw.Point3D{ + draw.Point3D{0.5, 0.5, 0.0}, + draw.Point3D{0.5, -0.5, 0.0}, + draw.Point3D{-0.5, -0.5, 0.0}, + draw.Point3D{-0.5, 0.5, 0.0}, + }, + Colors: []color.Color{ + color.RGBA{255, 0, 0, 255}, + color.RGBA{0, 255, 0, 255}, + color.RGBA{0, 0, 255, 255}, + color.RGBA{255, 255, 0, 255}, + }, + TexCoords: []draw.Point2D{ + draw.Point2D{1.0, 1.0}, + draw.Point2D{1.0, 0.0}, + draw.Point2D{0.0, 0.0}, + draw.Point2D{0.0, 1.0}, + }, + Faces: [][3]uint32{ + {0, 1, 2}, + {0, 2, 3}, + }, } func main() { @@ -70,48 +85,30 @@ func main() { } vpath := filepath.Join(filepath.Dir(f), "vertex.glsl") fpath := filepath.Join(filepath.Dir(f), "fragment.glsl") + texpath := filepath.Join(filepath.Dir(f), "container.jpg") + + object.Load() shader, err := draw.NewShader(vpath, fpath) if err != nil { log.Fatalf("create shader: %v", err) } - var VBO, VAO, EBO uint32 - gl.GenVertexArrays(1, &VAO) - gl.GenBuffers(1, &VBO) - gl.GenBuffers(1, &EBO) - gl.BindVertexArray(VAO) - gl.BindBuffer(gl.ARRAY_BUFFER, VBO) - gl.BufferData(gl.ARRAY_BUFFER, len(vertices) * 4, - gl.Ptr(vertices), gl.STATIC_DRAW) - gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, EBO) - gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices) * 4, - gl.Ptr(indices), gl.STATIC_DRAW) - gl.VertexAttribPointerWithOffset(0, 3, gl.FLOAT, false, 6*4, 0) - gl.EnableVertexAttribArray(0) - gl.VertexAttribPointerWithOffset(1, 3, gl.FLOAT, false, 6*4, 3*4) - gl.EnableVertexAttribArray(1) - gl.BindBuffer(gl.ARRAY_BUFFER, 0); - gl.BindVertexArray(0) + texture, err := draw.NewTexture(texpath) + if err != nil { + log.Fatalf("create texture: %v") + } + _ = texture + + shader.Use() window.SetFramebufferSizeCallback(framebufferSizeCallback) - shader.Use() for !window.ShouldClose() { processInput(window) gl.ClearColor(0.2, 0.3, 0.3, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT) - gl.BindVertexArray(VAO) - t := glfw.GetTime() - px := math.Sin(t) - py := math.Cos(t) - if err := shader.SetFloat32("px", float32(px)); err != nil { - log.Fatal(err) - } - if err := shader.SetFloat32("py", float32(py)); err != nil { - log.Fatal(err) - } - gl.DrawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, nil) + gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil) window.SwapBuffers() glfw.PollEvents() } diff --git a/cmd/sample/vertex.glsl b/cmd/sample/vertex.glsl @@ -1,10 +1,12 @@ #version 330 core layout (location = 0) in vec3 pos; layout (location = 1) in vec3 col; +layout (location = 2) in vec2 vtexCoord; out vec3 vcol; -uniform float px, py; +out vec2 texCoord; void main() { - gl_Position = vec4(pos.x + px, pos.y + py, pos.z, 1.0); - vcol = gl_Position.xyz; + gl_Position = vec4(pos, 1.0); + vcol = col; + texCoord = vtexCoord; } diff --git a/draw.go b/draw.go @@ -1,19 +0,0 @@ -package draw - -type Point struct { - X, Y int -} - -type Rectangle struct { - Min, Max Point -} - -type Display struct { - -} - -type Image struct { - Display *Display - R Rectangle - Clipr Rectangle -} diff --git a/object.go b/object.go @@ -0,0 +1,113 @@ +package draw + +import ( + "image/color" + + "github.com/go-gl/gl/v3.3-core/gl" +) + +type Point2D struct { + X, Y float32 +} + +type Point3D struct { + X, Y, Z float32 +} + +type Object struct { + Vertices []Point3D + Colors []color.Color + TexCoords []Point2D + Faces [][3]uint32 + vao *VAO +} + +func (obj Object) data() []float32 { + data := make([]float32, 8 * len(obj.Vertices)) + for i := 0; i < len(obj.Vertices); i++ { + data[8 * i] = obj.Vertices[i].X + data[8 * i + 1] = obj.Vertices[i].Y + data[8 * i + 2] = obj.Vertices[i].Z + r, g, b, _ := obj.Colors[i].RGBA() + data[8 * i + 3] = float32(r)/0xffff + data[8 * i + 4] = float32(g)/0xffff + data[8 * i + 5] = float32(b)/0xffff + data[8 * i + 6] = obj.TexCoords[i].X + data[8 * i + 7] = obj.TexCoords[i].Y + } + return data +} + +func (obj Object) faceData() []uint32 { + fdata := make([]uint32, 3 * len(obj.Faces)) + for i, v := range obj.Faces { + fdata[3 * i] = v[0] + fdata[3 * i + 1] = v[1] + fdata[3 * i + 2] = v[2] + } + return fdata +} + +// Load copies object data into the GPU. +func (obj *Object) Load() { + obj.vao = newVAO(obj) +} + +type VAO struct { + id uint32 + vbo *Buffer + ebo *Buffer +} + +const ( + UniformVertex = iota + UniformColor + UniformTexCoords +) + +func newVAO(obj *Object) *VAO { + var id uint32 + gl.GenVertexArrays(1, &id) + vao := &VAO{id: id, vbo: newBuffer(), ebo: newBuffer()} + vao.bind() + data := obj.data() + vao.setData(data) + vao.setAttribute(UniformVertex, 3, 8, 0) + vao.setAttribute(UniformColor, 3, 8, 3) + vao.setAttribute(UniformTexCoords, 2, 8, 6) + fdata := obj.faceData() + vao.setFaces(fdata) + return vao +} + +func (vao *VAO) bind() { + gl.BindVertexArray(vao.id) +} + +func (vao *VAO) setAttribute(uniformNum uint32, size int, stride int, offset int) { + gl.VertexAttribPointerWithOffset(uniformNum, int32(size), gl.FLOAT, + false, int32(stride) * 4, uintptr(offset) * 4) + gl.EnableVertexAttribArray(uniformNum) +} + +func (vao *VAO) setData(data []float32) { + gl.BindBuffer(gl.ARRAY_BUFFER, vao.vbo.id) + gl.BufferData(gl.ARRAY_BUFFER, len(data) * 4, + gl.Ptr(data), gl.STATIC_DRAW) +} + +func (vao *VAO) setFaces(data []uint32) { + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, vao.ebo.id) + gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(data) * 4, + gl.Ptr(data), gl.STATIC_DRAW) +} + +type Buffer struct { + id uint32 +} + +func newBuffer() *Buffer { + var id uint32 + gl.GenBuffers(1, &id) + return &Buffer{id: id} +} diff --git a/texture.go b/texture.go @@ -0,0 +1,52 @@ +package draw + +import ( + "fmt" + "image" + idraw "image/draw" + _ "image/jpeg" + "os" + + "github.com/go-gl/gl/v3.3-core/gl" +) + +type Texture struct { + id uint32 +} + +func NewTexture(name string) (*Texture, error) { + var id uint32 + gl.GenTextures(1, &id) + gl.BindTexture(gl.TEXTURE_2D, id) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + img, err := loadImage(name) + if err != nil { + return nil, fmt.Errorf("load texture: %v", err) + } + rgba := image.NewRGBA(img.Bounds()) + idraw.Draw(rgba, rgba.Bounds(), img, image.ZP, idraw.Src) + if rgba.Stride != rgba.Rect.Dx()*4 { + return nil, fmt.Errorf("data should be packed dense") + } + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(rgba.Rect.Dx()), int32(rgba.Rect.Dy()), 0, + gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba.Pix)) + gl.GenerateMipmap(gl.TEXTURE_2D) + return &Texture{id: id}, nil +} + + +func loadImage(name string) (image.Image, error) { + f, err := os.Open(name) + if err != nil { + return nil, fmt.Errorf("open: %v", err) + } + defer f.Close() + img, _, err := image.Decode(f) + if err != nil { + return nil, fmt.Errorf("decode image %s: %v", name, err) + } + return img, nil +}