Spotlight shadows

This commit is contained in:
bloeys
2024-04-16 10:34:30 +04:00
parent fbfcbaa156
commit f35c217d73
6 changed files with 380 additions and 99 deletions

View File

@ -11,6 +11,7 @@ type FramebufferAttachmentType int32
const ( const (
FramebufferAttachmentType_Unknown FramebufferAttachmentType = iota FramebufferAttachmentType_Unknown FramebufferAttachmentType = iota
FramebufferAttachmentType_Texture FramebufferAttachmentType_Texture
FramebufferAttachmentType_Texture_Array
FramebufferAttachmentType_Renderbuffer FramebufferAttachmentType_Renderbuffer
FramebufferAttachmentType_Cubemap FramebufferAttachmentType_Cubemap
FramebufferAttachmentType_Cubemap_Array FramebufferAttachmentType_Cubemap_Array
@ -21,6 +22,8 @@ func (f FramebufferAttachmentType) IsValid() bool {
switch f { switch f {
case FramebufferAttachmentType_Texture: case FramebufferAttachmentType_Texture:
fallthrough fallthrough
case FramebufferAttachmentType_Texture_Array:
fallthrough
case FramebufferAttachmentType_Renderbuffer: case FramebufferAttachmentType_Renderbuffer:
fallthrough fallthrough
case FramebufferAttachmentType_Cubemap: case FramebufferAttachmentType_Cubemap:
@ -180,6 +183,10 @@ func (fbo *Framebuffer) NewColorAttachment(
logging.ErrLog.Fatalf("failed creating color attachment because cubemaps can not be color attachments (at least in this implementation. You might be able to do it manually)\n") logging.ErrLog.Fatalf("failed creating color attachment because cubemaps can not be color attachments (at least in this implementation. You might be able to do it manually)\n")
} }
if attachType == FramebufferAttachmentType_Texture_Array {
logging.ErrLog.Fatalf("failed creating color attachment because texture arrays can not be color attachments (implementation can be updated to support it or you can do it manually)\n")
}
if !attachFormat.IsColorFormat() { if !attachFormat.IsColorFormat() {
logging.ErrLog.Fatalf("failed creating color attachment for framebuffer due to attachment data format not being a valid color type. Data format=%d\n", attachFormat) logging.ErrLog.Fatalf("failed creating color attachment for framebuffer due to attachment data format not being a valid color type. Data format=%d\n", attachFormat)
} }
@ -271,6 +278,10 @@ func (fbo *Framebuffer) NewDepthAttachment(
logging.ErrLog.Fatalf("failed creating cubemap array depth attachment because 'NewDepthCubemapArrayAttachment' must be used for that\n") logging.ErrLog.Fatalf("failed creating cubemap array depth attachment because 'NewDepthCubemapArrayAttachment' must be used for that\n")
} }
if attachType == FramebufferAttachmentType_Texture_Array {
logging.ErrLog.Fatalf("failed creating texture array depth attachment because 'NewDepthTextureArrayAttachment' must be used for that\n")
}
a := FramebufferAttachment{ a := FramebufferAttachment{
Type: attachType, Type: attachType,
Format: attachFormat, Format: attachFormat,
@ -407,6 +418,68 @@ func (fbo *Framebuffer) NewDepthCubemapArrayAttachment(
fbo.Attachments = append(fbo.Attachments, a) fbo.Attachments = append(fbo.Attachments, a)
} }
func (fbo *Framebuffer) NewDepthTextureArrayAttachment(
attachFormat FramebufferAttachmentDataFormat,
numTextures int32,
) {
if fbo.HasDepthAttachment() {
logging.ErrLog.Fatalf("failed creating texture array depth attachment for framebuffer because a depth attachment already exists\n")
}
if !attachFormat.IsDepthFormat() {
logging.ErrLog.Fatalf("failed creating depth attachment for framebuffer due to attachment data format not being a valid depth-stencil type. Data format=%d\n", attachFormat)
}
a := FramebufferAttachment{
Type: FramebufferAttachmentType_Texture_Array,
Format: attachFormat,
}
fbo.Bind()
// Create cubemap array
gl.GenTextures(1, &a.Id)
if a.Id == 0 {
logging.ErrLog.Fatalf("failed to generate texture for framebuffer. GlError=%d\n", gl.GetError())
}
gl.BindTexture(gl.TEXTURE_2D_ARRAY, a.Id)
gl.TexImage3D(
gl.TEXTURE_2D_ARRAY,
0,
attachFormat.GlInternalFormat(),
int32(fbo.Width),
int32(fbo.Height),
numTextures,
0,
attachFormat.GlFormat(),
gl.FLOAT,
nil,
)
gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
// This is so that any sampling outside the depth map gives a full depth value.
// Useful for example when doing shadow maps where we want things outside
// the range of the texture to not show shadow
borderColor := []float32{1, 1, 1, 1}
gl.TexParameterfv(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_BORDER_COLOR, &borderColor[0])
gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER)
gl.TexParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER)
gl.BindTexture(gl.TEXTURE_2D_ARRAY, 0)
// Attach to fbo
gl.FramebufferTexture(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, a.Id, 0)
fbo.UnBind()
fbo.ClearFlags |= gl.DEPTH_BUFFER_BIT
fbo.Attachments = append(fbo.Attachments, a)
}
func (fbo *Framebuffer) NewDepthStencilAttachment( func (fbo *Framebuffer) NewDepthStencilAttachment(
attachType FramebufferAttachmentType, attachType FramebufferAttachmentType,
attachFormat FramebufferAttachmentDataFormat, attachFormat FramebufferAttachmentDataFormat,

235
main.go
View File

@ -32,7 +32,8 @@ import (
- Spotlights ✅ - Spotlights ✅
- Directional light shadows ✅ - Directional light shadows ✅
- Point light shadows ✅ - Point light shadows ✅
- Spotlight shadows - Spotlight shadows
- UBO support
- HDR - HDR
- Cascaded shadow mapping - Cascaded shadow mapping
- Skeletal animations - Skeletal animations
@ -70,7 +71,7 @@ func (d *DirLight) GetProjViewMat() gglm.Mat4 {
farClip := dirLightFar farClip := dirLightFar
projMat := gglm.Ortho(-size, size, -size, size, nearClip, farClip).Mat4 projMat := gglm.Ortho(-size, size, -size, size, nearClip, farClip).Mat4
viewMat := gglm.LookAtRH(pos, pos.Clone().Add(d.Dir.Clone().Scale(10)), gglm.NewVec3(0, 1, 0)).Mat4 viewMat := gglm.LookAtRH(pos, pos.Clone().Add(&d.Dir), gglm.NewVec3(0, 1, 0)).Mat4
return *projMat.Mul(&viewMat) return *projMat.Mul(&viewMat)
} }
@ -93,6 +94,9 @@ type PointLight struct {
const ( const (
MaxPointLights = 8 MaxPointLights = 8
// If this changes update the array depth map shader
MaxSpotLights = 4
) )
var ( var (
@ -121,20 +125,38 @@ type SpotLight struct {
Dir gglm.Vec3 Dir gglm.Vec3
DiffuseColor gglm.Vec3 DiffuseColor gglm.Vec3
SpecularColor gglm.Vec3 SpecularColor gglm.Vec3
InnerCutoff float32 InnerCutoffRad float32
OuterCutoff float32 OuterCutoffRad float32
// Near plane like 0.x (or anything too small) causes shadows to not work properly.
// Needs adjusting as the distance of light to object increases
NearPlane float32
FarPlane float32
} }
// SetCutoffs properly sets the cosine values of the cutoffs using the passed func (s *SpotLight) GetProjViewMat() gglm.Mat4 {
// degrees.
// projMat := gglm.Perspective(s.OuterCutoffRad*2, 1, s.NearPlane, s.FarPlane)
// The light has full intensity within the inner cutoff, falloff between
// inner-outer cutoff, and zero light beyond the outer cutoff. // Adjust up vector if lightDir is parallel or nearly parallel to upVector
// // as lookat view matrix breaks if up and look at are parallel
// The inner cuttoff degree must be *smaller* than the outer cutoff up := gglm.NewVec3(0, 1, 0)
func (s *SpotLight) SetCutoffs(innerCutoffAngleDeg, outerCutoffAngleDeg float32) { if gglm.Abs32(gglm.DotVec3(&s.Dir, up)) > 0.99 {
s.InnerCutoff = gglm.Cos32(innerCutoffAngleDeg * gglm.Deg2Rad) up.SetXY(1, 0)
s.OuterCutoff = gglm.Cos32(outerCutoffAngleDeg * gglm.Deg2Rad) }
viewMat := gglm.LookAtRH(&s.Pos, s.Pos.Clone().Add(&s.Dir), up).Mat4
return *projMat.Mul(&viewMat)
}
func (s *SpotLight) InnerCutoffCos() float32 {
return gglm.Cos32(s.InnerCutoffRad)
}
func (s *SpotLight) OuterCutoffCos() float32 {
return gglm.Cos32(s.OuterCutoffRad)
} }
const ( const (
@ -153,20 +175,23 @@ var (
cam *camera.Camera cam *camera.Camera
// Demo fbo // Demo fbo
renderToDemoFbo = true renderToDemoFbo = false
renderToBackBuffer = true renderToBackBuffer = true
demoFboScale = gglm.NewVec2(0.25, 0.25) demoFboScale = gglm.NewVec2(0.25, 0.25)
demoFboOffset = gglm.NewVec2(0.75, -0.75) demoFboOffset = gglm.NewVec2(0.75, -0.75)
demoFbo buffers.Framebuffer demoFbo buffers.Framebuffer
// Dir light fbo // Dir light fbo
showDirLightDepthMapFbo = true showDirLightDepthMapFbo = false
dirLightDepthMapFboScale = gglm.NewVec2(0.25, 0.25) dirLightDepthMapFboScale = gglm.NewVec2(0.25, 0.25)
dirLightDepthMapFboOffset = gglm.NewVec2(0.75, -0.2) dirLightDepthMapFboOffset = gglm.NewVec2(0.75, -0.2)
dirLightDepthMapFbo buffers.Framebuffer dirLightDepthMapFbo buffers.Framebuffer
// Point light fbo // Point light fbo
omnidirDepthMapFbo buffers.Framebuffer pointLightDepthMapFbo buffers.Framebuffer
// Spot light fbo
spotLightDepthMapFbo buffers.Framebuffer
screenQuadVao buffers.VertexArray screenQuadVao buffers.VertexArray
screenQuadMat *materials.Material screenQuadMat *materials.Material
@ -176,7 +201,8 @@ var (
containerMat *materials.Material containerMat *materials.Material
palleteMat *materials.Material palleteMat *materials.Material
skyboxMat *materials.Material skyboxMat *materials.Material
dirLightDepthMapMat *materials.Material depthMapMat *materials.Material
arrayDepthMapMat *materials.Material
omnidirDepthMapMat *materials.Material omnidirDepthMapMat *materials.Material
debugDepthMat *materials.Material debugDepthMat *materials.Material
@ -245,13 +271,16 @@ var (
} }
spotLights = [...]SpotLight{ spotLights = [...]SpotLight{
{ {
Pos: *gglm.NewVec3(2, 5, 5), Pos: *gglm.NewVec3(-4, 7, 5),
Dir: *gglm.NewVec3(0, -1, 0), Dir: *gglm.NewVec3(1.5, -0.9, 0).Normalize(),
DiffuseColor: *gglm.NewVec3(0, 1, 1), DiffuseColor: *gglm.NewVec3(1, 0, 1),
SpecularColor: *gglm.NewVec3(1, 1, 1), SpecularColor: *gglm.NewVec3(1, 1, 1),
// These must be cosine values // These must be cosine values
InnerCutoff: gglm.Cos32(15 * gglm.Deg2Rad), InnerCutoffRad: 15 * gglm.Deg2Rad,
OuterCutoff: gglm.Cos32(20 * gglm.Deg2Rad), OuterCutoffRad: 20 * gglm.Deg2Rad,
NearPlane: 1,
FarPlane: 30,
}, },
} }
) )
@ -442,8 +471,9 @@ func (g *Game) Init() {
whiteMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) whiteMat.SetUnifVec3("dirLight.dir", &dirLight.Dir)
whiteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) whiteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor)
whiteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) whiteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor)
whiteMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap)) whiteMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap1))
whiteMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array)) whiteMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array))
whiteMat.SetUnifInt32("spotLightShadowMaps", int32(materials.TextureSlot_ShadowMap_Array1))
containerMat = materials.NewMaterial("Container mat", "./res/shaders/simple.glsl") containerMat = materials.NewMaterial("Container mat", "./res/shaders/simple.glsl")
containerMat.Shininess = 64 containerMat.Shininess = 64
@ -460,8 +490,9 @@ func (g *Game) Init() {
containerMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) containerMat.SetUnifVec3("dirLight.dir", &dirLight.Dir)
containerMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) containerMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor)
containerMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) containerMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor)
containerMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap)) containerMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap1))
containerMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array)) containerMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array))
containerMat.SetUnifInt32("spotLightShadowMaps", int32(materials.TextureSlot_ShadowMap_Array1))
palleteMat = materials.NewMaterial("Pallete mat", "./res/shaders/simple.glsl") palleteMat = materials.NewMaterial("Pallete mat", "./res/shaders/simple.glsl")
palleteMat.Shininess = 64 palleteMat.Shininess = 64
@ -477,12 +508,15 @@ func (g *Game) Init() {
palleteMat.SetUnifFloat32("material.shininess", palleteMat.Shininess) palleteMat.SetUnifFloat32("material.shininess", palleteMat.Shininess)
palleteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) palleteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor)
palleteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) palleteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor)
palleteMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap)) palleteMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap1))
palleteMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array)) palleteMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array))
palleteMat.SetUnifInt32("spotLightShadowMaps", int32(materials.TextureSlot_ShadowMap_Array1))
debugDepthMat = materials.NewMaterial("Debug depth mat", "./res/shaders/debug-depth.glsl") debugDepthMat = materials.NewMaterial("Debug depth mat", "./res/shaders/debug-depth.glsl")
dirLightDepthMapMat = materials.NewMaterial("Directional Depth Map mat", "./res/shaders/directional-depth-map.glsl") depthMapMat = materials.NewMaterial("Depth Map mat", "./res/shaders/depth-map.glsl")
arrayDepthMapMat = materials.NewMaterial("Array Depth Map mat", "./res/shaders/array-depth-map.glsl")
omnidirDepthMapMat = materials.NewMaterial("Omnidirectional Depth Map mat", "./res/shaders/omnidirectional-depth-map.glsl") omnidirDepthMapMat = materials.NewMaterial("Omnidirectional Depth Map mat", "./res/shaders/omnidirectional-depth-map.glsl")
@ -540,15 +574,25 @@ func (g *Game) initFbos() {
assert.T(dirLightDepthMapFbo.IsComplete(), "Depth map fbo is not complete after init") assert.T(dirLightDepthMapFbo.IsComplete(), "Depth map fbo is not complete after init")
// Cubemap fbo // Point light depth map fbo
omnidirDepthMapFbo = buffers.NewFramebuffer(1024, 1024) pointLightDepthMapFbo = buffers.NewFramebuffer(1024, 1024)
omnidirDepthMapFbo.SetNoColorBuffer() pointLightDepthMapFbo.SetNoColorBuffer()
omnidirDepthMapFbo.NewDepthCubemapArrayAttachment( pointLightDepthMapFbo.NewDepthCubemapArrayAttachment(
buffers.FramebufferAttachmentDataFormat_DepthF32, buffers.FramebufferAttachmentDataFormat_DepthF32,
MaxPointLights, MaxPointLights,
) )
assert.T(omnidirDepthMapFbo.IsComplete(), "Cubemap fbo is not complete after init") assert.T(pointLightDepthMapFbo.IsComplete(), "Point light depth map fbo is not complete after init")
// Spot light depth map fbo
spotLightDepthMapFbo = buffers.NewFramebuffer(1024, 1024)
spotLightDepthMapFbo.SetNoColorBuffer()
spotLightDepthMapFbo.NewDepthTextureArrayAttachment(
buffers.FramebufferAttachmentDataFormat_DepthF32,
MaxSpotLights,
)
assert.T(spotLightDepthMapFbo.IsComplete(), "Spot light depth map fbo is not complete after init")
} }
func (g *Game) updateLights() { func (g *Game) updateLights() {
@ -593,14 +637,17 @@ func (g *Game) updateLights() {
palleteMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) palleteMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane)
} }
whiteMat.CubemapArrayTex = omnidirDepthMapFbo.Attachments[0].Id whiteMat.CubemapArrayTex = pointLightDepthMapFbo.Attachments[0].Id
containerMat.CubemapArrayTex = omnidirDepthMapFbo.Attachments[0].Id containerMat.CubemapArrayTex = pointLightDepthMapFbo.Attachments[0].Id
palleteMat.CubemapArrayTex = omnidirDepthMapFbo.Attachments[0].Id palleteMat.CubemapArrayTex = pointLightDepthMapFbo.Attachments[0].Id
// Spotlights // Spotlights
for i := 0; i < len(spotLights); i++ { for i := 0; i < len(spotLights); i++ {
l := &spotLights[i] l := &spotLights[i]
innerCutoffCos := l.InnerCutoffCos()
outerCutoffCos := l.OuterCutoffCos()
indexString := "spotLights[" + strconv.Itoa(i) + "]" indexString := "spotLights[" + strconv.Itoa(i) + "]"
whiteMat.SetUnifVec3(indexString+".pos", &l.Pos) whiteMat.SetUnifVec3(indexString+".pos", &l.Pos)
@ -619,14 +666,18 @@ func (g *Game) updateLights() {
containerMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) containerMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor)
palleteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) palleteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor)
whiteMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) whiteMat.SetUnifFloat32(indexString+".innerCutoff", innerCutoffCos)
containerMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) containerMat.SetUnifFloat32(indexString+".innerCutoff", innerCutoffCos)
palleteMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) palleteMat.SetUnifFloat32(indexString+".innerCutoff", innerCutoffCos)
whiteMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) whiteMat.SetUnifFloat32(indexString+".outerCutoff", outerCutoffCos)
containerMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) containerMat.SetUnifFloat32(indexString+".outerCutoff", outerCutoffCos)
palleteMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) palleteMat.SetUnifFloat32(indexString+".outerCutoff", outerCutoffCos)
} }
whiteMat.ShadowMapTexArray1 = spotLightDepthMapFbo.Attachments[0].Id
containerMat.ShadowMapTexArray1 = spotLightDepthMapFbo.Attachments[0].Id
palleteMat.ShadowMapTexArray1 = spotLightDepthMapFbo.Attachments[0].Id
} }
func (g *Game) Update() { func (g *Game) Update() {
@ -638,11 +689,6 @@ func (g *Game) Update() {
g.updateCameraLookAround() g.updateCameraLookAround()
g.updateCameraPos() g.updateCameraPos()
//Rotating cubes
if input.KeyDown(sdl.K_SPACE) {
cubeModelMat.Rotate(10*timing.DT()*gglm.Deg2Rad, gglm.NewVec3(1, 1, 1).Normalize())
}
g.showDebugWindow() g.showDebugWindow()
if input.KeyClicked(sdl.K_F4) { if input.KeyClicked(sdl.K_F4) {
@ -763,6 +809,8 @@ func (g *Game) showDebugWindow() {
} }
// Spot lights // Spot lights
imgui.Checkbox("Render Spot Light Shadows", &renderSpotLightShadows)
if imgui.BeginListBoxV("Spot Lights", imgui.Vec2{Y: 200}) { if imgui.BeginListBoxV("Spot Lights", imgui.Vec2{Y: 200}) {
for i := 0; i < len(spotLights); i++ { for i := 0; i < len(spotLights); i++ {
@ -800,18 +848,27 @@ func (g *Game) showDebugWindow() {
palleteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) palleteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor)
} }
if imgui.DragFloat("Inner Cutoff", &l.InnerCutoff) { if imgui.DragFloat("Inner Cutoff Radians", &l.InnerCutoffRad) {
whiteMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff)
containerMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff) cos := l.InnerCutoffCos()
palleteMat.SetUnifFloat32(indexString+".innerCutoff", l.InnerCutoff)
whiteMat.SetUnifFloat32(indexString+".innerCutoff", cos)
containerMat.SetUnifFloat32(indexString+".innerCutoff", cos)
palleteMat.SetUnifFloat32(indexString+".innerCutoff", cos)
} }
if imgui.DragFloat("Outer Cutoff", &l.OuterCutoff) { if imgui.DragFloat("Outer Cutoff Radians", &l.OuterCutoffRad) {
whiteMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff)
containerMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff) cos := l.OuterCutoffCos()
palleteMat.SetUnifFloat32(indexString+".outerCutoff", l.OuterCutoff)
whiteMat.SetUnifFloat32(indexString+".outerCutoff", cos)
containerMat.SetUnifFloat32(indexString+".outerCutoff", cos)
palleteMat.SetUnifFloat32(indexString+".outerCutoff", cos)
} }
imgui.DragFloat("Spot Near Plane", &l.NearPlane)
imgui.DragFloat("Spot Far Plane", &l.FarPlane)
imgui.TreePop() imgui.TreePop()
} }
@ -902,24 +959,36 @@ func (g *Game) updateCameraPos() {
var ( var (
renderDirLightShadows = true renderDirLightShadows = true
renderPointLightShadows = true renderPointLightShadows = true
renderSpotLightShadows = true
rotatingCubeSpeedDeg1 float32 = 45 rotatingCubeSpeedDeg1 float32 = 45
rotatingCubeSpeedDeg2 float32 = 120 rotatingCubeSpeedDeg2 float32 = 120
rotatingCubeSpeedDeg3 float32 = 120
rotatingCubeTrMat1 = *gglm.NewTrMatId().Translate(gglm.NewVec3(-4, -1, 4)) rotatingCubeTrMat1 = *gglm.NewTrMatId().Translate(gglm.NewVec3(-4, -1, 4))
rotatingCubeTrMat2 = *gglm.NewTrMatId().Translate(gglm.NewVec3(-1, 0.5, 4)) rotatingCubeTrMat2 = *gglm.NewTrMatId().Translate(gglm.NewVec3(-1, 0.5, 4))
rotatingCubeTrMat3 = *gglm.NewTrMatId().Translate(gglm.NewVec3(5, 0.5, 4))
) )
func (g *Game) Render() { func (g *Game) Render() {
whiteMat.SetUnifVec3("camPos", &cam.Pos)
containerMat.SetUnifVec3("camPos", &cam.Pos)
palleteMat.SetUnifVec3("camPos", &cam.Pos)
rotatingCubeTrMat1.Rotate(rotatingCubeSpeedDeg1*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(0, 1, 0)) rotatingCubeTrMat1.Rotate(rotatingCubeSpeedDeg1*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(0, 1, 0))
rotatingCubeTrMat2.Rotate(rotatingCubeSpeedDeg2*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(1, 1, 0)) rotatingCubeTrMat2.Rotate(rotatingCubeSpeedDeg2*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(1, 1, 0))
rotatingCubeTrMat3.Rotate(rotatingCubeSpeedDeg3*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(1, 1, 1))
if renderDirLightShadows { if renderDirLightShadows {
g.renderDirectionalShadowmap() g.renderDirectionalLightShadowmap()
}
if renderSpotLightShadows {
g.renderSpotLightShadowmaps()
} }
if renderPointLightShadows { if renderPointLightShadows {
g.renderOmnidirectionalShadowmap() g.renderPointLightShadowmaps()
} }
if renderToBackBuffer { if renderToBackBuffer {
@ -940,21 +1009,16 @@ func (g *Game) Render() {
} }
} }
func (g *Game) renderDirectionalShadowmap() { func (g *Game) renderDirectionalLightShadowmap() {
// Set some uniforms // Set some uniforms
dirLightProjViewMat := dirLight.GetProjViewMat() dirLightProjViewMat := dirLight.GetProjViewMat()
whiteMat.SetUnifVec3("camPos", &cam.Pos)
whiteMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat) whiteMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat)
containerMat.SetUnifVec3("camPos", &cam.Pos)
containerMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat) containerMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat)
palleteMat.SetUnifVec3("camPos", &cam.Pos)
palleteMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat) palleteMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat)
dirLightDepthMapMat.SetUnifMat4("projViewMat", &dirLightProjViewMat) depthMapMat.SetUnifMat4("projViewMat", &dirLightProjViewMat)
// Start rendering // Start rendering
dirLightDepthMapFbo.BindWithViewport() dirLightDepthMapFbo.BindWithViewport()
@ -966,7 +1030,7 @@ func (g *Game) renderDirectionalShadowmap() {
// //
// Some note that this is too troublesome and fails in many cases. Might be better to remove. // Some note that this is too troublesome and fails in many cases. Might be better to remove.
gl.CullFace(gl.FRONT) gl.CullFace(gl.FRONT)
g.RenderScene(dirLightDepthMapMat) g.RenderScene(depthMapMat)
gl.CullFace(gl.BACK) gl.CullFace(gl.BACK)
dirLightDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight)) dirLightDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight))
@ -980,10 +1044,41 @@ func (g *Game) renderDirectionalShadowmap() {
} }
} }
func (g *Game) renderOmnidirectionalShadowmap() { func (g *Game) renderSpotLightShadowmaps() {
omnidirDepthMapFbo.BindWithViewport() for i := 0; i < len(spotLights); i++ {
omnidirDepthMapFbo.Clear()
l := &spotLights[i]
indexStr := strconv.Itoa(i)
projViewMatIndexStr := "spotLightProjViewMats[" + indexStr + "]"
// Set render uniforms
projViewMat := l.GetProjViewMat()
whiteMat.SetUnifMat4(projViewMatIndexStr, &projViewMat)
containerMat.SetUnifMat4(projViewMatIndexStr, &projViewMat)
palleteMat.SetUnifMat4(projViewMatIndexStr, &projViewMat)
// Set depth uniforms
arrayDepthMapMat.SetUnifMat4("projViewMats["+indexStr+"]", &projViewMat)
}
// Render
spotLightDepthMapFbo.BindWithViewport()
spotLightDepthMapFbo.Clear()
// Front culling created issues
// gl.CullFace(gl.FRONT)
g.RenderScene(arrayDepthMapMat)
// gl.CullFace(gl.BACK)
spotLightDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight))
}
func (g *Game) renderPointLightShadowmaps() {
pointLightDepthMapFbo.BindWithViewport()
pointLightDepthMapFbo.Clear()
for i := 0; i < len(pointLights); i++ { for i := 0; i < len(pointLights); i++ {
@ -995,7 +1090,7 @@ func (g *Game) renderOmnidirectionalShadowmap() {
omnidirDepthMapMat.SetUnifFloat32("farPlane", p.FarPlane) omnidirDepthMapMat.SetUnifFloat32("farPlane", p.FarPlane)
// Set projView matrices // Set projView matrices
projViewMats := p.GetProjViewMats(float32(omnidirDepthMapFbo.Width), float32(omnidirDepthMapFbo.Height)) projViewMats := p.GetProjViewMats(float32(pointLightDepthMapFbo.Width), float32(pointLightDepthMapFbo.Height))
for j := 0; j < len(projViewMats); j++ { for j := 0; j < len(projViewMats); j++ {
omnidirDepthMapMat.SetUnifMat4("cubemapProjViewMats["+strconv.Itoa(j)+"]", &projViewMats[j]) omnidirDepthMapMat.SetUnifMat4("cubemapProjViewMats["+strconv.Itoa(j)+"]", &projViewMats[j])
} }
@ -1003,7 +1098,7 @@ func (g *Game) renderOmnidirectionalShadowmap() {
g.RenderScene(omnidirDepthMapMat) g.RenderScene(omnidirDepthMapMat)
} }
omnidirDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight)) pointLightDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight))
} }
func (g *Game) renderDemoFob() { func (g *Game) renderDemoFob() {
@ -1070,8 +1165,8 @@ func (g *Game) RenderScene(overrideMat *materials.Material) {
// Rotating cubes // Rotating cubes
window.Rend.DrawMesh(cubeMesh, &rotatingCubeTrMat1, cubeMat) window.Rend.DrawMesh(cubeMesh, &rotatingCubeTrMat1, cubeMat)
window.Rend.DrawMesh(cubeMesh, &rotatingCubeTrMat2, cubeMat) window.Rend.DrawMesh(cubeMesh, &rotatingCubeTrMat2, cubeMat)
window.Rend.DrawMesh(cubeMesh, &rotatingCubeTrMat3, cubeMat)
// Cubes generator // Cubes generator
// rowSize := 1 // rowSize := 1

View File

@ -16,8 +16,9 @@ const (
TextureSlot_Normal TextureSlot = 2 TextureSlot_Normal TextureSlot = 2
TextureSlot_Emission TextureSlot = 3 TextureSlot_Emission TextureSlot = 3
TextureSlot_Cubemap TextureSlot = 10 TextureSlot_Cubemap TextureSlot = 10
TextureSlot_ShadowMap TextureSlot = 11 TextureSlot_Cubemap_Array TextureSlot = 11
TextureSlot_Cubemap_Array TextureSlot = 12 TextureSlot_ShadowMap1 TextureSlot = 12
TextureSlot_ShadowMap_Array1 TextureSlot = 13
) )
type Material struct { type Material struct {
@ -43,6 +44,7 @@ type Material struct {
// Shadowmaps // Shadowmaps
ShadowMapTex1 uint32 ShadowMapTex1 uint32
ShadowMapTexArray1 uint32
} }
func (m *Material) Bind() { func (m *Material) Bind() {
@ -80,9 +82,14 @@ func (m *Material) Bind() {
} }
if m.ShadowMapTex1 != 0 { if m.ShadowMapTex1 != 0 {
gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_ShadowMap)) gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_ShadowMap1))
gl.BindTexture(gl.TEXTURE_2D, m.ShadowMapTex1) gl.BindTexture(gl.TEXTURE_2D, m.ShadowMapTex1)
} }
if m.ShadowMapTexArray1 != 0 {
gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_ShadowMap_Array1))
gl.BindTexture(gl.TEXTURE_2D_ARRAY, m.ShadowMapTexArray1)
}
} }
func (m *Material) UnBind() { func (m *Material) UnBind() {

View File

@ -0,0 +1,54 @@
//shader:vertex
#version 410
layout(location=0) in vec3 vertPosIn;
uniform mat4 modelMat;
void main()
{
gl_Position = modelMat * vec4(vertPosIn, 1);
}
//shader:geometry
#version 410
layout (triangles) in;
#define NUM_PROJ_VIEW_MATS 4
// 3 * NUM_PROJ_VIEW_MATS
layout (triangle_strip, max_vertices=12) out;
// This is the same number as max spot lights or whatever else is being rendered
uniform mat4 projViewMats[NUM_PROJ_VIEW_MATS];
out vec4 FragPos;
void main()
{
for(int projViewMatIndex = 0; projViewMatIndex < NUM_PROJ_VIEW_MATS; projViewMatIndex++){
gl_Layer = projViewMatIndex;
mat4 projViewMat = projViewMats[projViewMatIndex];
for(int i = 0; i < 3; i++)
{
FragPos = gl_in[i].gl_Position;
gl_Position = projViewMat * FragPos;
EmitVertex();
}
EndPrimitive();
}
}
//shader:fragment
#version 410
in vec4 FragPos;
void main()
{
// This implicitly writes to the depth buffer with no color operations
// Equivalent: gl_FragDepth = gl_FragCoord.z;
}

View File

@ -6,15 +6,19 @@ layout(location=1) in vec3 vertNormalIn;
layout(location=2) in vec2 vertUV0In; layout(location=2) in vec2 vertUV0In;
layout(location=3) in vec3 vertColorIn; layout(location=3) in vec3 vertColorIn;
uniform mat4 modelMat;
uniform mat4 projViewMat;
uniform mat4 dirLightProjViewMat;
#define NUM_SPOT_LIGHTS 4
uniform mat4 spotLightProjViewMats[NUM_SPOT_LIGHTS];
out vec3 vertNormal; out vec3 vertNormal;
out vec2 vertUV0; out vec2 vertUV0;
out vec3 vertColor; out vec3 vertColor;
out vec3 fragPos; out vec3 fragPos;
out vec4 fragPosDirLight; out vec4 fragPosDirLight;
out vec4 fragPosSpotLight[NUM_SPOT_LIGHTS];
uniform mat4 modelMat;
uniform mat4 projViewMat;
uniform mat4 dirLightProjViewMat;
void main() void main()
{ {
@ -31,6 +35,9 @@ void main()
fragPos = modelVert.xyz; fragPos = modelVert.xyz;
fragPosDirLight = dirLightProjViewMat * vec4(fragPos, 1); fragPosDirLight = dirLightProjViewMat * vec4(fragPos, 1);
for (int i = 0; i < NUM_SPOT_LIGHTS; i++)
fragPosSpotLight[i] = spotLightProjViewMats[i] * vec4(fragPos, 1);
gl_Position = projViewMat * modelVert; gl_Position = projViewMat * modelVert;
} }
@ -81,6 +88,7 @@ struct SpotLight {
#define NUM_SPOT_LIGHTS 4 #define NUM_SPOT_LIGHTS 4
uniform SpotLight spotLights[NUM_SPOT_LIGHTS]; uniform SpotLight spotLights[NUM_SPOT_LIGHTS];
uniform sampler2DArray spotLightShadowMaps;
uniform vec3 camPos; uniform vec3 camPos;
uniform vec3 ambientColor = vec3(0.2, 0.2, 0.2); uniform vec3 ambientColor = vec3(0.2, 0.2, 0.2);
@ -90,6 +98,7 @@ in vec3 vertNormal;
in vec2 vertUV0; in vec2 vertUV0;
in vec3 fragPos; in vec3 fragPos;
in vec4 fragPosDirLight; in vec4 fragPosDirLight;
in vec4 fragPosSpotLight[NUM_SPOT_LIGHTS];
out vec4 fragColor; out vec4 fragColor;
@ -123,9 +132,9 @@ float CalcDirShadow(sampler2D shadowMap, vec3 lightDir)
// Basically get soft shadows by averaging this texel and surrounding ones // Basically get soft shadows by averaging this texel and surrounding ones
float shadow = 0; float shadow = 0;
vec2 texelSize = 1 / textureSize(shadowMap, 0); vec2 texelSize = 1 / textureSize(shadowMap, 0);
for(int x = -1; x <= 1; ++x) for(int x = -1; x <= 1; x++)
{ {
for(int y = -1; y <= 1; ++y) for(int y = -1; y <= 1; y++)
{ {
float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
@ -205,7 +214,47 @@ vec3 CalcPointLight(PointLight pointLight, int lightIndex)
return (finalDiffuse + finalSpecular) * attenuation * (1 - shadow); return (finalDiffuse + finalSpecular) * attenuation * (1 - shadow);
} }
vec3 CalcSpotLight(SpotLight light) float CalcSpotShadow(vec3 lightDir, int lightIndex)
{
// Move from clip space to NDC
vec3 projCoords = fragPosSpotLight[lightIndex].xyz / fragPosSpotLight[lightIndex].w;
// Move from [-1,1] to [0, 1]
projCoords = projCoords * 0.5 + 0.5;
// If sampling outside the depth texture then force 'no shadow'
if(projCoords.z > 1)
return 0;
// currentDepth is the fragment depth from the light's perspective
float currentDepth = projCoords.z;
// Bias in the range [0.005, 0.05] depending on the angle, where a higher
// angle gives a higher bias, as shadow acne gets worse with angle
float bias = max(0.05 * (1 - dot(normalizedVertNorm, lightDir)), 0.005);
// 'Percentage Close Filtering'.
// Basically get soft shadows by averaging this texel and surrounding ones
float shadow = 0;
vec2 texelSize = 1 / textureSize(spotLightShadowMaps, 0).xy;
for(int x = -1; x <= 1; x++)
{
for(int y = -1; y <= 1; y++)
{
float pcfDepth = texture(spotLightShadowMaps, vec3(projCoords.xy + vec2(x, y) * texelSize, lightIndex)).r;
// If our depth is larger than the lights closest depth at the texel we checked (projCoords),
// then there is something closer to the light than us, and so we are in shadow
shadow += currentDepth - bias > pcfDepth ? 1 : 0;
}
}
shadow /= 9;
return shadow;
}
vec3 CalcSpotLight(SpotLight light, int lightIndex)
{ {
if (light.innerCutoff == 0) if (light.innerCutoff == 0)
return vec3(0); return vec3(0);
@ -231,7 +280,10 @@ vec3 CalcSpotLight(SpotLight light)
float specularAmount = pow(max(dot(normalizedVertNorm, halfwayDir), 0.0), material.shininess); float specularAmount = pow(max(dot(normalizedVertNorm, halfwayDir), 0.0), material.shininess);
vec3 finalSpecular = specularAmount * light.specularColor * specularTexColor.rgb; vec3 finalSpecular = specularAmount * light.specularColor * specularTexColor.rgb;
return (finalDiffuse + finalSpecular) * intensity; // Shadow
float shadow = CalcSpotShadow(fragToLightDir, lightIndex);
return (finalDiffuse + finalSpecular) * intensity * (1 - shadow);
} }
void main() void main()
@ -254,7 +306,7 @@ void main()
for (int i = 0; i < NUM_SPOT_LIGHTS; i++) for (int i = 0; i < NUM_SPOT_LIGHTS; i++)
{ {
finalColor += CalcSpotLight(spotLights[i]); finalColor += CalcSpotLight(spotLights[i], i);
} }
vec3 finalEmission = emissionTexColor.rgb; vec3 finalEmission = emissionTexColor.rgb;