From fbfcbaa156b674fdb08123dc1251eef40035d853 Mon Sep 17 00:00:00 2001 From: bloeys Date: Mon, 15 Apr 2024 10:49:18 +0400 Subject: [PATCH] Point light shadows+cubemap array fbo+cleanup --- buffers/framebuffer.go | 89 ++++++++- engine/engine.go | 6 + main.go | 219 ++++++++++++++------- materials/material.go | 30 +-- res/shaders/omnidirectional-depth-map.glsl | 7 +- res/shaders/simple.glsl | 37 +++- 6 files changed, 294 insertions(+), 94 deletions(-) diff --git a/buffers/framebuffer.go b/buffers/framebuffer.go index b8eeb80..bdbda3a 100755 --- a/buffers/framebuffer.go +++ b/buffers/framebuffer.go @@ -1,6 +1,7 @@ package buffers import ( + "github.com/bloeys/nmage/assert" "github.com/bloeys/nmage/logging" "github.com/go-gl/gl/v4.1-core/gl" ) @@ -12,6 +13,7 @@ const ( FramebufferAttachmentType_Texture FramebufferAttachmentType_Renderbuffer FramebufferAttachmentType_Cubemap + FramebufferAttachmentType_Cubemap_Array ) func (f FramebufferAttachmentType) IsValid() bool { @@ -22,6 +24,8 @@ func (f FramebufferAttachmentType) IsValid() bool { case FramebufferAttachmentType_Renderbuffer: fallthrough case FramebufferAttachmentType_Cubemap: + fallthrough + case FramebufferAttachmentType_Cubemap_Array: return true default: @@ -172,7 +176,7 @@ func (fbo *Framebuffer) NewColorAttachment( logging.ErrLog.Fatalf("failed creating color attachment for framebuffer due to unknown attachment type. Type=%d\n", attachType) } - if attachType == FramebufferAttachmentType_Cubemap { + if attachType == FramebufferAttachmentType_Cubemap || attachType == FramebufferAttachmentType_Cubemap_Array { 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") } @@ -263,6 +267,10 @@ func (fbo *Framebuffer) NewDepthAttachment( 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) } + if attachType == FramebufferAttachmentType_Cubemap_Array { + logging.ErrLog.Fatalf("failed creating cubemap array depth attachment because 'NewDepthCubemapArrayAttachment' must be used for that\n") + } + a := FramebufferAttachment{ Type: attachType, Format: attachFormat, @@ -342,6 +350,63 @@ func (fbo *Framebuffer) NewDepthAttachment( fbo.Attachments = append(fbo.Attachments, a) } +func (fbo *Framebuffer) NewDepthCubemapArrayAttachment( + attachFormat FramebufferAttachmentDataFormat, + numCubemaps int32, +) { + + if fbo.HasDepthAttachment() { + logging.ErrLog.Fatalf("failed creating cubemap 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_Cubemap_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_CUBE_MAP_ARRAY, a.Id) + + gl.TexImage3D( + gl.TEXTURE_CUBE_MAP_ARRAY, + 0, + attachFormat.GlInternalFormat(), + int32(fbo.Width), + int32(fbo.Height), + 6*numCubemaps, + 0, + attachFormat.GlFormat(), + gl.FLOAT, + nil, + ) + + gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE) + + gl.BindTexture(gl.TEXTURE_2D, 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( attachType FramebufferAttachmentType, attachFormat FramebufferAttachmentDataFormat, @@ -405,6 +470,28 @@ func (fbo *Framebuffer) NewDepthStencilAttachment( fbo.Attachments = append(fbo.Attachments, a) } +// SetCubemapArrayLayerFace 'binds' a single face of a cubemap from the cubemap +// array to the fbo, such that rendering only affects that one face and the others inaccessible. +// +// If this is not called, the default is that the entire cubemap array and all the faces in it +// are bound and available for use when binding the fbo. +func (fbo *Framebuffer) SetCubemapArrayLayerFace(layerFace int32) { + + for i := 0; i < len(fbo.Attachments); i++ { + + a := &fbo.Attachments[i] + if a.Type != FramebufferAttachmentType_Cubemap_Array { + continue + } + + assert.T(a.Format.IsDepthFormat(), "SetCubemapFromArray called but a cubemap array is set on a color attachment, which is not currently handled. Code must be updated!") + gl.FramebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, a.Id, 0, layerFace) + return + } + + logging.ErrLog.Fatalf("SetCubemapFromArray failed because no cubemap array attachment was found on fbo. Fbo=%+v\n", *fbo) +} + func (fbo *Framebuffer) Delete() { if fbo.Id == 0 { diff --git a/engine/engine.go b/engine/engine.go index 1d2fe2a..44dbfce 100755 --- a/engine/engine.go +++ b/engine/engine.go @@ -217,6 +217,7 @@ func createWindow(title string, x, y, width, height int32, flags WindowFlags, re if err != nil { return nil, err } + win := &Window{ SDLWin: sdlWin, EventCallbacks: make([]func(sdl.Event), 0), @@ -233,6 +234,10 @@ func createWindow(title string, x, y, width, height int32, flags WindowFlags, re return nil, err } + // Get rid of the blinding white startup screen (unfortunately there is still one frame of white) + gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) + sdlWin.GLSwap() + return win, err } @@ -254,6 +259,7 @@ func initOpenGL() error { gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) gl.ClearColor(0, 0, 0, 1) + return nil } diff --git a/main.go b/main.go index e68bc4a..bb8644c 100755 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ import ( - Point lights ✅ - Spotlights ✅ - Directional light shadows ✅ - - Point light shadows + - Point light shadows ✅ - Spotlight shadows - HDR - Cascaded shadow mapping @@ -81,22 +81,28 @@ type PointLight struct { DiffuseColor gglm.Vec3 SpecularColor gglm.Vec3 + // @TODO Radius float32 Constant float32 Linear float32 Quadratic float32 + + FarPlane float32 } +const ( + MaxPointLights = 8 +) + var ( pointLightNear float32 = 1 - pointLightFar float32 = 25 ) func (p *PointLight) GetProjViewMats(shadowMapWidth, shadowMapHeight float32) [6]gglm.Mat4 { aspect := float32(shadowMapWidth) / float32(shadowMapHeight) - projMat := gglm.Perspective(90*gglm.Deg2Rad, aspect, pointLightNear, pointLightFar) + projMat := gglm.Perspective(90*gglm.Deg2Rad, aspect, pointLightNear, p.FarPlane) projViewMats := [6]gglm.Mat4{ *projMat.Clone().Mul(&gglm.LookAtRH(&p.Pos, gglm.NewVec3(1, 0, 0).Add(&p.Pos), gglm.NewVec3(0, -1, 0)).Mat4), @@ -146,17 +152,22 @@ var ( yaw float32 = -1.5 cam *camera.Camera + // Demo fbo renderToDemoFbo = true renderToBackBuffer = true demoFboScale = gglm.NewVec2(0.25, 0.25) demoFboOffset = gglm.NewVec2(0.75, -0.75) demoFbo buffers.Framebuffer + // Dir light fbo showDirLightDepthMapFbo = true dirLightDepthMapFboScale = gglm.NewVec2(0.25, 0.25) dirLightDepthMapFboOffset = gglm.NewVec2(0.75, -0.2) dirLightDepthMapFbo buffers.Framebuffer + // Point light fbo + omnidirDepthMapFbo buffers.Framebuffer + screenQuadVao buffers.VertexArray screenQuadMat *materials.Material @@ -194,13 +205,15 @@ var ( } pointLights = [...]PointLight{ { - Pos: *gglm.NewVec3(0, 5, 0), + Pos: *gglm.NewVec3(0, 2, -2), DiffuseColor: *gglm.NewVec3(1, 0, 0), SpecularColor: *gglm.NewVec3(1, 1, 1), // These values are for 50m range Constant: 1.0, Linear: 0.09, Quadratic: 0.032, + + FarPlane: 25, }, { Pos: *gglm.NewVec3(0, -5, 0), @@ -209,6 +222,7 @@ var ( Constant: 1.0, Linear: 0.09, Quadratic: 0.032, + FarPlane: 25, }, { Pos: *gglm.NewVec3(5, 0, 0), @@ -217,19 +231,21 @@ var ( Constant: 1.0, Linear: 0.09, Quadratic: 0.032, + FarPlane: 25, }, { - Pos: *gglm.NewVec3(-4, 0, 0), - DiffuseColor: *gglm.NewVec3(0, 0, 1), + Pos: *gglm.NewVec3(-3, 4, 3), + DiffuseColor: *gglm.NewVec3(1, 1, 1), SpecularColor: *gglm.NewVec3(1, 1, 1), Constant: 1.0, Linear: 0.09, Quadratic: 0.032, + FarPlane: 25, }, } spotLights = [...]SpotLight{ { - Pos: *gglm.NewVec3(0, 5, 0), + Pos: *gglm.NewVec3(2, 5, 5), Dir: *gglm.NewVec3(0, -1, 0), DiffuseColor: *gglm.NewVec3(0, 1, 1), SpecularColor: *gglm.NewVec3(1, 1, 1), @@ -427,6 +443,7 @@ func (g *Game) Init() { whiteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) whiteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) whiteMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap)) + whiteMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array)) containerMat = materials.NewMaterial("Container mat", "./res/shaders/simple.glsl") containerMat.Shininess = 64 @@ -444,6 +461,7 @@ func (g *Game) Init() { containerMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) containerMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) containerMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap)) + containerMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array)) palleteMat = materials.NewMaterial("Pallete mat", "./res/shaders/simple.glsl") palleteMat.Shininess = 64 @@ -460,6 +478,7 @@ func (g *Game) Init() { palleteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) palleteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) palleteMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap)) + palleteMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array)) debugDepthMat = materials.NewMaterial("Debug depth mat", "./res/shaders/debug-depth.glsl") @@ -522,54 +541,62 @@ func (g *Game) initFbos() { assert.T(dirLightDepthMapFbo.IsComplete(), "Depth map fbo is not complete after init") // Cubemap fbo - cubemapFbo := buffers.NewFramebuffer(1024, 1024) - cubemapFbo.SetNoColorBuffer() - cubemapFbo.NewDepthAttachment( - buffers.FramebufferAttachmentType_Cubemap, + omnidirDepthMapFbo = buffers.NewFramebuffer(1024, 1024) + omnidirDepthMapFbo.SetNoColorBuffer() + omnidirDepthMapFbo.NewDepthCubemapArrayAttachment( buffers.FramebufferAttachmentDataFormat_DepthF32, + MaxPointLights, ) - assert.T(cubemapFbo.IsComplete(), "Cubemap fbo is not complete after init") + assert.T(omnidirDepthMapFbo.IsComplete(), "Cubemap fbo is not complete after init") } func (g *Game) updateLights() { // Directional light - whiteMat.ShadowMap = dirLightDepthMapFbo.Attachments[0].Id - containerMat.ShadowMap = dirLightDepthMapFbo.Attachments[0].Id - palleteMat.ShadowMap = dirLightDepthMapFbo.Attachments[0].Id + whiteMat.ShadowMapTex1 = dirLightDepthMapFbo.Attachments[0].Id + containerMat.ShadowMapTex1 = dirLightDepthMapFbo.Attachments[0].Id + palleteMat.ShadowMapTex1 = dirLightDepthMapFbo.Attachments[0].Id // Point lights for i := 0; i < len(pointLights); i++ { - pl := &pointLights[i] + p := &pointLights[i] indexString := "pointLights[" + strconv.Itoa(i) + "]" - whiteMat.SetUnifVec3(indexString+".pos", &pl.Pos) - containerMat.SetUnifVec3(indexString+".pos", &pl.Pos) - palleteMat.SetUnifVec3(indexString+".pos", &pl.Pos) + whiteMat.SetUnifVec3(indexString+".pos", &p.Pos) + containerMat.SetUnifVec3(indexString+".pos", &p.Pos) + palleteMat.SetUnifVec3(indexString+".pos", &p.Pos) - whiteMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) - containerMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) - palleteMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) + whiteMat.SetUnifVec3(indexString+".diffuseColor", &p.DiffuseColor) + containerMat.SetUnifVec3(indexString+".diffuseColor", &p.DiffuseColor) + palleteMat.SetUnifVec3(indexString+".diffuseColor", &p.DiffuseColor) - whiteMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) - containerMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) - palleteMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) + whiteMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) + containerMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) + palleteMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) - whiteMat.SetUnifFloat32(indexString+".constant", pl.Constant) - containerMat.SetUnifFloat32(indexString+".constant", pl.Constant) - palleteMat.SetUnifFloat32(indexString+".constant", pl.Constant) + whiteMat.SetUnifFloat32(indexString+".constant", p.Constant) + containerMat.SetUnifFloat32(indexString+".constant", p.Constant) + palleteMat.SetUnifFloat32(indexString+".constant", p.Constant) - whiteMat.SetUnifFloat32(indexString+".linear", pl.Linear) - containerMat.SetUnifFloat32(indexString+".linear", pl.Linear) - palleteMat.SetUnifFloat32(indexString+".linear", pl.Linear) + whiteMat.SetUnifFloat32(indexString+".linear", p.Linear) + containerMat.SetUnifFloat32(indexString+".linear", p.Linear) + palleteMat.SetUnifFloat32(indexString+".linear", p.Linear) - whiteMat.SetUnifFloat32(indexString+".quadratic", pl.Quadratic) - containerMat.SetUnifFloat32(indexString+".quadratic", pl.Quadratic) - palleteMat.SetUnifFloat32(indexString+".quadratic", pl.Quadratic) + whiteMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic) + containerMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic) + palleteMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic) + + whiteMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) + containerMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) + palleteMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) } + whiteMat.CubemapArrayTex = omnidirDepthMapFbo.Attachments[0].Id + containerMat.CubemapArrayTex = omnidirDepthMapFbo.Attachments[0].Id + palleteMat.CubemapArrayTex = omnidirDepthMapFbo.Attachments[0].Id + // Spotlights for i := 0; i < len(spotLights); i++ { @@ -658,6 +685,8 @@ func (g *Game) showDebugWindow() { // Directional light imgui.Text("Directional Light") + imgui.Checkbox("Render Directional Light Shadows", &renderDirLightShadows) + if imgui.DragFloat3("Direction", &dirLight.Dir.Data) { whiteMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) containerMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) @@ -695,6 +724,7 @@ func (g *Game) showDebugWindow() { imgui.Spacing() // Point lights + imgui.Checkbox("Render Point Light Shadows", &renderPointLightShadows) if imgui.BeginListBoxV("Point Lights", imgui.Vec2{Y: 200}) { for i := 0; i < len(pointLights); i++ { @@ -869,11 +899,52 @@ func (g *Game) updateCameraPos() { } } +var ( + renderDirLightShadows = true + renderPointLightShadows = true + + rotatingCubeSpeedDeg1 float32 = 45 + rotatingCubeSpeedDeg2 float32 = 120 + rotatingCubeTrMat1 = *gglm.NewTrMatId().Translate(gglm.NewVec3(-4, -1, 4)) + rotatingCubeTrMat2 = *gglm.NewTrMatId().Translate(gglm.NewVec3(-1, 0.5, 4)) +) + func (g *Game) Render() { - dirLightProjViewMat := dirLight.GetProjViewMat() + rotatingCubeTrMat1.Rotate(rotatingCubeSpeedDeg1*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(0, 1, 0)) + rotatingCubeTrMat2.Rotate(rotatingCubeSpeedDeg2*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(1, 1, 0)) + + if renderDirLightShadows { + g.renderDirectionalShadowmap() + } + + if renderPointLightShadows { + g.renderOmnidirectionalShadowmap() + } + + if renderToBackBuffer { + + if renderDepthBuffer { + g.RenderScene(debugDepthMat) + } else { + g.RenderScene(nil) + } + } + + if renderSkybox { + g.DrawSkybox() + } + + if renderToDemoFbo { + g.renderDemoFob() + } +} + +func (g *Game) renderDirectionalShadowmap() { // Set some uniforms + dirLightProjViewMat := dirLight.GetProjViewMat() + whiteMat.SetUnifVec3("camPos", &cam.Pos) whiteMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat) @@ -885,9 +956,7 @@ func (g *Game) Render() { dirLightDepthMapMat.SetUnifMat4("projViewMat", &dirLightProjViewMat) - // - // Render depth map for shadows - // + // Start rendering dirLightDepthMapFbo.BindWithViewport() dirLightDepthMapFbo.Clear() @@ -909,52 +978,58 @@ func (g *Game) Render() { screenQuadMat.Bind() window.Rend.DrawVertexArray(screenQuadMat, &screenQuadVao, 0, 6) } +} - if renderToBackBuffer { +func (g *Game) renderOmnidirectionalShadowmap() { - if renderDepthBuffer { - g.RenderScene(debugDepthMat) - } else { - g.RenderScene(nil) + omnidirDepthMapFbo.BindWithViewport() + omnidirDepthMapFbo.Clear() + + for i := 0; i < len(pointLights); i++ { + + p := &pointLights[i] + + // Generic uniforms + omnidirDepthMapMat.SetUnifVec3("lightPos", &p.Pos) + omnidirDepthMapMat.SetUnifInt32("cubemapIndex", int32(i)) + omnidirDepthMapMat.SetUnifFloat32("farPlane", p.FarPlane) + + // Set projView matrices + projViewMats := p.GetProjViewMats(float32(omnidirDepthMapFbo.Width), float32(omnidirDepthMapFbo.Height)) + for j := 0; j < len(projViewMats); j++ { + omnidirDepthMapMat.SetUnifMat4("cubemapProjViewMats["+strconv.Itoa(j)+"]", &projViewMats[j]) } + + g.RenderScene(omnidirDepthMapMat) + } + + omnidirDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight)) +} + +func (g *Game) renderDemoFob() { + + demoFbo.Bind() + demoFbo.Clear() + + if renderDepthBuffer { + g.RenderScene(debugDepthMat) + } else { + g.RenderScene(nil) } if renderSkybox { g.DrawSkybox() } - if renderToDemoFbo { + demoFbo.UnBind() - demoFbo.Bind() - demoFbo.Clear() + screenQuadMat.DiffuseTex = demoFbo.Attachments[0].Id + screenQuadMat.SetUnifVec2("offset", demoFboOffset) + screenQuadMat.SetUnifVec2("scale", demoFboScale) - if renderDepthBuffer { - g.RenderScene(debugDepthMat) - } else { - g.RenderScene(nil) - } - - if renderSkybox { - g.DrawSkybox() - } - - demoFbo.UnBind() - - screenQuadMat.DiffuseTex = demoFbo.Attachments[0].Id - screenQuadMat.SetUnifVec2("offset", demoFboOffset) - screenQuadMat.SetUnifVec2("scale", demoFboScale) - - window.Rend.DrawVertexArray(screenQuadMat, &screenQuadVao, 0, 6) - } + window.Rend.DrawVertexArray(screenQuadMat, &screenQuadVao, 0, 6) } -var ( - rotatingCubeSpeedDeg1 float32 = 45 - rotatingCubeSpeedDeg2 float32 = 90 - rotatingCubeTrMat1 = *gglm.NewTrMatId().Translate(gglm.NewVec3(-4, -1, 4)) - rotatingCubeTrMat2 = *gglm.NewTrMatId().Translate(gglm.NewVec3(-1, 0.5, 4)) -) - func (g *Game) RenderScene(overrideMat *materials.Material) { tempModelMatrix := cubeModelMat.Clone() @@ -994,10 +1069,8 @@ func (g *Game) RenderScene(overrideMat *materials.Material) { window.Rend.DrawMesh(cubeMesh, tempModelMatrix, cubeMat) // Rotating cubes - rotatingCubeTrMat1.Rotate(rotatingCubeSpeedDeg1*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(0, 1, 0)) window.Rend.DrawMesh(cubeMesh, &rotatingCubeTrMat1, cubeMat) - rotatingCubeTrMat2.Rotate(rotatingCubeSpeedDeg2*gglm.Deg2Rad*timing.DT(), gglm.NewVec3(1, 1, 0)) window.Rend.DrawMesh(cubeMesh, &rotatingCubeTrMat2, cubeMat) // Cubes generator diff --git a/materials/material.go b/materials/material.go index 1d476e9..a430476 100755 --- a/materials/material.go +++ b/materials/material.go @@ -11,12 +11,13 @@ import ( type TextureSlot uint32 const ( - TextureSlot_Diffuse TextureSlot = 0 - TextureSlot_Specular TextureSlot = 1 - TextureSlot_Normal TextureSlot = 2 - TextureSlot_Emission TextureSlot = 3 - TextureSlot_Cubemap TextureSlot = 10 - TextureSlot_ShadowMap TextureSlot = 11 + TextureSlot_Diffuse TextureSlot = 0 + TextureSlot_Specular TextureSlot = 1 + TextureSlot_Normal TextureSlot = 2 + TextureSlot_Emission TextureSlot = 3 + TextureSlot_Cubemap TextureSlot = 10 + TextureSlot_ShadowMap TextureSlot = 11 + TextureSlot_Cubemap_Array TextureSlot = 12 ) type Material struct { @@ -26,6 +27,7 @@ type Material struct { UnifLocs map[string]int32 AttribLocs map[string]int32 + // @TODO do this in a better way. Perhaps something like how we do fbo attachments // Phong shading DiffuseTex uint32 SpecularTex uint32 @@ -35,11 +37,12 @@ type Material struct { // Shininess of specular highlights Shininess float32 - // Cubemap - CubemapTex uint32 + // Cubemaps + CubemapTex uint32 + CubemapArrayTex uint32 // Shadowmaps - ShadowMap uint32 + ShadowMapTex1 uint32 } func (m *Material) Bind() { @@ -71,9 +74,14 @@ func (m *Material) Bind() { gl.BindTexture(gl.TEXTURE_CUBE_MAP, m.CubemapTex) } - if m.ShadowMap != 0 { + if m.CubemapArrayTex != 0 { + gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_Cubemap_Array)) + gl.BindTexture(gl.TEXTURE_CUBE_MAP_ARRAY, m.CubemapArrayTex) + } + + if m.ShadowMapTex1 != 0 { gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_ShadowMap)) - gl.BindTexture(gl.TEXTURE_2D, m.ShadowMap) + gl.BindTexture(gl.TEXTURE_2D, m.ShadowMapTex1) } } diff --git a/res/shaders/omnidirectional-depth-map.glsl b/res/shaders/omnidirectional-depth-map.glsl index 70d99d6..10931f4 100755 --- a/res/shaders/omnidirectional-depth-map.glsl +++ b/res/shaders/omnidirectional-depth-map.glsl @@ -19,6 +19,7 @@ layout (triangles) in; // input 3 triangle vertices are drawn once per face, so 6*3=18 layout (triangle_strip, max_vertices=18) out; +uniform int cubemapIndex; uniform mat4 cubemapProjViewMats[6]; out vec4 FragPos; @@ -28,8 +29,10 @@ void main() for(int face = 0; face < 6; ++face) { // Built in variable that specifies which cubemap face we are rendering to - // and only works when a cubemap is attached to the active fbo - gl_Layer = face; + // and only works when a cubemap is attached to the active fbo. + // + // We use an additional index here because our fbo has a cubemap array + gl_Layer = (cubemapIndex * 6) + face; // Transform each triangle vertex for(int i = 0; i < 3; ++i) diff --git a/res/shaders/simple.glsl b/res/shaders/simple.glsl index 6779b8a..cd935d7 100755 --- a/res/shaders/simple.glsl +++ b/res/shaders/simple.glsl @@ -63,10 +63,12 @@ struct PointLight { float constant; float linear; float quadratic; + float farPlane; }; -#define NUM_POINT_LIGHTS 16 +#define NUM_POINT_LIGHTS 8 uniform PointLight pointLights[NUM_POINT_LIGHTS]; +uniform samplerCubeArray pointLightCubeShadowMaps; struct SpotLight { vec3 pos; @@ -98,7 +100,7 @@ vec4 emissionTexColor; vec3 normalizedVertNorm; vec3 viewDir; -float CalcShadow(sampler2D shadowMap, vec3 lightDir) +float CalcDirShadow(sampler2D shadowMap, vec3 lightDir) { // Move from clip space to NDC vec3 projCoords = fragPosDirLight.xyz / fragPosDirLight.w; @@ -152,12 +154,30 @@ vec3 CalcDirLight() vec3 finalSpecular = specularAmount * dirLight.specularColor * specularTexColor.rgb; // Shadow - float shadow = CalcShadow(dirLight.shadowMap, lightDir); + float shadow = CalcDirShadow(dirLight.shadowMap, lightDir); return (finalDiffuse + finalSpecular) * (1 - shadow); } -vec3 CalcPointLight(PointLight pointLight) +float CalcPointShadow(int lightIndex, vec3 lightPos, vec3 lightDir, float farPlane) { + + vec3 lightToFrag = fragPos - lightPos; + + float closestDepth = texture(pointLightCubeShadowMaps, vec4(lightToFrag, lightIndex)).r; + + // We stored depth in the cubemap in the range [0, 1], so now we move back to [0, farPlane] + closestDepth *= farPlane; + + // Get depth of current fragment + float currentDepth = length(lightToFrag); + + float bias = max(0.05 * (1 - dot(normalizedVertNorm, lightDir)), 0.005); + float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + + return shadow; +} + +vec3 CalcPointLight(PointLight pointLight, int lightIndex) { // Ignore unset lights if (pointLight.constant == 0){ @@ -175,11 +195,14 @@ vec3 CalcPointLight(PointLight pointLight) float specularAmount = pow(max(dot(normalizedVertNorm, halfwayDir), 0.0), material.shininess); vec3 finalSpecular = specularAmount * pointLight.specularColor * specularTexColor.rgb; - // attenuation + // Attenuation float distToLight = length(pointLight.pos - fragPos); float attenuation = 1 / (pointLight.constant + pointLight.linear * distToLight + pointLight.quadratic * (distToLight * distToLight)); - return (finalDiffuse + finalSpecular) * attenuation; + // Shadow + float shadow = CalcPointShadow(lightIndex, pointLight.pos, lightDir, pointLight.farPlane); + + return (finalDiffuse + finalSpecular) * attenuation * (1 - shadow); } vec3 CalcSpotLight(SpotLight light) @@ -226,7 +249,7 @@ void main() for (int i = 0; i < NUM_POINT_LIGHTS; i++) { - finalColor += CalcPointLight(pointLights[i]); + finalColor += CalcPointLight(pointLights[i], i); } for (int i = 0; i < NUM_SPOT_LIGHTS; i++)