diff --git a/main.go b/main.go index fe2c8be..bd5be06 100755 --- a/main.go +++ b/main.go @@ -26,9 +26,10 @@ import ( /* @TODO: - Rendering: - - Phong lighting model - - Point lights - - Spotlights + - Phong lighting model ✅ + - Directional lights ✅ + - Point lights ✅ + - Spotlights ✅ - HDR - Cascaded shadow mapping - Skeletal animations @@ -62,6 +63,7 @@ type PointLight struct { } type SpotLight struct { + Pos gglm.Vec3 Dir gglm.Vec3 DiffuseColor gglm.Vec3 SpecularColor gglm.Vec3 @@ -69,6 +71,18 @@ type SpotLight struct { OuterCutoff float32 } +// SetCutoffs properly sets the cosine values of the cutoffs using the passed +// degrees. +// +// The light has full intensity within the inner cutoff, falloff between +// inner-outer cutoff, and zero light beyond the outer cutoff. +// +// The inner cuttoff degree must be *smaller* than the outer cutoff +func (s *SpotLight) SetCutoffs(innerCutoffAngleDeg, outerCutoffAngleDeg float32) { + s.InnerCutoff = gglm.Cos32(innerCutoffAngleDeg * gglm.Deg2Rad) + s.OuterCutoff = gglm.Cos32(outerCutoffAngleDeg * gglm.Deg2Rad) +} + const ( camSpeed = 15 mouseSensitivity = 0.5 @@ -148,6 +162,17 @@ var ( Quadratic: 0.032, }, } + spotLights = [...]SpotLight{ + { + Pos: *gglm.NewVec3(0, 5, 0), + Dir: *gglm.NewVec3(0, -1, 0), + DiffuseColor: *gglm.NewVec3(0, 1, 1), + SpecularColor: *gglm.NewVec3(1, 1, 1), + // These must be cosine values + InnerCutoff: gglm.Cos32(15 * gglm.Deg2Rad), + OuterCutoff: gglm.Cos32(20 * gglm.Deg2Rad), + }, + } ) type Game struct { @@ -456,6 +481,36 @@ func (g *Game) updateLights() { containerMat.SetUnifFloat32(indexString+".quadratic", pl.Quadratic) palleteMat.SetUnifFloat32(indexString+".quadratic", pl.Quadratic) } + + for i := 0; i < len(spotLights); i++ { + + l := &spotLights[i] + indexString := "spotLights[" + strconv.Itoa(i) + "]" + + whiteMat.SetUnifVec3(indexString+".pos", &l.Pos) + containerMat.SetUnifVec3(indexString+".pos", &l.Pos) + palleteMat.SetUnifVec3(indexString+".pos", &l.Pos) + + whiteMat.SetUnifVec3(indexString+".dir", &l.Dir) + containerMat.SetUnifVec3(indexString+".dir", &l.Dir) + palleteMat.SetUnifVec3(indexString+".dir", &l.Dir) + + whiteMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) + containerMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) + palleteMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) + + whiteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) + containerMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) + palleteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) + + whiteMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) + containerMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) + palleteMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) + + whiteMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) + containerMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) + palleteMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) + } } func (g *Game) Update() { @@ -544,35 +599,91 @@ func (g *Game) showDebugWindow() { imgui.Spacing() // Point lights - imgui.Text("Point Lights") - - if imgui.BeginListBoxV("", imgui.Vec2{Y: 200}) { + if imgui.BeginListBoxV("Point Lights", imgui.Vec2{Y: 200}) { for i := 0; i < len(pointLights); i++ { pl := &pointLights[i] - indexString := strconv.Itoa(i) + indexNumString := strconv.Itoa(i) - if !imgui.TreeNodeExStrV("Light "+indexString, imgui.TreeNodeFlagsSpanAvailWidth) { + if !imgui.TreeNodeExStrV("Point Light "+indexNumString, imgui.TreeNodeFlagsSpanAvailWidth) { continue } + indexString := "pointLights[" + indexNumString + "]" + if imgui.DragFloat3("Pos", &pl.Pos.Data) { - whiteMat.SetUnifVec3("pointLights["+indexString+"].pos", &pl.Pos) - containerMat.SetUnifVec3("pointLights["+indexString+"].pos", &pl.Pos) - palleteMat.SetUnifVec3("pointLights["+indexString+"].pos", &pl.Pos) + whiteMat.SetUnifVec3(indexString+".pos", &pl.Pos) + containerMat.SetUnifVec3(indexString+".pos", &pl.Pos) + palleteMat.SetUnifVec3(indexString+".pos", &pl.Pos) } if imgui.DragFloat3("Diffuse Color", &pl.DiffuseColor.Data) { - whiteMat.SetUnifVec3("pointLights["+indexString+"].diffuseColor", &pl.DiffuseColor) - containerMat.SetUnifVec3("pointLights["+indexString+"].diffuseColor", &pl.DiffuseColor) - palleteMat.SetUnifVec3("pointLights["+indexString+"].diffuseColor", &pl.DiffuseColor) + whiteMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) + containerMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) + palleteMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) } if imgui.DragFloat3("Specular Color", &pl.SpecularColor.Data) { - whiteMat.SetUnifVec3("pointLights["+indexString+"].specularColor", &pl.SpecularColor) - containerMat.SetUnifVec3("pointLights["+indexString+"].specularColor", &pl.SpecularColor) - palleteMat.SetUnifVec3("pointLights["+indexString+"].specularColor", &pl.SpecularColor) + whiteMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) + containerMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) + palleteMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) + } + + imgui.TreePop() + } + + imgui.EndListBox() + } + + // Spot lights + if imgui.BeginListBoxV("Spot Lights", imgui.Vec2{Y: 200}) { + + for i := 0; i < len(spotLights); i++ { + + l := &spotLights[i] + indexNumString := strconv.Itoa(i) + + if !imgui.TreeNodeExStrV("Spot Light "+indexNumString, imgui.TreeNodeFlagsSpanAvailWidth) { + continue + } + + indexString := "spotLights[" + indexNumString + "]" + + if imgui.DragFloat3("Pos", &l.Pos.Data) { + whiteMat.SetUnifVec3(indexString+".pos", &l.Pos) + containerMat.SetUnifVec3(indexString+".pos", &l.Pos) + palleteMat.SetUnifVec3(indexString+".pos", &l.Pos) + } + + if imgui.DragFloat3("Dir", &l.Dir.Data) { + whiteMat.SetUnifVec3(indexString+".dir", &l.Dir) + containerMat.SetUnifVec3(indexString+".dir", &l.Dir) + palleteMat.SetUnifVec3(indexString+".dir", &l.Dir) + } + + if imgui.DragFloat3("Diffuse Color", &l.DiffuseColor.Data) { + whiteMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) + containerMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) + palleteMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) + } + + if imgui.DragFloat3("Specular Color", &l.SpecularColor.Data) { + whiteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) + containerMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) + palleteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) + } + + if imgui.DragFloat("Inner Cutoff", &l.InnerCutoff) { + whiteMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) + containerMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) + palleteMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) + } + + if imgui.DragFloat("Outer Cutoff", &l.OuterCutoff) { + whiteMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) + containerMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) + palleteMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) } imgui.TreePop() diff --git a/res/shaders/simple.glsl b/res/shaders/simple.glsl index e67b362..32bea72 100755 --- a/res/shaders/simple.glsl +++ b/res/shaders/simple.glsl @@ -64,6 +64,18 @@ struct PointLight { #define NUM_POINT_LIGHTS 16 uniform PointLight pointLights[NUM_POINT_LIGHTS]; +struct SpotLight { + vec3 pos; + vec3 dir; + vec3 diffuseColor; + vec3 specularColor; + float innerCutoff; + float outerCutoff; +}; + +#define NUM_SPOT_LIGHTS 4 +uniform SpotLight spotLights[NUM_SPOT_LIGHTS]; + uniform vec3 camPos; uniform vec3 ambientColor = vec3(0.2, 0.2, 0.2); @@ -122,9 +134,33 @@ vec3 CalcPointLight(PointLight pointLight) return (finalDiffuse + finalSpecular) * attenuation; } -vec3 CalcSpotLight() +vec3 CalcSpotLight(SpotLight light) { - return vec3(0); + if (light.innerCutoff == 0) + return vec3(0); + + vec3 fragToLightDir = normalize(light.pos - fragPos); + + // Spot light cone with full intensity within inner cutoff, + // and falloff between inner-outer cutoffs, and zero + // light after outer cutoff + float theta = dot(fragToLightDir, normalize(-light.dir)); + float epsilon = (light.innerCutoff - light.outerCutoff); + float intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0); + + if (intensity == 0) + return vec3(0); + + // Diffuse + float diffuseAmount = max(0.0, dot(normalizedVertNorm, fragToLightDir)); + vec3 finalDiffuse = diffuseAmount * light.diffuseColor * diffuseTexColor.rgb; + + // Specular + vec3 reflectDir = reflect(-fragToLightDir, normalizedVertNorm); + float specularAmount = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + vec3 finalSpecular = specularAmount * light.specularColor * specularTexColor.rgb; + + return (finalDiffuse + finalSpecular) * intensity; } void main() @@ -145,7 +181,10 @@ void main() finalColor += CalcPointLight(pointLights[i]); } - finalColor += CalcSpotLight(); + for (int i = 0; i < NUM_SPOT_LIGHTS; i++) + { + finalColor += CalcSpotLight(spotLights[i]); + } vec3 finalEmission = emissionTexColor.rgb; vec3 finalAmbient = ambientColor * diffuseTexColor.rgb;