commit f6639e8a819091d515edf063f94ab061991e280b
parent 751fda512bd75b6b5e4f7fe8498e9d05270365b3
Author: Matsuda Kenji <info@mtkn.jp>
Date: Wed, 23 Oct 2024 08:48:43 +0900
encapsulate VBO, VAO, EBO
Diffstat:
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
+}