package main import ( "fmt" "os" "runtime" "runtime/pprof" "strconv" imgui "github.com/AllenDang/cimgui-go" "github.com/bloeys/gglm/gglm" "github.com/bloeys/nmage/assert" "github.com/bloeys/nmage/assets" "github.com/bloeys/nmage/buffers" "github.com/bloeys/nmage/camera" "github.com/bloeys/nmage/engine" "github.com/bloeys/nmage/input" "github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/meshes" "github.com/bloeys/nmage/renderer/rend3dgl" "github.com/bloeys/nmage/timing" nmageimgui "github.com/bloeys/nmage/ui/imgui" "github.com/go-gl/gl/v4.1-core/gl" "github.com/veandco/go-sdl2/sdl" ) /* @TODO: - Rendering: - Blinn-Phong lighting model ✅ - Directional lights ✅ - Point lights ✅ - Spotlights ✅ - Directional light shadows ✅ - Point light shadows ✅ - Spotlight shadows ✅ - Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing) ✅ - Normals maps ✅ - HDR ✅ - Fix bad point light acne - UBO support - Cascaded shadow mapping - 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) - Renderer batching - Scene graph - Separate engine loop from rendering loop? or leave it to the user? - Abstract keys enum away from sdl? - Frustum culling - Proper Asset loading system - Material system editor with fields automatically extracted from the shader */ type DirLight struct { Dir gglm.Vec3 DiffuseColor gglm.Vec3 SpecularColor gglm.Vec3 } var ( dirLightSize float32 = 30 dirLightNear float32 = 0.1 dirLightFar float32 = 30 dirLightPos = gglm.NewVec3(0, 10, 0) ) func (d *DirLight) GetProjViewMat() gglm.Mat4 { // Some arbitrary position for the directional light pos := dirLightPos size := dirLightSize nearClip := dirLightNear farClip := dirLightFar up := gglm.NewVec3(0, 1, 0) projMat := gglm.Ortho(-size, size, -size, size, nearClip, farClip).Mat4 viewMat := gglm.LookAtRH(&pos, pos.Clone().Add(&d.Dir), &up).Mat4 return *projMat.Mul(&viewMat) } // Check https://wiki.ogre3d.org/tiki-index.php?page=-Point+Light+Attenuation for values type PointLight struct { Pos gglm.Vec3 DiffuseColor gglm.Vec3 SpecularColor gglm.Vec3 // @TODO Radius float32 Constant float32 Linear float32 Quadratic float32 FarPlane float32 } const ( MaxPointLights = 8 // If this changes update the array depth map shader MaxSpotLights = 4 ) var ( pointLightNear float32 = 1 ) func (p *PointLight) GetProjViewMats(shadowMapWidth, shadowMapHeight float32) [6]gglm.Mat4 { aspect := float32(shadowMapWidth) / float32(shadowMapHeight) projMat := gglm.Perspective(90*gglm.Deg2Rad, aspect, pointLightNear, p.FarPlane) targetPos0 := gglm.NewVec3(1+p.Pos.X(), p.Pos.Y(), p.Pos.Z()) targetPos1 := gglm.NewVec3(-1+p.Pos.X(), p.Pos.Y(), p.Pos.Z()) targetPos2 := gglm.NewVec3(p.Pos.X(), 1+p.Pos.Y(), p.Pos.Z()) targetPos3 := gglm.NewVec3(p.Pos.X(), -1+p.Pos.Y(), p.Pos.Z()) targetPos4 := gglm.NewVec3(p.Pos.X(), p.Pos.Y(), 1+p.Pos.Z()) targetPos5 := gglm.NewVec3(p.Pos.X(), p.Pos.Y(), -1+p.Pos.Z()) worldUp0 := gglm.NewVec3(0, -1, 0) worldUp1 := gglm.NewVec3(0, -1, 0) worldUp2 := gglm.NewVec3(0, 0, 1) worldUp3 := gglm.NewVec3(0, 0, -1) worldUp4 := gglm.NewVec3(0, -1, 0) worldUp5 := gglm.NewVec3(0, -1, 0) lookAt0 := gglm.LookAtRH(&p.Pos, &targetPos0, &worldUp0) lookAt1 := gglm.LookAtRH(&p.Pos, &targetPos1, &worldUp1) lookAt2 := gglm.LookAtRH(&p.Pos, &targetPos2, &worldUp2) lookAt3 := gglm.LookAtRH(&p.Pos, &targetPos3, &worldUp3) lookAt4 := gglm.LookAtRH(&p.Pos, &targetPos4, &worldUp4) lookAt5 := gglm.LookAtRH(&p.Pos, &targetPos5, &worldUp5) projViewMats := [6]gglm.Mat4{ *projMat.Clone().Mul(&lookAt0.Mat4), *projMat.Clone().Mul(&lookAt1.Mat4), *projMat.Clone().Mul(&lookAt2.Mat4), *projMat.Clone().Mul(&lookAt3.Mat4), *projMat.Clone().Mul(&lookAt4.Mat4), *projMat.Clone().Mul(&lookAt5.Mat4), } return projViewMats } type SpotLight struct { Pos gglm.Vec3 Dir gglm.Vec3 DiffuseColor gglm.Vec3 SpecularColor gglm.Vec3 InnerCutoffRad 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 } func (s *SpotLight) GetProjViewMat() gglm.Mat4 { projMat := gglm.Perspective(s.OuterCutoffRad*2, 1, s.NearPlane, s.FarPlane) // Adjust up vector if lightDir is parallel or nearly parallel to upVector // as lookat view matrix breaks if up and look at are parallel up := gglm.NewVec3(0, 1, 0) if gglm.Abs32(gglm.DotVec3(&s.Dir, &up)) > 0.99 { up.SetXY(1, 0) } 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 ( camSpeed = 15 mouseSensitivity = 0.5 unscaledWindowWidth = 1280 unscaledWindowHeight = 720 PROFILE_CPU = true ) var ( window engine.Window pitch float32 = 0 yaw float32 = -1.5 cam camera.Camera renderToBackBuffer = true // Demo fbo renderToDemoFbo = false demoFboScale = gglm.NewVec2(0.25, 0.25) demoFboOffset = gglm.NewVec2(0.75, -0.75) demoFbo buffers.Framebuffer // Dir light fbo showDirLightDepthMapFbo = false dirLightDepthMapFboScale = gglm.NewVec2(0.25, 0.25) dirLightDepthMapFboOffset = gglm.NewVec2(0.75, -0.2) dirLightDepthMapFbo buffers.Framebuffer // Point light fbo pointLightDepthMapFbo buffers.Framebuffer // Spot light fbo spotLightDepthMapFbo buffers.Framebuffer // Hdr Fbo hdrRendering = true hdrExposure float32 = 1 tonemappedScreenQuadMat materials.Material hdrFbo buffers.Framebuffer screenQuadVao buffers.VertexArray screenQuadMat materials.Material unlitMat materials.Material whiteMat materials.Material containerMat materials.Material groundMat materials.Material palleteMat materials.Material skyboxMat materials.Material depthMapMat materials.Material arrayDepthMapMat materials.Material omnidirDepthMapMat materials.Material debugDepthMat materials.Material cubeMesh meshes.Mesh sphereMesh meshes.Mesh chairMesh meshes.Mesh skyboxMesh meshes.Mesh cubeModelMat = gglm.NewTrMatId() renderSkybox = true renderDepthBuffer = false skyboxCmap assets.Cubemap dpiScaling float32 // Light settings ambientColor = gglm.NewVec3(0, 0, 0) dirLightDir = gglm.NewVec3(0, -0.5, -0.8) // Lights dirLight = DirLight{ Dir: *dirLightDir.Normalize(), DiffuseColor: gglm.NewVec3(1, 1, 1), SpecularColor: gglm.NewVec3(1, 1, 1), } pointLights = [...]PointLight{ { 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), DiffuseColor: gglm.NewVec3(0, 1, 0), SpecularColor: gglm.NewVec3(1, 1, 1), Constant: 1.0, Linear: 0.09, Quadratic: 0.032, FarPlane: 25, }, { Pos: gglm.NewVec3(5, 0, 0), DiffuseColor: gglm.NewVec3(1, 1, 1), SpecularColor: gglm.NewVec3(1, 1, 1), Constant: 1.0, Linear: 0.09, Quadratic: 0.032, FarPlane: 25, }, { 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, }, } spotLightDir0 = gglm.NewVec3(1.5, -0.9, 0) spotLights = [...]SpotLight{ { Pos: gglm.NewVec3(-4, 7, 5), Dir: *spotLightDir0.Normalize(), DiffuseColor: gglm.NewVec3(1, 0, 1), SpecularColor: gglm.NewVec3(1, 1, 1), // These must be cosine values InnerCutoffRad: 15 * gglm.Deg2Rad, OuterCutoffRad: 20 * gglm.Deg2Rad, NearPlane: 2, FarPlane: 50, }, } ) type Game struct { WinWidth int32 WinHeight int32 Win *engine.Window ImGUIInfo nmageimgui.ImguiInfo } func main() { //Init engine err := engine.Init() if err != nil { logging.ErrLog.Fatalln("Failed to init nMage. Err:", err) } //Create window dpiScaling = getDpiScaling(unscaledWindowWidth, unscaledWindowHeight) window, err = engine.CreateOpenGLWindowCentered("nMage", int32(unscaledWindowWidth*dpiScaling), int32(unscaledWindowHeight*dpiScaling), engine.WindowFlags_RESIZABLE, rend3dgl.NewRend3DGL()) if err != nil { logging.ErrLog.Fatalln("Failed to create window. Err: ", err) } defer window.Destroy() engine.SetMSAA(true) engine.SetVSync(false) engine.SetSrgbFramebuffer(true) game := &Game{ Win: &window, WinWidth: int32(unscaledWindowWidth * dpiScaling), WinHeight: int32(unscaledWindowHeight * dpiScaling), ImGUIInfo: nmageimgui.NewImGui("./res/shaders/imgui.glsl"), } window.EventCallbacks = append(window.EventCallbacks, game.handleWindowEvents) if PROFILE_CPU { pf, err := os.Create("cpu.pprof") if err == nil { defer pf.Close() pprof.StartCPUProfile(pf) } else { logging.ErrLog.Printf("Creating cpu.pprof file failed. CPU profiling will not run. Err=%v\n", err) } } engine.Run(game, &window, game.ImGUIInfo) if PROFILE_CPU { pprof.StopCPUProfile() heapProfile, err := os.Create("heap.pprof") if err == nil { err = pprof.WriteHeapProfile(heapProfile) if err != nil { logging.ErrLog.Printf("Writing heap profile to heap.pprof failed. Err=%v\n", err) } heapProfile.Close() } else { logging.ErrLog.Printf("Creating heap.pprof file failed. Err=%v\n", err) } } } func (g *Game) handleWindowEvents(e sdl.Event) { switch e := e.(type) { case *sdl.WindowEvent: if e.Event == sdl.WINDOWEVENT_SIZE_CHANGED { g.WinWidth = e.Data1 g.WinHeight = e.Data2 cam.AspectRatio = float32(g.WinWidth) / float32(g.WinHeight) cam.Update() updateAllProjViewMats(cam.ProjMat, cam.ViewMat) } } } func getDpiScaling(unscaledWindowWidth, unscaledWindowHeight int32) float32 { // Great read on DPI here: https://nlguillemot.wordpress.com/2016/12/11/high-dpi-rendering/ // The no-scaling DPI on different platforms (e.g. when scale=100% on windows) var defaultDpi float32 = 96 if runtime.GOOS == "windows" { defaultDpi = 96 } else if runtime.GOOS == "darwin" { defaultDpi = 72 } // Current DPI of the monitor _, dpiHorizontal, _, err := sdl.GetDisplayDPI(0) if err != nil { dpiHorizontal = defaultDpi fmt.Printf("Failed to get DPI with error '%s'. Using default DPI of '%f'\n", err.Error(), defaultDpi) } // Scaling factor (e.g. will be 1.25 for 125% scaling on windows) dpiScaling := dpiHorizontal / defaultDpi fmt.Printf( "Default DPI=%f\nHorizontal DPI=%f\nDPI scaling=%f\nUnscaled window size (width, height)=(%d, %d)\nScaled window size (width, height)=(%d, %d)\n\n", defaultDpi, dpiHorizontal, dpiScaling, unscaledWindowWidth, unscaledWindowHeight, int32(float32(unscaledWindowWidth)*dpiScaling), int32(float32(unscaledWindowHeight)*dpiScaling), ) return dpiScaling } func (g *Game) Init() { var err error // Camera winWidth, winHeight := g.Win.SDLWin.GetSize() camPos := gglm.NewVec3(0, 0, 10) camForward := gglm.NewVec3(0, 0, -1) camWorldUp := gglm.NewVec3(0, 1, 0) cam = camera.NewPerspective( &camPos, &camForward, &camWorldUp, 0.1, 200, 45*gglm.Deg2Rad, float32(winWidth)/float32(winHeight), ) //Load meshes cubeMesh, err = meshes.NewMesh("Cube", "./res/models/cube.fbx", 0) if err != nil { logging.ErrLog.Fatalln("Failed to load mesh. Err: ", err) } sphereMesh, err = meshes.NewMesh("Sphere", "./res/models/sphere.fbx", 0) if err != nil { logging.ErrLog.Fatalln("Failed to load mesh. Err: ", err) } chairMesh, err = meshes.NewMesh("Chair", "./res/models/chair.fbx", 0) if err != nil { logging.ErrLog.Fatalln("Failed to load mesh. Err: ", err) } skyboxMesh, err = meshes.NewMesh("Skybox", "./res/models/skybox-cube.obj", 0) if err != nil { logging.ErrLog.Fatalln("Failed to load mesh. Err: ", err) } //Load textures containerDiffuseTex, err := assets.LoadTexturePNG("./res/textures/container-diffuse.png", &assets.TextureLoadOptions{}) if err != nil { logging.ErrLog.Fatalln("Failed to load texture. Err: ", err) } containerSpecularTex, err := assets.LoadTexturePNG("./res/textures/container-specular.png", &assets.TextureLoadOptions{}) if err != nil { logging.ErrLog.Fatalln("Failed to load texture. Err: ", err) } palleteTex, err := assets.LoadTexturePNG("./res/textures/pallete-endesga-64-1x.png", &assets.TextureLoadOptions{}) if err != nil { logging.ErrLog.Fatalln("Failed to load texture. Err: ", err) } brickwallDiffuseTex, err := assets.LoadTexturePNG("./res/textures/brickwall.png", &assets.TextureLoadOptions{}) if err != nil { logging.ErrLog.Fatalln("Failed to load texture. Err: ", err) } brickwallNormalTex, err := assets.LoadTexturePNG("./res/textures/brickwall-normal.png", &assets.TextureLoadOptions{NoSrgba: true}) if err != nil { logging.ErrLog.Fatalln("Failed to load texture. Err: ", err) } skyboxCmap, err = assets.LoadCubemapTextures( "./res/textures/sb-right.jpg", "./res/textures/sb-left.jpg", "./res/textures/sb-top.jpg", "./res/textures/sb-bottom.jpg", "./res/textures/sb-front.jpg", "./res/textures/sb-back.jpg", &assets.TextureLoadOptions{}, ) if err != nil { logging.ErrLog.Fatalln("Failed to load cubemap. Err: ", err) } // // Create materials and assign any unused texture slots to black // screenQuadMat = materials.NewMaterial("Screen Quad Mat", "./res/shaders/screen-quad.glsl") screenQuadMat.SetUnifVec2("scale", &demoFboScale) screenQuadMat.SetUnifVec2("offset", &demoFboOffset) screenQuadMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) tonemappedScreenQuadMat = materials.NewMaterial("Tonemapped Screen Quad Mat", "./res/shaders/tonemapped-screen-quad.glsl") tonemappedScreenQuadMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) unlitMat = materials.NewMaterial("Unlit mat", "./res/shaders/simple-unlit.glsl") unlitMat.Settings.Set(materials.MaterialSettings_HasModelMtx) unlitMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) whiteMat = materials.NewMaterial("White mat", "./res/shaders/simple.glsl") whiteMat.Settings.Set(materials.MaterialSettings_HasModelMtx) whiteMat.Shininess = 64 whiteMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) whiteMat.SetUnifInt32("material.specular", int32(materials.TextureSlot_Specular)) whiteMat.SetUnifInt32("material.normal", int32(materials.TextureSlot_Normal)) whiteMat.SetUnifInt32("material.emission", int32(materials.TextureSlot_Emission)) whiteMat.SetUnifVec3("ambientColor", &ambientColor) whiteMat.SetUnifFloat32("material.shininess", whiteMat.Shininess) whiteMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) whiteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) whiteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) whiteMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap1)) 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.Settings.Set(materials.MaterialSettings_HasModelMtx) containerMat.Shininess = 64 containerMat.DiffuseTex = containerDiffuseTex.TexID containerMat.SpecularTex = containerSpecularTex.TexID containerMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) containerMat.SetUnifInt32("material.specular", int32(materials.TextureSlot_Specular)) containerMat.SetUnifInt32("material.normal", int32(materials.TextureSlot_Normal)) containerMat.SetUnifInt32("material.emission", int32(materials.TextureSlot_Emission)) containerMat.SetUnifVec3("ambientColor", &ambientColor) containerMat.SetUnifFloat32("material.shininess", containerMat.Shininess) containerMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) containerMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) containerMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) containerMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap1)) containerMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array)) containerMat.SetUnifInt32("spotLightShadowMaps", int32(materials.TextureSlot_ShadowMap_Array1)) groundMat = materials.NewMaterial("Ground mat", "./res/shaders/simple.glsl") groundMat.Settings.Set(materials.MaterialSettings_HasModelMtx) groundMat.Shininess = 64 groundMat.DiffuseTex = brickwallDiffuseTex.TexID groundMat.NormalTex = brickwallNormalTex.TexID groundMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) groundMat.SetUnifInt32("material.specular", int32(materials.TextureSlot_Specular)) groundMat.SetUnifInt32("material.normal", int32(materials.TextureSlot_Normal)) groundMat.SetUnifInt32("material.emission", int32(materials.TextureSlot_Emission)) groundMat.SetUnifVec3("ambientColor", &ambientColor) groundMat.SetUnifFloat32("material.shininess", groundMat.Shininess) groundMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) groundMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) groundMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) groundMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap1)) groundMat.SetUnifInt32("pointLightCubeShadowMaps", int32(materials.TextureSlot_Cubemap_Array)) groundMat.SetUnifInt32("spotLightShadowMaps", int32(materials.TextureSlot_ShadowMap_Array1)) palleteMat = materials.NewMaterial("Pallete mat", "./res/shaders/simple.glsl") palleteMat.Settings.Set(materials.MaterialSettings_HasModelMtx) palleteMat.Shininess = 64 palleteMat.DiffuseTex = palleteTex.TexID palleteMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) palleteMat.SetUnifInt32("material.specular", int32(materials.TextureSlot_Specular)) palleteMat.SetUnifInt32("material.normal", int32(materials.TextureSlot_Normal)) palleteMat.SetUnifInt32("material.emission", int32(materials.TextureSlot_Emission)) palleteMat.SetUnifVec3("ambientColor", &ambientColor) palleteMat.SetUnifFloat32("material.shininess", palleteMat.Shininess) palleteMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) palleteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) palleteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) palleteMat.SetUnifInt32("dirLight.shadowMap", int32(materials.TextureSlot_ShadowMap1)) 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.Settings.Set(materials.MaterialSettings_HasModelMtx) depthMapMat = materials.NewMaterial("Depth Map mat", "./res/shaders/depth-map.glsl") depthMapMat.Settings.Set(materials.MaterialSettings_HasModelMtx) arrayDepthMapMat = materials.NewMaterial("Array Depth Map mat", "./res/shaders/array-depth-map.glsl") arrayDepthMapMat.Settings.Set(materials.MaterialSettings_HasModelMtx) omnidirDepthMapMat = materials.NewMaterial("Omnidirectional Depth Map mat", "./res/shaders/omnidirectional-depth-map.glsl") omnidirDepthMapMat.Settings.Set(materials.MaterialSettings_HasModelMtx) skyboxMat = materials.NewMaterial("Skybox mat", "./res/shaders/skybox.glsl") skyboxMat.CubemapTex = skyboxCmap.TexID skyboxMat.SetUnifInt32("skybox", int32(materials.TextureSlot_Cubemap)) // Cube model mat translationMat := gglm.NewTranslationMat(0, 0, 0) scaleMat := gglm.NewScaleMat(1, 1, 1) rotMatRot := gglm.NewQuatEuler(-90*gglm.Deg2Rad, -90*gglm.Deg2Rad, 0) rotMat := gglm.NewRotMatQuat(&rotMatRot) cubeModelMat.Mul(translationMat.Mul(rotMat.Mul(&scaleMat))) // Screen quad vao setup. // We don't actually care about the values here because the quad is hardcoded in the shader, // but we just want to have a vao with 6 vertices and uv0 so opengl can be called properly screenQuadVbo := buffers.NewVertexBuffer(buffers.Element{ElementType: buffers.DataTypeVec3}, buffers.Element{ElementType: buffers.DataTypeVec2}) screenQuadVbo.SetData(make([]float32, 6), buffers.BufUsage_Static) screenQuadVao = buffers.NewVertexArray() screenQuadVao.AddVertexBuffer(screenQuadVbo) // Fbos and lights g.initFbos() g.updateLights() // Initial camera update cam.Update() updateAllProjViewMats(cam.ProjMat, cam.ViewMat) } func (g *Game) initFbos() { // @TODO: Resize window sized fbos on window resize // Demo fbo demoFbo = buffers.NewFramebuffer(uint32(g.WinWidth), uint32(g.WinHeight)) demoFbo.NewColorAttachment( buffers.FramebufferAttachmentType_Texture, buffers.FramebufferAttachmentDataFormat_SRGBA, ) demoFbo.NewDepthStencilAttachment( buffers.FramebufferAttachmentType_Renderbuffer, buffers.FramebufferAttachmentDataFormat_Depth24Stencil8, ) assert.T(demoFbo.IsComplete(), "Demo fbo is not complete after init") // Depth map fbo dirLightDepthMapFbo = buffers.NewFramebuffer(2048, 2048) dirLightDepthMapFbo.SetNoColorBuffer() dirLightDepthMapFbo.NewDepthAttachment( buffers.FramebufferAttachmentType_Texture, buffers.FramebufferAttachmentDataFormat_DepthF32, ) assert.T(dirLightDepthMapFbo.IsComplete(), "Depth map fbo is not complete after init") // Point light depth map fbo pointLightDepthMapFbo = buffers.NewFramebuffer(512, 512) pointLightDepthMapFbo.SetNoColorBuffer() pointLightDepthMapFbo.NewDepthCubemapArrayAttachment( buffers.FramebufferAttachmentDataFormat_DepthF32, MaxPointLights, ) assert.T(pointLightDepthMapFbo.IsComplete(), "Point light depth map fbo is not complete after init") // Spot light depth map fbo spotLightDepthMapFbo = buffers.NewFramebuffer(512, 512) spotLightDepthMapFbo.SetNoColorBuffer() spotLightDepthMapFbo.NewDepthTextureArrayAttachment( buffers.FramebufferAttachmentDataFormat_DepthF32, MaxSpotLights, ) assert.T(spotLightDepthMapFbo.IsComplete(), "Spot light depth map fbo is not complete after init") // Hdr fbo hdrFbo = buffers.NewFramebuffer(uint32(g.WinWidth), uint32(g.WinHeight)) hdrFbo.NewColorAttachment( buffers.FramebufferAttachmentType_Texture, buffers.FramebufferAttachmentDataFormat_RGBAF16, ) hdrFbo.NewDepthStencilAttachment( buffers.FramebufferAttachmentType_Renderbuffer, buffers.FramebufferAttachmentDataFormat_Depth24Stencil8, ) assert.T(hdrFbo.IsComplete(), "Hdr fbo is not complete after init") } func (g *Game) updateLights() { // Directional light whiteMat.ShadowMapTex1 = dirLightDepthMapFbo.Attachments[0].Id containerMat.ShadowMapTex1 = dirLightDepthMapFbo.Attachments[0].Id groundMat.ShadowMapTex1 = dirLightDepthMapFbo.Attachments[0].Id palleteMat.ShadowMapTex1 = dirLightDepthMapFbo.Attachments[0].Id // Point lights for i := 0; i < len(pointLights); i++ { p := &pointLights[i] indexString := "pointLights[" + strconv.Itoa(i) + "]" whiteMat.SetUnifVec3(indexString+".pos", &p.Pos) containerMat.SetUnifVec3(indexString+".pos", &p.Pos) groundMat.SetUnifVec3(indexString+".pos", &p.Pos) palleteMat.SetUnifVec3(indexString+".pos", &p.Pos) whiteMat.SetUnifVec3(indexString+".diffuseColor", &p.DiffuseColor) containerMat.SetUnifVec3(indexString+".diffuseColor", &p.DiffuseColor) groundMat.SetUnifVec3(indexString+".diffuseColor", &p.DiffuseColor) palleteMat.SetUnifVec3(indexString+".diffuseColor", &p.DiffuseColor) whiteMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) containerMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) groundMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) palleteMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) whiteMat.SetUnifFloat32(indexString+".constant", p.Constant) containerMat.SetUnifFloat32(indexString+".constant", p.Constant) groundMat.SetUnifFloat32(indexString+".constant", p.Constant) palleteMat.SetUnifFloat32(indexString+".constant", p.Constant) whiteMat.SetUnifFloat32(indexString+".linear", p.Linear) containerMat.SetUnifFloat32(indexString+".linear", p.Linear) groundMat.SetUnifFloat32(indexString+".linear", p.Linear) palleteMat.SetUnifFloat32(indexString+".linear", p.Linear) whiteMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic) containerMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic) groundMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic) palleteMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic) whiteMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) containerMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) groundMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) palleteMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) } whiteMat.CubemapArrayTex = pointLightDepthMapFbo.Attachments[0].Id containerMat.CubemapArrayTex = pointLightDepthMapFbo.Attachments[0].Id groundMat.CubemapArrayTex = pointLightDepthMapFbo.Attachments[0].Id palleteMat.CubemapArrayTex = pointLightDepthMapFbo.Attachments[0].Id // Spotlights for i := 0; i < len(spotLights); i++ { l := &spotLights[i] innerCutoffCos := l.InnerCutoffCos() outerCutoffCos := l.OuterCutoffCos() indexString := "spotLights[" + strconv.Itoa(i) + "]" whiteMat.SetUnifVec3(indexString+".pos", &l.Pos) containerMat.SetUnifVec3(indexString+".pos", &l.Pos) groundMat.SetUnifVec3(indexString+".pos", &l.Pos) palleteMat.SetUnifVec3(indexString+".pos", &l.Pos) whiteMat.SetUnifVec3(indexString+".dir", &l.Dir) containerMat.SetUnifVec3(indexString+".dir", &l.Dir) groundMat.SetUnifVec3(indexString+".dir", &l.Dir) palleteMat.SetUnifVec3(indexString+".dir", &l.Dir) whiteMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) containerMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) groundMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) palleteMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) whiteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) containerMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) groundMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) palleteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) whiteMat.SetUnifFloat32(indexString+".innerCutoff", innerCutoffCos) containerMat.SetUnifFloat32(indexString+".innerCutoff", innerCutoffCos) groundMat.SetUnifFloat32(indexString+".innerCutoff", innerCutoffCos) palleteMat.SetUnifFloat32(indexString+".innerCutoff", innerCutoffCos) whiteMat.SetUnifFloat32(indexString+".outerCutoff", outerCutoffCos) containerMat.SetUnifFloat32(indexString+".outerCutoff", outerCutoffCos) groundMat.SetUnifFloat32(indexString+".outerCutoff", outerCutoffCos) palleteMat.SetUnifFloat32(indexString+".outerCutoff", outerCutoffCos) } whiteMat.ShadowMapTexArray1 = spotLightDepthMapFbo.Attachments[0].Id containerMat.ShadowMapTexArray1 = spotLightDepthMapFbo.Attachments[0].Id groundMat.ShadowMapTexArray1 = spotLightDepthMapFbo.Attachments[0].Id palleteMat.ShadowMapTexArray1 = spotLightDepthMapFbo.Attachments[0].Id } func (g *Game) Update() { if input.IsQuitClicked() || input.KeyClicked(sdl.K_ESCAPE) { engine.Quit() } g.updateCameraLookAround() g.updateCameraPos() g.showDebugWindow() if input.KeyClicked(sdl.K_F4) { fmt.Printf("Pos: %s; Forward: %s; |Forward|: %f\n", cam.Pos.String(), cam.Forward.String(), cam.Forward.Mag()) } g.Win.SDLWin.SetTitle(fmt.Sprint("nMage (", timing.GetAvgFPS(), " fps)")) } func (g *Game) showDebugWindow() { imgui.ShowDemoWindow() imgui.Begin("Debug controls") // Camera imgui.Text("Camera") if imgui.DragFloat3("Cam Pos", &cam.Pos.Data) { cam.Update() updateAllProjViewMats(cam.ProjMat, cam.ViewMat) } if imgui.DragFloat3("Cam Forward", &cam.Forward.Data) { cam.Update() updateAllProjViewMats(cam.ProjMat, cam.ViewMat) } imgui.Spacing() imgui.Text("HDR") imgui.Checkbox("Enable HDR", &hdrRendering) if imgui.DragFloat("Exposure", &hdrExposure) { tonemappedScreenQuadMat.SetUnifFloat32("exposure", hdrExposure) } imgui.Spacing() // Ambient light imgui.Text("Ambient Light") if imgui.DragFloat3("Ambient Color", &ambientColor.Data) { whiteMat.SetUnifVec3("ambientColor", &ambientColor) containerMat.SetUnifVec3("ambientColor", &ambientColor) groundMat.SetUnifVec3("ambientColor", &ambientColor) palleteMat.SetUnifVec3("ambientColor", &ambientColor) } imgui.Spacing() // 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) groundMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) palleteMat.SetUnifVec3("dirLight.dir", &dirLight.Dir) } if imgui.DragFloat3("Diffuse Color", &dirLight.DiffuseColor.Data) { whiteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) containerMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) groundMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) palleteMat.SetUnifVec3("dirLight.diffuseColor", &dirLight.DiffuseColor) } if imgui.DragFloat3("Specular Color", &dirLight.SpecularColor.Data) { whiteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) containerMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) groundMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) palleteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) } imgui.DragFloat3("dPos", &dirLightPos.Data) imgui.DragFloat("dSize", &dirLightSize) imgui.DragFloat("dNear", &dirLightNear) imgui.DragFloat("dFar", &dirLightFar) imgui.Spacing() // Specular imgui.Text("Specular Settings") if imgui.DragFloat("Specular Shininess", &whiteMat.Shininess) { whiteMat.SetUnifFloat32("material.shininess", whiteMat.Shininess) containerMat.SetUnifFloat32("material.shininess", whiteMat.Shininess) groundMat.SetUnifFloat32("material.shininess", whiteMat.Shininess) palleteMat.SetUnifFloat32("material.shininess", whiteMat.Shininess) } 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++ { pl := &pointLights[i] indexNumString := strconv.Itoa(i) if !imgui.TreeNodeExStrV("Point Light "+indexNumString, imgui.TreeNodeFlagsSpanAvailWidth) { continue } indexString := "pointLights[" + indexNumString + "]" if imgui.DragFloat3("Pos", &pl.Pos.Data) { whiteMat.SetUnifVec3(indexString+".pos", &pl.Pos) containerMat.SetUnifVec3(indexString+".pos", &pl.Pos) groundMat.SetUnifVec3(indexString+".pos", &pl.Pos) palleteMat.SetUnifVec3(indexString+".pos", &pl.Pos) } if imgui.DragFloat3("Diffuse Color", &pl.DiffuseColor.Data) { whiteMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) containerMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) groundMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) palleteMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) } if imgui.DragFloat3("Specular Color", &pl.SpecularColor.Data) { whiteMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) containerMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) groundMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) palleteMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) } imgui.TreePop() } imgui.EndListBox() } // Spot lights imgui.Checkbox("Render Spot Light Shadows", &renderSpotLightShadows) if imgui.BeginListBoxV("Spot Lights", imgui.Vec2{Y: 200}) { for i := 0; i < len(spotLights); i++ { l := &spotLights[i] indexNumString := strconv.Itoa(i) if !imgui.TreeNodeExStrV("Spot Light "+indexNumString, imgui.TreeNodeFlagsSpanAvailWidth) { continue } indexString := "spotLights[" + indexNumString + "]" if imgui.DragFloat3("Pos", &l.Pos.Data) { whiteMat.SetUnifVec3(indexString+".pos", &l.Pos) containerMat.SetUnifVec3(indexString+".pos", &l.Pos) groundMat.SetUnifVec3(indexString+".pos", &l.Pos) palleteMat.SetUnifVec3(indexString+".pos", &l.Pos) } if imgui.DragFloat3("Dir", &l.Dir.Data) { whiteMat.SetUnifVec3(indexString+".dir", &l.Dir) containerMat.SetUnifVec3(indexString+".dir", &l.Dir) groundMat.SetUnifVec3(indexString+".dir", &l.Dir) palleteMat.SetUnifVec3(indexString+".dir", &l.Dir) } if imgui.DragFloat3("Diffuse Color", &l.DiffuseColor.Data) { whiteMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) containerMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) groundMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) palleteMat.SetUnifVec3(indexString+".diffuseColor", &l.DiffuseColor) } if imgui.DragFloat3("Specular Color", &l.SpecularColor.Data) { whiteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) containerMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) groundMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) palleteMat.SetUnifVec3(indexString+".specularColor", &l.SpecularColor) } if imgui.DragFloat("Inner Cutoff Radians", &l.InnerCutoffRad) { cos := l.InnerCutoffCos() whiteMat.SetUnifFloat32(indexString+".innerCutoff", cos) containerMat.SetUnifFloat32(indexString+".innerCutoff", cos) groundMat.SetUnifFloat32(indexString+".innerCutoff", cos) palleteMat.SetUnifFloat32(indexString+".innerCutoff", cos) } if imgui.DragFloat("Outer Cutoff Radians", &l.OuterCutoffRad) { cos := l.OuterCutoffCos() whiteMat.SetUnifFloat32(indexString+".outerCutoff", cos) containerMat.SetUnifFloat32(indexString+".outerCutoff", cos) groundMat.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.EndListBox() } // Demo fbo imgui.Text("Demo Framebuffer") imgui.Checkbox("Show FBO##0", &renderToDemoFbo) imgui.DragFloat2("Scale##0", &demoFboScale.Data) imgui.DragFloat2("Offset##0", &demoFboOffset.Data) // Depth map fbo imgui.Text("Directional Light Depth Map Framebuffer") imgui.Checkbox("Show FBO##1", &showDirLightDepthMapFbo) imgui.DragFloat2("Scale##1", &dirLightDepthMapFboScale.Data) imgui.DragFloat2("Offset##1", &dirLightDepthMapFboOffset.Data) // Other imgui.Text("Other Settings") imgui.Checkbox("Render skybox", &renderSkybox) imgui.Checkbox("Render to back buffer", &renderToBackBuffer) imgui.Checkbox("Render depth buffer", &renderDepthBuffer) imgui.End() } func (g *Game) updateCameraLookAround() { mouseX, mouseY := input.GetMouseMotion() if (mouseX == 0 && mouseY == 0) || !input.MouseDown(sdl.BUTTON_RIGHT) { return } // Yaw yaw += float32(mouseX) * mouseSensitivity * timing.DT() // Pitch pitch += float32(-mouseY) * mouseSensitivity * timing.DT() if pitch > 1.5 { pitch = 1.5 } if pitch < -1.5 { pitch = -1.5 } // Update cam forward cam.UpdateRotation(pitch, yaw) updateAllProjViewMats(cam.ProjMat, cam.ViewMat) } func (g *Game) updateCameraPos() { update := false var camSpeedScale float32 = 1.0 if input.KeyDown(sdl.K_LSHIFT) { camSpeedScale = 2 } // Forward and backward if input.KeyDown(sdl.K_w) { cam.Pos.Add(cam.Forward.Clone().Scale(camSpeed * camSpeedScale * timing.DT())) update = true } else if input.KeyDown(sdl.K_s) { cam.Pos.Add(cam.Forward.Clone().Scale(-camSpeed * camSpeedScale * timing.DT())) update = true } // Left and right if input.KeyDown(sdl.K_d) { cross := gglm.Cross(&cam.Forward, &cam.WorldUp) cam.Pos.Add(cross.Normalize().Scale(camSpeed * camSpeedScale * timing.DT())) update = true } else if input.KeyDown(sdl.K_a) { cross := gglm.Cross(&cam.Forward, &cam.WorldUp) cam.Pos.Add(cross.Normalize().Scale(-camSpeed * camSpeedScale * timing.DT())) update = true } if update { cam.Update() updateAllProjViewMats(cam.ProjMat, cam.ViewMat) } } var ( renderDirLightShadows = true renderPointLightShadows = true renderSpotLightShadows = true rotatingCubeSpeedDeg1 float32 = 45 rotatingCubeSpeedDeg2 float32 = 120 rotatingCubeSpeedDeg3 float32 = 120 rotatingCubeTrMat1 = gglm.NewTrMatWithPos(-4, -1, 4) rotatingCubeTrMat2 = gglm.NewTrMatWithPos(-1, 0.5, 4) rotatingCubeTrMat3 = gglm.NewTrMatWithPos(5, 0.5, 4) ) func (g *Game) Render() { whiteMat.SetUnifVec3("camPos", &cam.Pos) containerMat.SetUnifVec3("camPos", &cam.Pos) groundMat.SetUnifVec3("camPos", &cam.Pos) palleteMat.SetUnifVec3("camPos", &cam.Pos) rotatingCubeTrMat1.Rotate(rotatingCubeSpeedDeg1*gglm.Deg2Rad*timing.DT(), 0, 1, 0) rotatingCubeTrMat2.Rotate(rotatingCubeSpeedDeg2*gglm.Deg2Rad*timing.DT(), 1, 1, 0) rotatingCubeTrMat3.Rotate(rotatingCubeSpeedDeg3*gglm.Deg2Rad*timing.DT(), 1, 1, 1) if renderDirLightShadows { g.renderDirectionalLightShadowmap() } if renderSpotLightShadows { g.renderSpotLightShadowmaps() } if renderPointLightShadows { g.renderPointLightShadowmaps() } if renderToBackBuffer { if renderDepthBuffer { g.RenderScene(&debugDepthMat) } else if hdrRendering { g.renderHdrFbo() } else { g.RenderScene(nil) if renderSkybox { g.DrawSkybox() } } } if renderToDemoFbo { g.renderDemoFbo() } } func (g *Game) renderDirectionalLightShadowmap() { // Set some uniforms dirLightProjViewMat := dirLight.GetProjViewMat() whiteMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat) containerMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat) groundMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat) palleteMat.SetUnifMat4("dirLightProjViewMat", &dirLightProjViewMat) depthMapMat.SetUnifMat4("projViewMat", &dirLightProjViewMat) // Start rendering dirLightDepthMapFbo.BindWithViewport() dirLightDepthMapFbo.Clear() // Culling front faces helps 'peter panning' when // drawing shadow maps, but works only for solids with a back face (i.e. quads won't cast shadows). // Check more here: https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping // // Some note that this is too troublesome and fails in many cases. Might be better to remove. gl.CullFace(gl.FRONT) g.RenderScene(&depthMapMat) gl.CullFace(gl.BACK) dirLightDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight)) if showDirLightDepthMapFbo { screenQuadMat.DiffuseTex = dirLightDepthMapFbo.Attachments[0].Id screenQuadMat.SetUnifVec2("offset", &dirLightDepthMapFboOffset) screenQuadMat.SetUnifVec2("scale", &dirLightDepthMapFboScale) screenQuadMat.Bind() window.Rend.DrawVertexArray(&screenQuadMat, &screenQuadVao, 0, 6) } } func (g *Game) renderSpotLightShadowmaps() { for i := 0; i < len(spotLights); i++ { l := &spotLights[i] indexStr := strconv.Itoa(i) projViewMatIndexStr := "spotLightProjViewMats[" + indexStr + "]" // Set render uniforms projViewMat := l.GetProjViewMat() whiteMat.SetUnifMat4(projViewMatIndexStr, &projViewMat) containerMat.SetUnifMat4(projViewMatIndexStr, &projViewMat) groundMat.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++ { 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(pointLightDepthMapFbo.Width), float32(pointLightDepthMapFbo.Height)) for j := 0; j < len(projViewMats); j++ { omnidirDepthMapMat.SetUnifMat4("cubemapProjViewMats["+strconv.Itoa(j)+"]", &projViewMats[j]) } g.RenderScene(&omnidirDepthMapMat) } pointLightDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight)) } func (g *Game) renderDemoFbo() { demoFbo.Bind() demoFbo.Clear() 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) } func (g *Game) renderHdrFbo() { hdrFbo.Bind() hdrFbo.Clear() g.RenderScene(nil) if renderSkybox { g.DrawSkybox() } hdrFbo.UnBind() tonemappedScreenQuadMat.DiffuseTex = hdrFbo.Attachments[0].Id window.Rend.DrawVertexArray(&tonemappedScreenQuadMat, &screenQuadVao, 0, 6) } func (g *Game) RenderScene(overrideMat *materials.Material) { tempModelMatrix := cubeModelMat.Clone() // See if we need overrides sunMat := &palleteMat chairMat := &palleteMat cubeMat := &containerMat groundMat := &groundMat if overrideMat != nil { sunMat = overrideMat chairMat = overrideMat cubeMat = overrideMat groundMat = overrideMat } // Draw dir light dirLightTrMat := gglm.NewTrMatId() window.Rend.DrawMesh(&sphereMesh, dirLightTrMat.Translate(0, 10, 0).Scale(0.1, 0.1, 0.1), sunMat) // Draw point lights for i := 0; i < len(pointLights); i++ { pl := &pointLights[i] plTrMat := gglm.NewTrMatId() window.Rend.DrawMesh(&cubeMesh, plTrMat.TranslateVec(&pl.Pos).Scale(0.1, 0.1, 0.1), sunMat) } // Chair window.Rend.DrawMesh(&chairMesh, tempModelMatrix, chairMat) // Ground groundTrMat := gglm.NewTrMatId() window.Rend.DrawMesh(&cubeMesh, groundTrMat.Translate(0, -3, 0).Scale(20, 1, 20), groundMat) // Cubes tempModelMatrix.Translate(-6, 0, 0) window.Rend.DrawMesh(&cubeMesh, tempModelMatrix, cubeMat) tempModelMatrix.Translate(0, -1, -4) window.Rend.DrawMesh(&cubeMesh, tempModelMatrix, cubeMat) // Rotating cubes window.Rend.DrawMesh(&cubeMesh, &rotatingCubeTrMat1, cubeMat) window.Rend.DrawMesh(&cubeMesh, &rotatingCubeTrMat2, cubeMat) window.Rend.DrawMesh(&cubeMesh, &rotatingCubeTrMat3, cubeMat) // Cubes generator // rowSize := 1 // for y := 0; y < rowSize; y++ { // for x := 0; x < rowSize; x++ { // tempModelMatrix.Translate(gglm.NewVec3(-6, 0, 0)) // window.Rend.DrawMesh(cubeMesh, tempModelMatrix, cubeMat) // } // tempModelMatrix.Translate(gglm.NewVec3(float32(rowSize), -1, 0)) // } } func (g *Game) DrawSkybox() { gl.Disable(gl.CULL_FACE) gl.DepthFunc(gl.LEQUAL) window.Rend.DrawCubemap(&skyboxMesh, &skyboxMat) gl.DepthFunc(gl.LESS) gl.Enable(gl.CULL_FACE) } func (g *Game) FrameEnd() { } func (g *Game) DeInit() { g.Win.Destroy() } func updateAllProjViewMats(projMat, viewMat gglm.Mat4) { projViewMat := projMat.Clone().Mul(&viewMat) unlitMat.SetUnifMat4("projViewMat", projViewMat) whiteMat.SetUnifMat4("projViewMat", projViewMat) containerMat.SetUnifMat4("projViewMat", projViewMat) groundMat.SetUnifMat4("projViewMat", projViewMat) palleteMat.SetUnifMat4("projViewMat", projViewMat) debugDepthMat.SetUnifMat4("projViewMat", projViewMat) // Update skybox projViewMat skyboxViewMat := viewMat.Clone() skyboxViewMat.Set(0, 3, 0) skyboxViewMat.Set(1, 3, 0) skyboxViewMat.Set(2, 3, 0) skyboxViewMat.Set(3, 0, 0) skyboxViewMat.Set(3, 1, 0) skyboxViewMat.Set(3, 2, 0) skyboxViewMat.Set(3, 3, 0) skyboxMat.SetUnifMat4("projViewMat", projMat.Clone().Mul(skyboxViewMat)) }