Add material and lighting.

main
Sean Hickey 2021-09-05 02:24:18 -07:00
parent ebb429819a
commit 0098f283f2
15 changed files with 276 additions and 121 deletions

14
main.go
View File

@ -66,6 +66,9 @@ func run() {
}
defer gameWindow.Destroy()
sunLight := gl_objects.NewDirLight(mgl32.Vec3{1, -1, -1})
sunLight.GLInit(gameWindow.GLProgram)
camera := gl_objects.NewCamera(gameWindow.GLProgram)
camera.Position = mgl32.Vec3{0, 0, 30}
gameWindow.SetCamera(camera)
@ -78,18 +81,25 @@ func run() {
// TODO bounding box
// Brick Targets
targets := breakout.NewTargets(5, 4, mgl32.Vec3{-10, 4, 0})
targets.GLInit(gameWindow.GLProgram)
for _, brick := range targets.Bricks {
gameWindow.AddObject(brick)
}
paddle := gl_objects.NewBox(6.0, 1.0, 4.0, mgl32.Vec3{0, -10, 0}, mgl32.Vec3{0, 1, 0})
// Paddle
paddleMaterial := gl_objects.NewMaterial()
paddleMaterial.Color = mgl32.Vec3{0, 1, 0}
paddle := gl_objects.NewBox(6.0, 1.0, 4.0, mgl32.Vec3{0, -10, 0}, paddleMaterial)
paddle.GLInit(gameWindow.GLProgram)
gameWindow.AddObject(paddle)
gameWindow.SetPaddle(paddle)
ball := gl_objects.NewBox(1, 1, 1, mgl32.Vec3{-8, -2, 0}, mgl32.Vec3{1, 1, 1})
// Ball
ballMaterial := gl_objects.NewMaterial()
ballMaterial.Color = mgl32.Vec3{1, 1, 1}
ball := gl_objects.NewBox(1, 1, 1, mgl32.Vec3{-8, -2, 0}, ballMaterial)
ball.GLInit(gameWindow.GLProgram)
gameWindow.AddObject(ball)

View File

@ -11,9 +11,15 @@ type Brick struct {
broken bool
}
func NewBrick(brickWidth, brickHeight, brickDepth float32, position, color mgl32.Vec3) *Brick {
func NewBrick(
brickWidth,
brickHeight,
brickDepth float32,
position mgl32.Vec3,
material *gl_objects.Material) *Brick {
brick := Brick{
Box: gl_objects.NewBox(brickWidth, brickHeight, brickDepth, position, color),
Box: gl_objects.NewBox(brickWidth, brickHeight, brickDepth, position, material),
broken: false,
}
return &brick

View File

@ -2,6 +2,8 @@ package breakout
import (
"github.com/go-gl/mathgl/mgl32"
"gitea.wisellama.rocks/carpy-breakout/pkg/gl_objects"
)
type Targets struct {
@ -21,11 +23,14 @@ func NewTargets(numRows, numColumns int, lowerLeftPos mgl32.Vec3) *Targets {
var brickHeight float32 = 1.0
var brickDepth float32 = 4.0
colors := make([]mgl32.Vec3, numColumns)
materials := make([]*gl_objects.Material, numColumns)
for i := 0; i < numColumns; i++ {
red := 0 + float32(i)/float32(numColumns)
blue := 1 - float32(i)/float32(numColumns)
colors[i] = mgl32.Vec3{red, 0, blue}
m := gl_objects.NewMaterial()
m.Color = mgl32.Vec3{red, 0, blue}
materials[i] = m
}
n := 0
@ -36,7 +41,7 @@ func NewTargets(numRows, numColumns int, lowerLeftPos mgl32.Vec3) *Targets {
float32(j) + float32(j)*brickWidth,
float32(i) + float32(i)*brickHeight,
0})
targets.Bricks[n] = NewBrick(brickWidth, brickHeight, brickDepth, position, colors[i])
targets.Bricks[n] = NewBrick(brickWidth, brickHeight, brickDepth, position, materials[i])
n++
}
}

View File

@ -95,8 +95,8 @@ func (w *GameWindow) IsFreelookOn() bool {
func (w *GameWindow) WindowProjection() {
width, height := w.SDLWindow.GLGetDrawableSize()
projection := mgl32.Perspective(mgl32.DegToRad(45.0), float32(width)/float32(height), 0.01, 1000.0)
projectionUniform := gl.GetUniformLocation(w.GLProgram, gl.Str("projection\x00"))
gl.UniformMatrix4fv(projectionUniform, 1, false, &projection[0])
gl_helpers.SetUniformMatrix4f(w.GLProgram, "projection", &projection)
gl.Viewport(0, 0, width, height)
}

View File

@ -0,0 +1,81 @@
package gl_helpers
var FragmentShaderSource = `
#version 330
in vec3 fragPos;
in vec3 fragNormal;
in vec2 fragTexCoord;
out vec4 outputColor;
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
vec3 color;
int textureOn; // if textureOn == 0, then just color is used
};
struct DirLight {
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform DirLight dirLight;
uniform Material material;
uniform vec3 cameraPos;
vec3 GetTextureDiffuse();
vec3 GetTextureSpecular();
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
void main() {
vec3 normal = normalize(fragNormal);
vec3 viewDir = normalize(fragPos - cameraPos);
vec3 result = CalcDirLight(dirLight, normal, viewDir);
outputColor = vec4(result, 1);
}
vec3 GetTextureDiffuse() {
vec3 t = material.color;
if (material.textureOn != 0) {
t = t * vec3(texture(material.diffuse, fragTexCoord));
}
return t;
}
vec3 GetTextureSpecular() {
vec3 t = material.color;
if (material.textureOn != 0) {
t = t * vec3(texture(material.specular, fragTexCoord));
}
return t;
}
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(light.direction);
// Diffuse
float diffuse = max(dot(normal, -lightDir), 0.0);
// Specular
vec3 reflectDir = reflect(-lightDir, normal);
float specular = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 tDiffuse = GetTextureDiffuse();
vec3 tSpecular = GetTextureSpecular();
vec3 a = light.ambient * tDiffuse;
vec3 d = light.diffuse * diffuse * tDiffuse;
vec3 s = light.specular * specular * tSpecular;
return (a + d + s);
}
` + "\x00"

View File

@ -117,29 +117,47 @@ func NewTexture(file string) (uint32, error) {
return texture, nil
}
func GLInitVertexAttribs(glProgram uint32, vertexSize int32, textureOn bool) {
func UpdateVertexAttribs(glProgram uint32, vertexSize int32) {
var sizeofFloat int32 = 4
stride := vertexSize * sizeofFloat
vertAttrib := uint32(gl.GetAttribLocation(glProgram, gl.Str("vert\x00")))
gl.EnableVertexAttribArray(vertAttrib)
gl.VertexAttribPointerWithOffset(vertAttrib, 3, gl.FLOAT, false, stride, uintptr(0))
vertPos := uint32(gl.GetAttribLocation(glProgram, gl.Str("vertPos\x00")))
gl.EnableVertexAttribArray(vertPos)
gl.VertexAttribPointerWithOffset(vertPos, 3, gl.FLOAT, false, stride, uintptr(0))
vertColorAttrib := uint32(gl.GetAttribLocation(glProgram, gl.Str("vertColor\x00")))
gl.EnableVertexAttribArray(vertColorAttrib)
gl.VertexAttribPointerWithOffset(vertColorAttrib, 3, gl.FLOAT, false, stride, uintptr(3*sizeofFloat))
vertNormal := uint32(gl.GetAttribLocation(glProgram, gl.Str("vertNormal\x00")))
gl.EnableVertexAttribArray(vertNormal)
gl.VertexAttribPointerWithOffset(vertNormal, 3, gl.FLOAT, false, stride, uintptr(3*sizeofFloat))
texCoordAttrib := uint32(gl.GetAttribLocation(glProgram, gl.Str("vertTexCoord\x00")))
gl.EnableVertexAttribArray(texCoordAttrib)
gl.VertexAttribPointerWithOffset(texCoordAttrib, 2, gl.FLOAT, false, stride, uintptr(6*sizeofFloat))
textureOnUniform := gl.GetUniformLocation(glProgram, gl.Str("textureOn\x00"))
if textureOn {
gl.Uniform1i(textureOnUniform, 1)
} else {
gl.Uniform1i(textureOnUniform, 0)
}
model := mgl32.Ident4()
modelUniform := gl.GetUniformLocation(glProgram, gl.Str("model\x00"))
gl.UniformMatrix4fv(modelUniform, 1, false, &model[0])
SetUniformMatrix4f(glProgram, "model", &model)
}
func GLStr(str string) *uint8 {
// Something about Strings going to OpenGL needs a null tagged on
// the end in addition to the special gl.Str() thing.
return gl.Str(str + "\x00")
}
func SetUniformInt(glProgram uint32, name string, i int32) {
location := gl.GetUniformLocation(glProgram, GLStr(name))
gl.Uniform1i(location, i)
}
func SetUniformFloat(glProgram uint32, name string, f float32) {
location := gl.GetUniformLocation(glProgram, GLStr(name))
gl.Uniform1f(location, f)
}
func SetUniformVec3f(glProgram uint32, name string, v mgl32.Vec3) {
location := gl.GetUniformLocation(glProgram, GLStr(name))
gl.Uniform3f(location, v.X(), v.Y(), v.Z())
}
func SetUniformMatrix4f(glProgram uint32, name string, m *mgl32.Mat4) {
location := gl.GetUniformLocation(glProgram, GLStr(name))
gl.UniformMatrix4fv(location, 1, false, &m[0])
}

View File

@ -1,34 +0,0 @@
package gl_helpers
var VertexShaderSource = `
#version 330
uniform mat4 projection;
uniform mat4 camera;
uniform mat4 model;
in vec3 vert;
in vec3 vertColor;
in vec2 vertTexCoord;
out vec3 fragColor;
out vec2 fragTexCoord;
void main() {
fragColor = vertColor;
fragTexCoord = vertTexCoord;
gl_Position = projection * camera * model * vec4(vert, 1);
}
` + "\x00"
var FragmentShaderSource = `
#version 330
uniform sampler2D tex;
uniform int textureOn;
in vec3 fragColor;
in vec2 fragTexCoord;
out vec4 outputColor;
void main() {
vec4 t = vec4(1);
if (textureOn != 0) {
t = texture(tex, fragTexCoord);
}
outputColor = t * vec4(fragColor, 1);
}
` + "\x00"

View File

@ -0,0 +1,24 @@
package gl_helpers
var VertexShaderSource = `
#version 330
uniform mat4 projection;
uniform mat4 camera;
uniform mat4 model;
in vec3 vertPos;
in vec3 vertNormal;
in vec2 vertTexCoord;
out vec3 fragPos;
out vec3 fragNormal;
out vec2 fragTexCoord;
void main() {
fragPos = vec3(model * vec4(vertPos, 1));
fragNormal = mat3(transpose(inverse(model))) * vertNormal;
fragTexCoord = vertTexCoord;
gl_Position = projection * camera * vec4(fragPos, 1);
}
` + "\x00"

View File

@ -12,47 +12,45 @@ import (
type Box struct {
Size int32
Faces []*Face
Color mgl32.Vec3
Position mgl32.Vec3
Rotation mgl32.Vec3
TextureOn bool
VertexArray []float32
Material *Material
GLProgram uint32
GLVertexArrayId uint32
GLVertexBufferId uint32
GLTextureId uint32
Wireframe bool
}
func NewBox(
width, height, depth float32,
position, color mgl32.Vec3) *Box {
position mgl32.Vec3, material *Material) *Box {
// depth = z axis (towards)
// width = x axis (right)
// height = y axis (up)
box := Box{
Color: color,
Size: 6,
Material: material,
Size: 6,
}
topPos := mgl32.Vec3{position.X(), position.Y() + height/2.0, position.Z()}
top := NewFace(width, depth, topPos, mgl32.Vec3{0, 1, 0}, color)
top := NewFace(width, depth, topPos, mgl32.Vec3{0, 1, 0}, material)
bottomPos := mgl32.Vec3{position.X(), position.Y() - height/2.0, position.Z()}
bottom := NewFace(width, depth, bottomPos, mgl32.Vec3{0, -1, 0}, color)
bottom := NewFace(width, depth, bottomPos, mgl32.Vec3{0, -1, 0}, material)
frontPos := mgl32.Vec3{position.X(), position.Y(), position.Z() + depth/2.0}
front := NewFace(width, height, frontPos, mgl32.Vec3{0, 0, 1}, color)
front := NewFace(width, height, frontPos, mgl32.Vec3{0, 0, 1}, material)
backPos := mgl32.Vec3{position.X(), position.Y(), position.Z() - depth/2.0}
back := NewFace(width, height, backPos, mgl32.Vec3{0, 0, -1}, color)
back := NewFace(width, height, backPos, mgl32.Vec3{0, 0, -1}, material)
rightPos := mgl32.Vec3{position.X() + width/2.0, position.Y(), position.Z()}
right := NewFace(height, depth, rightPos, mgl32.Vec3{1, 0, 0}, color)
right := NewFace(height, depth, rightPos, mgl32.Vec3{1, 0, 0}, material)
leftPos := mgl32.Vec3{position.X() - width/2.0, position.Y(), position.Z()}
left := NewFace(height, depth, leftPos, mgl32.Vec3{-1, 0, 0}, color)
left := NewFace(height, depth, leftPos, mgl32.Vec3{-1, 0, 0}, material)
box.Faces = []*Face{
top,
@ -91,12 +89,13 @@ func (b *Box) Update() {
func (b *Box) Draw() {
// Apply rotation
model := mgl32.HomogRotate3D(b.Rotation.Y(), mgl32.Vec3{0, 1, 0})
modelUniform := gl.GetUniformLocation(b.GLProgram, gl.Str("model\x00"))
gl.UniformMatrix4fv(modelUniform, 1, false, &model[0])
gl_helpers.SetUniformMatrix4f(b.GLProgram, "model", &model)
// Apply translation
// TODO
b.Material.Draw(b.GLProgram)
if b.Wireframe {
b.drawOutline()
} else {
@ -108,13 +107,12 @@ func (b *Box) drawTriangles() {
gl.BindVertexArray(b.GLVertexArrayId)
gl.BindBuffer(gl.ARRAY_BUFFER, b.GLVertexBufferId)
textureOn := b.GLTextureId != 0
if textureOn {
if b.Material.TextureOn {
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, b.GLTextureId)
gl.BindTexture(gl.TEXTURE_2D, b.Material.TextureId)
}
gl_helpers.GLInitVertexAttribs(b.GLProgram, VertexSize, textureOn)
gl_helpers.UpdateVertexAttribs(b.GLProgram, VertexSize)
gl.DrawArrays(gl.TRIANGLES, 0, 6*2*3) // 6 sides, 2 triangles, 3 points each
}
@ -124,10 +122,6 @@ func (b *Box) drawOutline() {
}
}
func (b *Box) SetTexture(textureId uint32) {
b.GLTextureId = textureId
}
func (b *Box) GetVertexArray() []float32 {
// Boxes have 6 faces, 2 triangles per face, 3 vertices per triangle
// (technically 2 vertices overlap in a face, but ignore that for now)

View File

@ -1,7 +1,7 @@
package gl_objects
import (
gl "github.com/go-gl/gl/v3.1/gles2"
"gitea.wisellama.rocks/carpy-breakout/pkg/gl_helpers"
"github.com/go-gl/mathgl/mgl32"
)
@ -47,8 +47,8 @@ func (c *Camera) GetCenter() mgl32.Vec3 {
func (c *Camera) Draw() {
cameraMatrix := mgl32.LookAtV(c.Position, c.GetCenter(), c.Up)
cameraUniform := gl.GetUniformLocation(c.GLProgram, gl.Str("camera\x00"))
gl.UniformMatrix4fv(cameraUniform, 1, false, &cameraMatrix[0])
gl_helpers.SetUniformMatrix4f(c.GLProgram, "camera", &cameraMatrix)
gl_helpers.SetUniformVec3f(c.GLProgram, "cameraPos", c.Position)
}
func (c *Camera) Update() {

View File

@ -0,0 +1,32 @@
package gl_objects
import (
"github.com/go-gl/mathgl/mgl32"
"gitea.wisellama.rocks/carpy-breakout/pkg/gl_helpers"
)
// Directional Light (e.g. the Sun)
type DirLight struct {
Direction mgl32.Vec3 // the direction the light is travelling from the sun
AmbientColor mgl32.Vec3
DiffuseColor mgl32.Vec3
SpecularColor mgl32.Vec3
}
func NewDirLight(direction mgl32.Vec3) *DirLight {
d := DirLight{
Direction: direction,
AmbientColor: mgl32.Vec3{0.2, 0.2, 0.2},
DiffuseColor: mgl32.Vec3{1, 1, 1},
SpecularColor: mgl32.Vec3{1, 1, 1},
}
return &d
}
func (l *DirLight) GLInit(glProgram uint32) {
gl_helpers.SetUniformVec3f(glProgram, "dirLight.ambient", l.AmbientColor)
gl_helpers.SetUniformVec3f(glProgram, "dirLight.diffuse", l.DiffuseColor)
gl_helpers.SetUniformVec3f(glProgram, "dirLight.specular", l.SpecularColor)
gl_helpers.SetUniformVec3f(glProgram, "dirLight.direction", l.Direction)
}

View File

@ -12,18 +12,16 @@ import (
type Face struct {
Triangles []*Triangle
LineLoopColor mgl32.Vec3
LineLoopVertexArray []float32
GLLineLoopVertexArrayId uint32
GLLineLoopVertexBufferId uint32
Material *Material
}
func NewFace(
width, height float32,
center, normal mgl32.Vec3,
color mgl32.Vec3) *Face {
faceColor := color
material *Material) *Face {
// top left, shared
v1 := newFaceVertex(
@ -31,7 +29,6 @@ func NewFace(
height/2.0,
center,
normal,
faceColor,
mgl32.Vec2{0, 1})
v1_2 := *v1
@ -41,7 +38,6 @@ func NewFace(
-1*height/2.0,
center,
normal,
faceColor,
mgl32.Vec2{0, 0})
// bottom right, shared
@ -50,7 +46,6 @@ func NewFace(
-1*height/2.0,
center,
normal,
faceColor,
mgl32.Vec2{1, 0})
v3_2 := *v3
@ -60,15 +55,14 @@ func NewFace(
height/2.0,
center,
normal,
faceColor,
mgl32.Vec2{1, 1})
t1 := NewTriangle(v1, v2, v3)
t2 := NewTriangle(&v3_2, v4, &v1_2)
face := Face{
Triangles: []*Triangle{t1, t2},
LineLoopColor: color,
Material: material,
Triangles: []*Triangle{t1, t2},
}
face.LineLoopVertexArray = face.GetLineLoopVertexArray()
return &face
@ -77,7 +71,7 @@ func NewFace(
func newFaceVertex(
xoffset, zoffset float32,
center, normal mgl32.Vec3,
color mgl32.Vec3, texture mgl32.Vec2) *Vertex {
texture mgl32.Vec2) *Vertex {
// We'll create the face as though the normal were simply upward
// {0, 1, 0} and then rotate all the points to fit the actual
// normal.
@ -102,7 +96,7 @@ func newFaceVertex(
vertex := NewVertex(
v.X(), v.Y(), v.Z(),
color[0], color[1], color[2],
normal.X(), normal.Y(), normal.Z(),
texture[0], texture[1])
return vertex
@ -126,14 +120,12 @@ func (f *Face) GetLineLoopVertexArray() []float32 {
i := 0
for _, vertex := range f.Triangles[0].Vertices {
v := *vertex
v.SetColor(f.LineLoopColor)
for _, elem := range v.GetVertexArray() {
a[i] = elem
i++
}
}
fourthV := *f.Triangles[1].Vertices[1]
fourthV.SetColor(f.LineLoopColor)
for _, elem := range fourthV.GetVertexArray() {
a[i] = elem
i++
@ -146,13 +138,12 @@ func (f *Face) DrawLineLoop(glProgram uint32) {
gl.BindVertexArray(f.GLLineLoopVertexArrayId)
gl.BindBuffer(gl.ARRAY_BUFFER, f.GLLineLoopVertexBufferId)
gl_helpers.GLInitVertexAttribs(glProgram, VertexSize, false)
gl_helpers.UpdateVertexAttribs(glProgram, VertexSize)
gl.DrawArrays(gl.LINE_LOOP, 0, 4)
}
func (f *Face) GLInit(glProgram uint32) {
textureOn := false
gl_helpers.GLInitVertexAttribs(glProgram, VertexSize, textureOn)
gl_helpers.UpdateVertexAttribs(glProgram, VertexSize)
// LineLoop setup
gl.GenVertexArrays(1, &f.GLLineLoopVertexArrayId)
gl.BindVertexArray(f.GLLineLoopVertexArrayId)

View File

@ -0,0 +1,40 @@
package gl_objects
import (
"github.com/go-gl/mathgl/mgl32"
"gitea.wisellama.rocks/carpy-breakout/pkg/gl_helpers"
)
type Material struct {
DiffuseTexture int32
SpecularTexture int32
Shininess float32
Color mgl32.Vec3
TextureOn bool
TextureId uint32
}
func NewMaterial() *Material {
m := Material{
Shininess: 32.0,
Color: mgl32.Vec3{1, 1, 1},
TextureOn: false,
}
return &m
}
func (m *Material) Draw(glProgram uint32) {
gl_helpers.SetUniformInt(glProgram, "material.diffuse", m.DiffuseTexture)
gl_helpers.SetUniformInt(glProgram, "material.specular", m.SpecularTexture)
gl_helpers.SetUniformFloat(glProgram, "material.shininess", m.Shininess)
gl_helpers.SetUniformVec3f(glProgram, "material.color", m.Color)
var tOn int32 = 0
if m.TextureOn {
tOn = 1
}
gl_helpers.SetUniformInt(glProgram, "material.textureOn", tOn)
}

View File

@ -1,7 +1,5 @@
package gl_objects
import "github.com/go-gl/mathgl/mgl32"
type Triangle struct {
Vertices []*Vertex
}
@ -14,12 +12,6 @@ func NewTriangle(a, b, c *Vertex) *Triangle {
return &triangle
}
func (t *Triangle) SetColor(color mgl32.Vec3) {
for _, vertex := range t.Vertices {
vertex.SetColor(color)
}
}
func (t *Triangle) GetVertexArray() []float32 {
a := make([]float32, 3*VertexSize)

View File

@ -8,23 +8,19 @@ const VertexSize int32 = 8
type Vertex struct {
Position mgl32.Vec3
Color mgl32.Vec3
Normal mgl32.Vec3
Texture mgl32.Vec2
}
func NewVertex(x, y, z, r, g, b, u, v float32) *Vertex {
func NewVertex(px, py, pz, nx, ny, nz, tx, ty float32) *Vertex {
vertex := Vertex{
Position: mgl32.Vec3{x, y, z},
Color: mgl32.Vec3{r, g, b},
Texture: mgl32.Vec2{u, v},
Position: mgl32.Vec3{px, py, pz},
Normal: mgl32.Vec3{nx, ny, nz},
Texture: mgl32.Vec2{tx, ty},
}
return &vertex
}
func (v *Vertex) SetColor(color mgl32.Vec3) {
v.Color = color
}
func (v *Vertex) GetVertexArray() []float32 {
a := make([]float32, VertexSize)
@ -32,9 +28,9 @@ func (v *Vertex) GetVertexArray() []float32 {
a[1] = v.Position[1]
a[2] = v.Position[2]
a[3] = v.Color[0]
a[4] = v.Color[1]
a[5] = v.Color[2]
a[3] = v.Normal[0]
a[4] = v.Normal[1]
a[5] = v.Normal[2]
a[6] = v.Texture[0]
a[7] = v.Texture[1]