Compare commits

...

4 Commits

Author SHA1 Message Date
abd7079e61 Correct modifier keys input to imgui 2024-04-24 19:56:35 +04:00
4d8ccdaf56 Captured/uncaptured mode in input package+comments 2024-04-20 11:53:06 +04:00
a131e1b52d Add todo regarding input package 2024-04-20 11:14:29 +04:00
f35c217d73 Spotlight shadows 2024-04-16 10:34:30 +04:00
8 changed files with 565 additions and 156 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,

View File

@ -33,25 +33,12 @@ type Window struct {
func (w *Window) handleInputs() { func (w *Window) handleInputs() {
input.EventLoopStart()
imIo := imgui.CurrentIO() imIo := imgui.CurrentIO()
imguiCaptureMouse := imIo.WantCaptureMouse() imguiCaptureMouse := imIo.WantCaptureMouse()
imguiCaptureKeyboard := imIo.WantCaptureKeyboard() imguiCaptureKeyboard := imIo.WantCaptureKeyboard()
// These two are to fix a bug where state isn't cleared input.EventLoopStart(imguiCaptureMouse, imguiCaptureKeyboard)
// even after imgui captures the keyboard/mouse.
//
// For example, if player is moving due to key held and then imgui captures the keyboard,
// the player keeps moving even when the key is no longer pressed because the input system never
// receives the key up event.
if imguiCaptureMouse {
input.ClearMouseState()
}
if imguiCaptureKeyboard {
input.ClearKeyboardState()
}
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
@ -65,46 +52,27 @@ func (w *Window) handleInputs() {
case *sdl.MouseWheelEvent: case *sdl.MouseWheelEvent:
if !imguiCaptureMouse { input.HandleMouseWheelEvent(e)
input.HandleMouseWheelEvent(e)
}
imIo.AddMouseWheelDelta(float32(e.X), float32(e.Y)) imIo.AddMouseWheelDelta(float32(e.X), float32(e.Y))
case *sdl.KeyboardEvent: case *sdl.KeyboardEvent:
if !imguiCaptureKeyboard { input.HandleKeyboardEvent(e)
input.HandleKeyboardEvent(e)
} // Send modifier key updates to imgui (based on the imgui SDL backend)
imIo.AddKeyEvent(imgui.ModCtrl, e.Keysym.Mod&sdl.KMOD_CTRL != 0)
imIo.AddKeyEvent(imgui.ModShift, e.Keysym.Mod&sdl.KMOD_SHIFT != 0)
imIo.AddKeyEvent(imgui.ModAlt, e.Keysym.Mod&sdl.KMOD_ALT != 0)
imIo.AddKeyEvent(imgui.ModSuper, e.Keysym.Mod&sdl.KMOD_GUI != 0)
imIo.AddKeyEvent(nmageimgui.SdlScancodeToImGuiKey(e.Keysym.Scancode), e.Type == sdl.KEYDOWN) imIo.AddKeyEvent(nmageimgui.SdlScancodeToImGuiKey(e.Keysym.Scancode), e.Type == sdl.KEYDOWN)
// Send modifier key updates to imgui
if e.Keysym.Sym == sdl.K_LCTRL || e.Keysym.Sym == sdl.K_RCTRL {
imIo.SetKeyCtrl(e.Type == sdl.KEYDOWN)
}
if e.Keysym.Sym == sdl.K_LSHIFT || e.Keysym.Sym == sdl.K_RSHIFT {
imIo.SetKeyShift(e.Type == sdl.KEYDOWN)
}
if e.Keysym.Sym == sdl.K_LALT || e.Keysym.Sym == sdl.K_RALT {
imIo.SetKeyAlt(e.Type == sdl.KEYDOWN)
}
if e.Keysym.Sym == sdl.K_LGUI || e.Keysym.Sym == sdl.K_RGUI {
imIo.SetKeySuper(e.Type == sdl.KEYDOWN)
}
case *sdl.TextInputEvent: case *sdl.TextInputEvent:
imIo.AddInputCharactersUTF8(e.GetText()) imIo.AddInputCharactersUTF8(e.GetText())
case *sdl.MouseButtonEvent: case *sdl.MouseButtonEvent:
if !imguiCaptureMouse { input.HandleMouseBtnEvent(e)
input.HandleMouseBtnEvent(e)
}
isPressed := e.State == sdl.PRESSED isPressed := e.State == sdl.PRESSED
if e.Button == sdl.BUTTON_LEFT { if e.Button == sdl.BUTTON_LEFT {
@ -117,9 +85,7 @@ func (w *Window) handleInputs() {
case *sdl.MouseMotionEvent: case *sdl.MouseMotionEvent:
if !imguiCaptureMouse { input.HandleMouseMotionEvent(e)
input.HandleMouseMotionEvent(e)
}
case *sdl.WindowEvent: case *sdl.WindowEvent:

View File

@ -1,6 +1,23 @@
// The input package provides an interface to mouse and keyboard inputs
// like key clicks and releases, along with some higher level constructs like
// pressed/released this frames, double clicks, and normalized inputs.
//
// The input package has two sets of functions for most cases, where one
// is in the form 'xy' and the other 'xyCaptured'. The captured form
// always returns normal events even if the mouse or keyboard are captured
// by the UI system. The 'xy' form however will return zero/false if the
// respective input device is currently captured (with the exception of mouse position, that is always correctly returned).
//
// For most cases, you want to use the 'xy' form. For example, you only want to receive
// key down events for game character movement when the UI isn't capturing the keyboard,
// because otherwise the character will move while typing in a UI textbox.
//
// The functions IsMouseCaptured and IsKeyboardCaptured are also available.
package input package input
import "github.com/veandco/go-sdl2/sdl" import (
"github.com/veandco/go-sdl2/sdl"
)
type keyState struct { type keyState struct {
Key sdl.Keycode Key sdl.Keycode
@ -31,15 +48,22 @@ type mouseWheelState struct {
} }
var ( var (
keyMap = make(map[sdl.Keycode]keyState) mouseWheel = mouseWheelState{}
mouseBtnMap = make(map[int]mouseBtnState) mouseMotion = mouseMotionState{}
mouseMotion = mouseMotionState{} mouseBtnMap = make(map[int]mouseBtnState)
mouseWheel = mouseWheelState{} keyMap = make(map[sdl.Keycode]keyState)
quitRequested bool
isQuitRequested bool
isMouseCaptured bool
isKeyboardCaptured bool
) )
func EventLoopStart() { func EventLoopStart(mouseGotCaptured, keyboardGotCaptured bool) {
isMouseCaptured = mouseGotCaptured
isKeyboardCaptured = keyboardGotCaptured
// Update per-frame state
for k, v := range keyMap { for k, v := range keyMap {
v.IsPressedThisFrame = false v.IsPressedThisFrame = false
v.IsReleasedThisFrame = false v.IsReleasedThisFrame = false
@ -59,7 +83,7 @@ func EventLoopStart() {
mouseWheel.XDelta = 0 mouseWheel.XDelta = 0
mouseWheel.YDelta = 0 mouseWheel.YDelta = 0
quitRequested = false isQuitRequested = false
} }
func ClearKeyboardState() { func ClearKeyboardState() {
@ -73,11 +97,19 @@ func ClearMouseState() {
} }
func HandleQuitEvent(e *sdl.QuitEvent) { func HandleQuitEvent(e *sdl.QuitEvent) {
quitRequested = true isQuitRequested = true
}
func IsMouseCaptured() bool {
return isMouseCaptured
}
func IsKeyboardCaptured() bool {
return isKeyboardCaptured
} }
func IsQuitClicked() bool { func IsQuitClicked() bool {
return quitRequested return isQuitRequested
} }
func HandleKeyboardEvent(e *sdl.KeyboardEvent) { func HandleKeyboardEvent(e *sdl.KeyboardEvent) {
@ -123,18 +155,36 @@ func HandleMouseWheelEvent(e *sdl.MouseWheelEvent) {
mouseWheel.YDelta = e.Y mouseWheel.YDelta = e.Y
} }
// GetMousePos returns the window coordinates of the mouse // GetMousePos returns the window coordinates of the mouse regardless of whether the mouse is captured or not
func GetMousePos() (x, y int32) { func GetMousePos() (x, y int32) {
return mouseMotion.XPos, mouseMotion.YPos return mouseMotion.XPos, mouseMotion.YPos
} }
// GetMouseMotion returns how many pixels were moved last frame // GetMouseMotion returns how many pixels were moved last frame
func GetMouseMotion() (xDelta, yDelta int32) { func GetMouseMotion() (xDelta, yDelta int32) {
if isMouseCaptured {
return 0, 0
}
return GetMouseMotionCaptured()
}
func GetMouseMotionCaptured() (xDelta, yDelta int32) {
return mouseMotion.XDelta, mouseMotion.YDelta return mouseMotion.XDelta, mouseMotion.YDelta
} }
func GetMouseMotionNorm() (xDelta, yDelta int32) { func GetMouseMotionNorm() (xDelta, yDelta int32) {
if isMouseCaptured {
return 0, 0
}
return GetMouseMotionNormCaptured()
}
func GetMouseMotionNormCaptured() (xDelta, yDelta int32) {
x, y := mouseMotion.XDelta, mouseMotion.YDelta x, y := mouseMotion.XDelta, mouseMotion.YDelta
if x > 0 { if x > 0 {
x = 1 x = 1
@ -152,12 +202,31 @@ func GetMouseMotionNorm() (xDelta, yDelta int32) {
} }
func GetMouseWheelMotion() (xDelta, yDelta int32) { func GetMouseWheelMotion() (xDelta, yDelta int32) {
if isMouseCaptured {
return 0, 0
}
return GetMouseWheelMotionCaptured()
}
func GetMouseWheelMotionCaptured() (xDelta, yDelta int32) {
return mouseWheel.XDelta, mouseWheel.YDelta return mouseWheel.XDelta, mouseWheel.YDelta
} }
// GetMouseWheelXNorm returns 1 if mouse wheel xDelta > 0, -1 if xDelta < 0, and 0 otherwise // GetMouseWheelXNorm returns 1 if mouse wheel xDelta > 0, -1 if xDelta < 0, and 0 otherwise
func GetMouseWheelXNorm() int32 { func GetMouseWheelXNorm() int32 {
if isMouseCaptured {
return 0
}
return GetMouseWheelXNormCaptured()
}
// GetMouseWheelXNormCaptured returns 1 if mouse wheel xDelta > 0, -1 if xDelta < 0, and 0 otherwise
func GetMouseWheelXNormCaptured() int32 {
if mouseWheel.XDelta > 0 { if mouseWheel.XDelta > 0 {
return 1 return 1
} else if mouseWheel.XDelta < 0 { } else if mouseWheel.XDelta < 0 {
@ -167,9 +236,19 @@ func GetMouseWheelXNorm() int32 {
return 0 return 0
} }
// returns 1 if mouse wheel yDelta > 0, -1 if yDelta < 0, and 0 otherwise // GetMouseWheelYNorm returns 1 if mouse wheel yDelta > 0, -1 if yDelta < 0, and 0 otherwise
func GetMouseWheelYNorm() int32 { func GetMouseWheelYNorm() int32 {
if isMouseCaptured {
return 0
}
return GetMouseWheelYNormCaptured()
}
// GetMouseWheelYNormCaptured returns 1 if mouse wheel yDelta > 0, -1 if yDelta < 0, and 0 otherwise
func GetMouseWheelYNormCaptured() int32 {
if mouseWheel.YDelta > 0 { if mouseWheel.YDelta > 0 {
return 1 return 1
} else if mouseWheel.YDelta < 0 { } else if mouseWheel.YDelta < 0 {
@ -181,6 +260,15 @@ func GetMouseWheelYNorm() int32 {
func KeyClicked(kc sdl.Keycode) bool { func KeyClicked(kc sdl.Keycode) bool {
if isKeyboardCaptured {
return false
}
return KeyClickedCaptured(kc)
}
func KeyClickedCaptured(kc sdl.Keycode) bool {
ks, ok := keyMap[kc] ks, ok := keyMap[kc]
if !ok { if !ok {
return false return false
@ -191,6 +279,15 @@ func KeyClicked(kc sdl.Keycode) bool {
func KeyReleased(kc sdl.Keycode) bool { func KeyReleased(kc sdl.Keycode) bool {
if isKeyboardCaptured {
return false
}
return KeyReleasedCaptured(kc)
}
func KeyReleasedCaptured(kc sdl.Keycode) bool {
ks, ok := keyMap[kc] ks, ok := keyMap[kc]
if !ok { if !ok {
return false return false
@ -201,6 +298,15 @@ func KeyReleased(kc sdl.Keycode) bool {
func KeyDown(kc sdl.Keycode) bool { func KeyDown(kc sdl.Keycode) bool {
if isKeyboardCaptured {
return false
}
return KeyDownCaptured(kc)
}
func KeyDownCaptured(kc sdl.Keycode) bool {
ks, ok := keyMap[kc] ks, ok := keyMap[kc]
if !ok { if !ok {
return false return false
@ -211,6 +317,15 @@ func KeyDown(kc sdl.Keycode) bool {
func KeyUp(kc sdl.Keycode) bool { func KeyUp(kc sdl.Keycode) bool {
if isKeyboardCaptured {
return false
}
return KeyUpCaptured(kc)
}
func KeyUpCaptured(kc sdl.Keycode) bool {
ks, ok := keyMap[kc] ks, ok := keyMap[kc]
if !ok { if !ok {
return true return true
@ -221,6 +336,15 @@ func KeyUp(kc sdl.Keycode) bool {
func MouseClicked(mb int) bool { func MouseClicked(mb int) bool {
if isMouseCaptured {
return false
}
return MouseClickedCaptued(mb)
}
func MouseClickedCaptued(mb int) bool {
btn, ok := mouseBtnMap[mb] btn, ok := mouseBtnMap[mb]
if !ok { if !ok {
return false return false
@ -231,6 +355,15 @@ func MouseClicked(mb int) bool {
func MouseDoubleClicked(mb int) bool { func MouseDoubleClicked(mb int) bool {
if isMouseCaptured {
return false
}
return MouseDoubleClickedCaptured(mb)
}
func MouseDoubleClickedCaptured(mb int) bool {
btn, ok := mouseBtnMap[mb] btn, ok := mouseBtnMap[mb]
if !ok { if !ok {
return false return false
@ -240,6 +373,16 @@ func MouseDoubleClicked(mb int) bool {
} }
func MouseReleased(mb int) bool { func MouseReleased(mb int) bool {
if isMouseCaptured {
return false
}
return MouseReleasedCaptured(mb)
}
func MouseReleasedCaptured(mb int) bool {
btn, ok := mouseBtnMap[mb] btn, ok := mouseBtnMap[mb]
if !ok { if !ok {
return false return false
@ -250,6 +393,15 @@ func MouseReleased(mb int) bool {
func MouseDown(mb int) bool { func MouseDown(mb int) bool {
if isMouseCaptured {
return false
}
return MouseDownCaptued(mb)
}
func MouseDownCaptued(mb int) bool {
btn, ok := mouseBtnMap[mb] btn, ok := mouseBtnMap[mb]
if !ok { if !ok {
return false return false
@ -260,6 +412,15 @@ func MouseDown(mb int) bool {
func MouseUp(mb int) bool { func MouseUp(mb int) bool {
if isMouseCaptured {
return false
}
return MouseUpCaptured(mb)
}
func MouseUpCaptured(mb int) bool {
btn, ok := mouseBtnMap[mb] btn, ok := mouseBtnMap[mb]
if !ok { if !ok {
return true return true

258
main.go
View File

@ -32,10 +32,12 @@ 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
- In some cases we DO want input even when captured by UI. We need two systems within input package, one filtered and one not
- Proper model loading (i.e. load model by reading all its meshes, textures, and so on together) - Proper model loading (i.e. load model by reading all its meshes, textures, and so on together)
- Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing) ✅ - Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing) ✅
- Renderer batching - Renderer batching
@ -70,7 +72,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 +95,9 @@ type PointLight struct {
const ( const (
MaxPointLights = 8 MaxPointLights = 8
// If this changes update the array depth map shader
MaxSpotLights = 4
) )
var ( var (
@ -117,24 +122,42 @@ func (p *PointLight) GetProjViewMats(shadowMapWidth, shadowMapHeight float32) [6
} }
type SpotLight struct { type SpotLight struct {
Pos gglm.Vec3 Pos gglm.Vec3
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,32 +176,36 @@ 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
unlitMat *materials.Material unlitMat *materials.Material
whiteMat *materials.Material whiteMat *materials.Material
containerMat *materials.Material containerMat *materials.Material
palleteMat *materials.Material palleteMat *materials.Material
skyboxMat *materials.Material skyboxMat *materials.Material
dirLightDepthMapMat *materials.Material depthMapMat *materials.Material
omnidirDepthMapMat *materials.Material arrayDepthMapMat *materials.Material
debugDepthMat *materials.Material omnidirDepthMapMat *materials.Material
debugDepthMat *materials.Material
cubeMesh *meshes.Mesh cubeMesh *meshes.Mesh
sphereMesh *meshes.Mesh sphereMesh *meshes.Mesh
@ -245,13 +272,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 +472,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 +491,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 +509,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 +575,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 +638,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 +667,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 +690,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 +810,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 +849,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 +960,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 +1010,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 +1031,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 +1045,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 +1091,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 +1099,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 +1166,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

@ -11,13 +11,14 @@ import (
type TextureSlot uint32 type TextureSlot uint32
const ( const (
TextureSlot_Diffuse TextureSlot = 0 TextureSlot_Diffuse TextureSlot = 0
TextureSlot_Specular TextureSlot = 1 TextureSlot_Specular TextureSlot = 1
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 {
@ -42,7 +43,8 @@ type Material struct {
CubemapArrayTex uint32 CubemapArrayTex uint32
// 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;