commit 1dad40f9d7f732011017b4c8f8c6b0e046047574
parent 9cb807c52a03be40dd03fc91695b1efd4684638b
Author: Matsuda Kenji <info@mtkn.jp>
Date: Thu, 21 Nov 2024 10:23:18 +0900
refactoring
Diffstat:
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 {