tofu

Making something with OpenGL in Go
Log | Files | Refs

commit 1dad40f9d7f732011017b4c8f8c6b0e046047574
parent 9cb807c52a03be40dd03fc91695b1efd4684638b
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Thu, 21 Nov 2024 10:23:18 +0900

refactoring

Diffstat:
Mcmd/sample/fragment.glsl | 52+++++++++++++++++++++++++++++-----------------------
Mcmd/sample/main.go | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mcmd/sample/teapot.glsl | 52+++++++++++++++++++++++++++++-----------------------
Mcmd/sample/vertex.glsl | 15+++++++++++----
Mobject.go | 33---------------------------------
Mshader.go | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++--
6 files changed, 240 insertions(+), 135 deletions(-)

diff --git a/cmd/sample/fragment.glsl b/cmd/sample/fragment.glsl @@ -6,17 +6,23 @@ struct Material { float shiness; }; -struct Light { - vec3 pos; - vec3 dir; - vec3 ambient; - vec3 diffuse; - vec3 specular; +struct DirLight { + vec3 Dir; + vec3 Ambient; + vec3 Diffuse; + vec3 Specular; +}; + +struct SpotLight { + vec3 Pos; + vec3 Dir; + vec3 Ambient; + vec3 Diffuse; + vec3 Specular; float Kc, Kl, Kq; - float cutOff, outerCutOff; + float CutOff, OuterCutOff; }; - in vec3 fnormal; in vec3 fpos; in vec2 texPos; @@ -24,8 +30,8 @@ in vec2 texPos; uniform vec3 camPos; uniform vec3 objCol; uniform Material material; -uniform Light sun; -uniform Light light; +uniform DirLight Sun; +uniform SpotLight Light; out vec4 fcol; @@ -41,36 +47,36 @@ void main() { diffuse = vec3(0, 0, 0); specular = vec3(0, 0, 0); - dist = length(light.pos - fpos); - attenuation = 1 / (light.Kc + light.Kl * dist + light.Kq * dist * dist); + dist = length(Light.Pos - fpos); + attenuation = 1 / (Light.Kc + Light.Kl * dist + Light.Kq * dist * dist); - ambient = vec3(texture(material.diffuse, texPos)) * light.ambient; + ambient = vec3(texture(material.diffuse, texPos)) * Light.Ambient; - lightDir = normalize(light.pos - fpos); - theta = dot(lightDir, normalize(-light.dir)); - epsilon = light.cutOff - light.outerCutOff; - intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); + lightDir = normalize(Light.Pos - fpos); + theta = dot(lightDir, normalize(-lightDir)); + epsilon = Light.CutOff - Light.OuterCutOff; + intensity = clamp((theta - Light.OuterCutOff) / epsilon, 0.0, 1.0); diff = max(dot(fnormal, lightDir), 0.0); - diffuse = diff * vec3(texture(material.diffuse, texPos)) * light.diffuse * intensity; + diffuse = diff * vec3(texture(material.diffuse, texPos)) * Light.Diffuse * intensity; viewDir = normalize(camPos - fpos); reflectDir = reflect(-lightDir, normalize(fnormal)); spec = pow(max(dot(viewDir, reflectDir), 0), material.shiness); - specular = vec3(texture(material.specular, texPos)) * spec * light.specular * intensity; + specular = vec3(texture(material.specular, texPos)) * spec * Light.Specular * intensity; fcol += vec4(ambient + diffuse + specular, 0) * attenuation; - ambient = vec3(texture(material.diffuse, texPos)) * sun.ambient; + ambient = vec3(texture(material.diffuse, texPos)) * Sun.Ambient; - lightDir = normalize(-sun.dir); + lightDir = normalize(-Sun.Dir); diff = max(dot(fnormal, lightDir), 0.0); - diffuse = diff * vec3(texture(material.diffuse, texPos)) * sun.diffuse; + diffuse = diff * vec3(texture(material.diffuse, texPos)) * Sun.Diffuse; viewDir = normalize(camPos - fpos); reflectDir = reflect(-lightDir, fnormal); spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shiness); - specular = vec3(texture(material.specular, texPos)) * spec * sun.specular; + specular = vec3(texture(material.specular, texPos)) * spec * Sun.Specular; fcol += vec4(ambient + diffuse + specular, 0); diff --git a/cmd/sample/main.go b/cmd/sample/main.go @@ -75,30 +75,88 @@ func watchCursor(app *App) { } } -type Uniform struct { - Sun +// directional light +type DirLight struct { + Dir tofu.Vec3 + Ambient tofu.Vec3 + Diffuse tofu.Vec3 + Specular tofu.Vec3 } -type Sun struct { - Dir tofu.Vec3 `tofu:"sun.dir"` - Ambient tofu.Vec3 `tofu:"sun.ambient"` - Diffuse tofu.Vec3 `tofu:"sun.diffuse"` - Specular tofu.Vec3 `tofu:"sun.specular"` +type SpotLight struct { + Pos tofu.Vec3 + Dir tofu.Vec3 + Ambient tofu.Vec3 + Diffuse tofu.Vec3 + Specular tofu.Vec3 + Kc, Kl, Kq float32 + CutOff float32 + OuterCutOff float32 } var ( sunCol = tofu.Vec3{1, 1, 1} - sunDir = tofu.Vec3{-0.2, -1.0, -0.3} - sun = Sun{ - Dir: sunDir, + sun = &DirLight{ + Dir: tofu.Vec3{-0.2, -1.0, -0.3}, Ambient: sunCol.MulF(0.5), Diffuse: sunCol.MulF(0.3), Specular: sunCol, } + spotLightCol = tofu.Vec3{1, 1, 1} + spotLight = &SpotLight{ + Ambient: spotLightCol.MulF(0.5), + Diffuse: spotLightCol.MulF(0.3), + Specular: spotLightCol, + CutOff: float32(math.Cos(25 * math.Pi / 180)), + OuterCutOff: float32(math.Cos(35 * math.Pi / 180)), + Kc: 1.0, + Kl: 0.0009, + Kq: 0.00032, + } ) var teapot *tofu.Object -var object = &tofu.Object{ +var cube = &tofu.Object{ + Vertices: []tofu.Vec3{ + tofu.Vec3{0.5, 0.5, -0.5}, + tofu.Vec3{0.5, -0.5, -0.5}, + tofu.Vec3{-0.5, -0.5, -0.5}, + tofu.Vec3{-0.5, 0.5, -0.5}, + tofu.Vec3{0.5, 0.5, 0.5}, + tofu.Vec3{0.5, -0.5, 0.5}, + tofu.Vec3{-0.5, -0.5, 0.5}, + tofu.Vec3{-0.5, 0.5, 0.5}, + }, + Normals: []tofu.Vec3{ + {0.0, 0.0, -1.0}, + {1.0, 0.0, 0.0}, + {0.0, -1.0, 0.0}, + {-1.0, 0.0, 0.0}, + {0.0, 1.0, 0.0}, + {0.0, 0.0, 1.0}, + }, + TexCoords: []tofu.Vec2{ + tofu.Vec2{1.0, 1.0}, + tofu.Vec2{1.0, 0.0}, + tofu.Vec2{0.0, 0.0}, + tofu.Vec2{0.0, 1.0}, + }, + Faces: []tofu.Face{ + {{0, 0, 0}, {1, 0, 1}, {2, 0, 2}}, + {{0, 0, 0}, {2, 0, 2}, {3, 0, 3}}, + {{0, 1, 0}, {1, 1, 1}, {5, 1, 2}}, + {{0, 1, 0}, {5, 1, 2}, {4, 1, 3}}, + {{1, 2, 0}, {2, 2, 1}, {6, 2, 2}}, + {{1, 2, 0}, {6, 2, 2}, {5, 2, 3}}, + {{2, 3, 0}, {3, 3, 1}, {7, 3, 2}}, + {{2, 3, 0}, {7, 3, 2}, {6, 3, 3}}, + {{3, 4, 0}, {0, 4, 1}, {4, 4, 2}}, + {{3, 4, 0}, {4, 4, 2}, {7, 4, 3}}, + {{4, 5, 0}, {5, 5, 1}, {6, 5, 2}}, + {{4, 5, 0}, {6, 5, 2}, {7, 5, 3}}, + }, +} +var light = &tofu.Object{ Vertices: []tofu.Vec3{ tofu.Vec3{0.5, 0.5, -0.5}, tofu.Vec3{0.5, -0.5, -0.5}, @@ -137,9 +195,15 @@ var object = &tofu.Object{ {{4, 5, 0}, {5, 5, 1}, {6, 5, 2}}, {{4, 5, 0}, {6, 5, 2}, {7, 5, 3}}, }, - Uniforms: Uniform{Sun: sun}, } +type Trans struct { + Projection, View, Model tofu.Mat4 +} + +var teapotTrans = new(Trans) +var cubeTrans = new(Trans) + var cubePositions = []tofu.Vec3{ {4.24, 0.94, -1.94}, {0.79, 2.40, 2.87}, @@ -179,62 +243,46 @@ func (app *App) Update() error { view := camera.View() projection := tofu.Perspective(80*math.Pi/180, 800/600, 0.1, 100) - lightCol := tofu.Vec3{1, 1, 1} lightModel := tofu.Rotate(now, tofu.Vec3{0, 1, 0}). Mul(tofu.Translate(tofu.Vec3{5, 4.5, 0})). Mul(tofu.Scale(0.3)) - lightPos := tofu.Rotate3(now, tofu.Vec3{0, 1, 0}). + spotLight.Pos = tofu.Rotate3(now, tofu.Vec3{0, 1, 0}). MulV(tofu.Vec3{5, 4.5, 0}) + spotLight.Dir = tofu.Vec3{0, 0, 0}.Sub(spotLight.Pos) app.program.Use() - app.program.SetMat4("view", view) - app.program.SetMat4("projection", projection) + cubeTrans.View = view + cubeTrans.Projection = projection app.program.SetVec3("camPos", camera.Pos) app.program.SetTexture("material.diffuse", app.texture) app.program.SetTexture("material.specular", app.specularMap) app.program.SetFloat32("material.shiness", 32) - app.program.SetVec3("light.pos", lightPos) - app.program.SetVec3("light.dir", tofu.Vec3{0, 0, 0}.Sub(lightPos)) - app.program.SetFloat32("light.cutOff", float32(math.Cos(25*math.Pi/180))) - app.program.SetFloat32("light.outerCutOff", float32(math.Cos(35*math.Pi/180))) - app.program.SetVec3("light.ambient", lightCol.MulF(0.1)) - app.program.SetVec3("light.diffuse", lightCol.MulF(0.9)) - app.program.SetVec3("light.specular", lightCol) - app.program.SetFloat32("light.Kc", 1.0) - app.program.SetFloat32("light.Kl", 0.0009) - app.program.SetFloat32("light.Kq", 0.00032) for _, p := range cubePositions { model := tofu.Translate(p.Inverse()) - // Mul(tofu.Rotate(20*float32(i)+now, tofu.Vec3{math.Sqrt2 / 2, -math.Sqrt2 / 2, 0})) - app.program.SetMat4("model", model) - object.Draw(app.program) + cubeTrans.Model = model + app.program.SetUniforms() + cube.Draw(app.program) } app.teapotProgram.Use() - app.teapotProgram.SetMat4("view", view) - app.teapotProgram.SetMat4("projection", projection) - app.teapotProgram.SetMat4("model", tofu.Translate(tofu.Vec3{-5, -5, 0}).Mul(tofu.Scale(0.06))) + teapotTrans.View = view + teapotTrans.Projection = projection + teapotTrans.Model = tofu.Translate(tofu.Vec3{0, 0, 0}). + Mul(tofu.Rotate(now, tofu.Vec3{0, 1, 0})). + Mul(tofu.Scale(0.06)) app.teapotProgram.SetVec3("camPos", camera.Pos) app.teapotProgram.SetVec3("material.diffuse", tofu.Vec3{0.3, 0.4, 0.4}) app.teapotProgram.SetVec3("material.specular", tofu.Vec3{0.3, 0.4, 0.4}) app.teapotProgram.SetFloat32("material.shiness", 32) - app.teapotProgram.SetVec3("light.pos", lightPos) - app.teapotProgram.SetVec3("light.dir", tofu.Vec3{0, 0, 0}.Sub(lightPos)) - app.teapotProgram.SetFloat32("light.outerCutOff", float32(math.Cos(35*math.Pi/180))) - app.teapotProgram.SetFloat32("light.cutOff", float32(math.Cos(25*math.Pi/180))) - app.teapotProgram.SetVec3("light.ambient", lightCol.MulF(0.9)) - app.teapotProgram.SetVec3("light.diffuse", lightCol.MulF(0.1)) - app.teapotProgram.SetVec3("light.specular", lightCol) - app.teapotProgram.SetFloat32("light.Kc", 1.0) - app.teapotProgram.SetFloat32("light.Kl", 0.0009) - app.teapotProgram.SetFloat32("light.Kq", 0.00032) + app.teapotProgram.SetUniforms() teapot.Draw(app.teapotProgram) app.lightProgram.Use() - app.program.SetMat4("view", view) - app.program.SetMat4("projection", projection) - app.program.SetMat4("model", lightModel) - app.lightProgram.SetVec3("lightCol", lightCol) - object.Draw(app.lightProgram) + app.lightProgram.SetMat4("Trans.View", view) + app.lightProgram.SetMat4("Trans.Projection", projection) + app.lightProgram.SetMat4("Trans.Model", lightModel) + app.lightProgram.SetVec3("lightCol", spotLight.Specular) + app.lightProgram.SetUniforms() + light.Draw(app.lightProgram) return nil } @@ -253,7 +301,8 @@ func main() { tofu.SetWindowTitle("Sample") tofu.Init() - object.Load() + cube.Load() + light.Load() _, f, _, ok := runtime.Caller(0) if !ok { @@ -271,14 +320,22 @@ func main() { if err != nil { log.Fatal(err) } - // teapot.SetNormals() - teapot.Uniforms = sun + teapot.SetNormals() teapot.Load() app.program, err = tofu.NewProgram(vpath, fpath) if err != nil { log.Fatalf("create shader program: %v", err) } + app.program.Uniforms = struct { + Sun *DirLight + Light *SpotLight + Trans *Trans + }{ + sun, + spotLight, + cubeTrans, + } app.lightProgram, err = tofu.NewProgram(vpath, lightFpath) if err != nil { log.Fatalf("create light shader program: %v", err) @@ -287,6 +344,15 @@ func main() { if err != nil { log.Fatalf("create teapot shader program: %v", err) } + app.teapotProgram.Uniforms = struct { + Sun *DirLight + Light *SpotLight + Trans *Trans + }{ + Sun: sun, + Light: spotLight, + Trans: teapotTrans, + } app.texture, err = tofu.NewTexture(texturePath) if err != nil { log.Fatalf("NewTesture: %v", err) diff --git a/cmd/sample/teapot.glsl b/cmd/sample/teapot.glsl @@ -6,17 +6,23 @@ struct Material { float shiness; }; -struct Light { - vec3 pos; - vec3 dir; - vec3 ambient; - vec3 diffuse; - vec3 specular; +struct DirLight { + vec3 Dir; + vec3 Ambient; + vec3 Diffuse; + vec3 Specular; +}; + +struct SpotLight { + vec3 Pos; + vec3 Dir; + vec3 Ambient; + vec3 Diffuse; + vec3 Specular; float Kc, Kl, Kq; - float cutOff, outerCutOff; + float CutOff, OuterCutOff; }; - in vec3 fnormal; in vec3 fpos; in vec2 texPos; @@ -24,8 +30,8 @@ in vec2 texPos; uniform vec3 camPos; uniform vec3 objCol; uniform Material material; -uniform Light sun; -uniform Light light; +uniform DirLight Sun; +uniform SpotLight Light; out vec4 fcol; @@ -41,36 +47,36 @@ void main() { diffuse = vec3(0, 0, 0); specular = vec3(0, 0, 0); - dist = length(light.pos - fpos); - attenuation = 1 / (light.Kc + light.Kl * dist + light.Kq * dist * dist); + dist = length(Light.Pos - fpos); + attenuation = 1 / (Light.Kc + Light.Kl * dist + Light.Kq * dist * dist); - ambient = material.diffuse * light.ambient; + ambient = material.diffuse * Light.Ambient; - lightDir = normalize(light.pos - fpos); - theta = dot(lightDir, normalize(-light.dir)); - epsilon = light.cutOff - light.outerCutOff; - intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); + lightDir = normalize(Light.Pos - fpos); + theta = dot(lightDir, normalize(-Light.Dir)); + epsilon = Light.CutOff - Light.OuterCutOff; + intensity = clamp((theta - Light.OuterCutOff) / epsilon, 0.0, 1.0); diff = max(dot(fnormal, lightDir), 0.0); - diffuse = diff * material.diffuse * light.diffuse * intensity; + diffuse = diff * material.diffuse * Light.Diffuse * intensity; viewDir = normalize(camPos - fpos); reflectDir = reflect(-lightDir, fnormal); spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shiness); - specular = material.specular * spec * light.specular * intensity; + specular = material.specular * spec * Light.Specular * intensity; fcol += vec4((ambient+diffuse+specular) * attenuation, 0); - ambient = material.diffuse * sun.ambient; + ambient = material.diffuse * Sun.Ambient; - lightDir = normalize(-sun.dir); + lightDir = normalize(-Sun.Dir); diff = max(dot(fnormal, lightDir), 0.0); - diffuse = diff * material.diffuse * sun.diffuse; + diffuse = diff * material.diffuse * Sun.Diffuse; viewDir = normalize(camPos - fpos); reflectDir = reflect(-lightDir, fnormal); spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shiness); - specular = material.specular * spec * sun.specular; + specular = material.specular * spec * Sun.Specular; fcol += vec4(ambient + diffuse + specular, 0); } diff --git a/cmd/sample/vertex.glsl b/cmd/sample/vertex.glsl @@ -3,14 +3,21 @@ layout (location = 0) in vec3 pos; layout (location = 1) in vec3 col; layout (location = 2) in vec2 vTexPos; layout (location = 3) in vec3 normal; -uniform mat4 projection, view, model; + +struct trans { + mat4 Projection; + mat4 View; + mat4 Model; +}; + +uniform trans Trans; out vec3 fnormal; out vec3 fpos; out vec2 texPos; void main() { - gl_Position = projection * view * model * vec4(pos, 1.0); - fnormal = normalize(mat3(transpose(inverse(model))) * normal); - fpos = vec3(model * vec4(pos, 1.0)); + gl_Position = Trans.Projection * Trans.View * Trans.Model * vec4(pos, 1.0); + fnormal = normalize(mat3(transpose(inverse(Trans.Model))) * normal); + fpos = vec3(Trans.Model * vec4(pos, 1.0)); texPos = vTexPos; } diff --git a/object.go b/object.go @@ -6,7 +6,6 @@ import ( "log" "math" "os" - "reflect" "strconv" "strings" @@ -18,7 +17,6 @@ type Object struct { TexCoords []Vec2 Normals []Vec3 Faces []Face - Uniforms interface{} vao *VAO } @@ -239,40 +237,9 @@ func (obj *Object) Load() { obj.vao = newVAO(obj) } -func (obj *Object) setUniforms(prog *Program) { - if obj.Uniforms == nil { - return - } - setUniforms(obj.Uniforms, prog) -} - -func setUniforms(uniforms interface{}, prog *Program) { - s := reflect.ValueOf(uniforms) - fields := reflect.VisibleFields(s.Type()) - for i := 0; i < s.NumField(); i++ { - fv := s.Field(i) - ft := fields[i] - if fv.Kind() == reflect.Struct { - setUniforms(fv.Interface(), prog) - continue - } - tag, ok := ft.Tag.Lookup("tofu") - if !ok { - continue - } - switch ft.Type.String() { - case "tofu.Vec3": - log.Println(prog.SetVec3(tag, fv.Interface().(Vec3))) - default: - log.Printf("unknown uniform type: %s", ft.Type) - } - } -} - func (obj *Object) Draw(prog *Program) { // TODO: performance? obj.vao.bind() - obj.setUniforms(prog) gl.DrawElements(gl.TRIANGLES, int32(obj.stride()*3*len(obj.Faces)), gl.UNSIGNED_INT, nil) diff --git a/shader.go b/shader.go @@ -3,6 +3,7 @@ package tofu import ( "fmt" "os" + "reflect" "strings" "github.com/go-gl/gl/v3.3-core/gl" @@ -58,7 +59,8 @@ func linkShaders(vsID, fsID uint32) (shaderID uint32, err error) { } type Program struct { - id uint32 + id uint32 + Uniforms any } // NewProgram creates a shader program from a vertex shader source file @@ -87,8 +89,59 @@ func (p *Program) Use() { gl.UseProgram(p.id) } +func (p *Program) SetUniforms() error { + return p.setUniforms(p.Uniforms, "") +} + +func (p *Program) setUniforms(uniforms any, prefix string) error { + if uniforms == nil { + return nil + } + s := reflect.ValueOf(uniforms) + fields := reflect.VisibleFields(s.Type()) + for i := 0; i < s.NumField(); i++ { + fv := s.Field(i) + ft := fields[i] + tag, ok := ft.Tag.Lookup("tofu") + if !ok { + tag = ft.Name + } + if prefix != "" { + tag = prefix + "." + tag + } + switch fv.Kind() { + case reflect.Struct: + if err := p.setUniforms(fv.Interface(), tag); err != nil { + return err + } + continue + case reflect.Pointer: + if err := p.setUniforms(fv.Elem().Interface(), tag); err != nil { + return err + } + continue + } + switch ft.Type.String() { + case "tofu.Mat4": + if err := p.SetMat4(tag, fv.Interface().(Mat4)); err != nil { + return err + } + case "tofu.Vec3": + if err := p.SetVec3(tag, fv.Interface().(Vec3)); err != nil { + return err + } + case "float32": + if err := p.SetFloat32(tag, fv.Interface().(float32)); err != nil { + return err + } + default: + return fmt.Errorf("unknown uniform type: %s", ft.Type) + } + } + return nil +} + // SetFloat32 set the value of the uniform name. -// TODO: Should I check error conditions? func (p *Program) SetFloat32(name string, val float32) error { l := gl.GetUniformLocation(p.id, gl.Str(name+"\x00")) if l == -1 {