From 040228319eae881e380344c757a8ee9545969231 Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 13 Apr 2024 23:55:52 +0400 Subject: [PATCH] Depth fbo+renderer work+texture slots enum+optimize shaders+more --- buffers/framebuffer.go | 103 +++++++++++++- main.go | 255 +++++++++++++++------------------- materials/material.go | 60 +++++--- renderer/rend3dgl/rend3dgl.go | 46 +++++- renderer/renderer.go | 5 +- res/shaders/debug-depth.glsl | 9 +- res/shaders/simple-unlit.glsl | 10 +- res/shaders/simple.glsl | 10 +- res/shaders/skybox.glsl | 5 +- 9 files changed, 311 insertions(+), 192 deletions(-) diff --git a/buffers/framebuffer.go b/buffers/framebuffer.go index 05ae38e..be7e3cf 100755 --- a/buffers/framebuffer.go +++ b/buffers/framebuffer.go @@ -33,6 +33,7 @@ const ( FramebufferAttachmentDataFormat_R32Int FramebufferAttachmentDataFormat_RGBA8 FramebufferAttachmentDataFormat_SRGBA + FramebufferAttachmentDataFormat_DepthF32 FramebufferAttachmentDataFormat_Depth24Stencil8 ) @@ -43,7 +44,8 @@ func (f FramebufferAttachmentDataFormat) IsColorFormat() bool { } func (f FramebufferAttachmentDataFormat) IsDepthFormat() bool { - return f == FramebufferAttachmentDataFormat_Depth24Stencil8 + return f == FramebufferAttachmentDataFormat_Depth24Stencil8 || + f == FramebufferAttachmentDataFormat_DepthF32 } func (f FramebufferAttachmentDataFormat) GlInternalFormat() int32 { @@ -55,6 +57,8 @@ func (f FramebufferAttachmentDataFormat) GlInternalFormat() int32 { return gl.RGB8 case FramebufferAttachmentDataFormat_SRGBA: return gl.SRGB_ALPHA + case FramebufferAttachmentDataFormat_DepthF32: + return gl.DEPTH_COMPONENT case FramebufferAttachmentDataFormat_Depth24Stencil8: return gl.DEPTH24_STENCIL8 default: @@ -74,6 +78,9 @@ func (f FramebufferAttachmentDataFormat) GlFormat() uint32 { case FramebufferAttachmentDataFormat_SRGBA: return gl.RGBA + case FramebufferAttachmentDataFormat_DepthF32: + return gl.DEPTH_COMPONENT + case FramebufferAttachmentDataFormat_Depth24Stencil8: return gl.DEPTH_STENCIL @@ -91,6 +98,7 @@ type FramebufferAttachment struct { type Framebuffer struct { Id uint32 + ClearFlags uint32 Attachments []FramebufferAttachment ColorAttachmentsCount uint32 Width uint32 @@ -106,6 +114,13 @@ func (fbo *Framebuffer) BindWithViewport() { gl.Viewport(0, 0, int32(fbo.Width), int32(fbo.Height)) } +// Clear calls gl.Clear with the fob's clear flags. +// Note that the fbo must be complete and bound. +// Calling this without a bound fbo will clear something else, like your screen. +func (fbo *Framebuffer) Clear() { + gl.Clear(fbo.ClearFlags) +} + func (fbo *Framebuffer) UnBind() { gl.BindFramebuffer(gl.FRAMEBUFFER, 0) } @@ -201,6 +216,91 @@ func (fbo *Framebuffer) NewColorAttachment( fbo.UnBind() fbo.ColorAttachmentsCount++ + fbo.ClearFlags |= gl.COLOR_BUFFER_BIT + fbo.Attachments = append(fbo.Attachments, a) +} + +// SetNoColorBuffer sets the read and draw buffers of this fbo to 'NONE', +// which tells the graphics driver that we don't want a color buffer for this fbo. +// +// This is required because normally an fbo must have a color buffer to be considered complete, but by +// doing this we get marked as complete even without one. +// +// Usually used when you only care about some other buffer, like a depth buffer. +func (fbo *Framebuffer) SetNoColorBuffer() { + + if fbo.HasColorAttachment() { + logging.ErrLog.Fatalf("failed SetNoColorBuffer because framebuffer already has a color attachment\n") + } + + fbo.Bind() + gl.DrawBuffer(gl.NONE) + gl.ReadBuffer(gl.NONE) + fbo.UnBind() +} + +func (fbo *Framebuffer) NewDepthAttachment( + attachType FramebufferAttachmentType, + attachFormat FramebufferAttachmentDataFormat, +) { + + if fbo.HasDepthAttachment() { + logging.ErrLog.Fatalf("failed creating depth attachment for framebuffer because a depth attachment already exists\n") + } + + if !attachType.IsValid() { + logging.ErrLog.Fatalf("failed creating depth attachment for framebuffer due to unknown attachment type. Type=%d\n", attachType) + } + + 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: attachType, + Format: attachFormat, + } + + fbo.Bind() + + if attachType == FramebufferAttachmentType_Texture { + + // Create texture + 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, a.Id) + gl.TexImage2D(gl.TEXTURE_2D, 0, attachFormat.GlInternalFormat(), int32(fbo.Width), int32(fbo.Height), 0, attachFormat.GlFormat(), gl.FLOAT, nil) + + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) + gl.BindTexture(gl.TEXTURE_2D, 0) + + // Attach to fbo + gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, a.Id, 0) + + } else if attachType == FramebufferAttachmentType_Renderbuffer { + + // Create rbo + gl.GenRenderbuffers(1, &a.Id) + if a.Id == 0 { + logging.ErrLog.Fatalf("failed to generate render buffer for framebuffer. GlError=%d\n", gl.GetError()) + } + + gl.BindRenderbuffer(gl.RENDERBUFFER, a.Id) + gl.RenderbufferStorage(gl.RENDERBUFFER, uint32(attachFormat.GlInternalFormat()), int32(fbo.Width), int32(fbo.Height)) + gl.BindRenderbuffer(gl.RENDERBUFFER, 0) + + // Attach to fbo + gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, a.Id) + } + + fbo.UnBind() + fbo.ClearFlags |= gl.DEPTH_BUFFER_BIT fbo.Attachments = append(fbo.Attachments, a) } @@ -263,6 +363,7 @@ func (fbo *Framebuffer) NewDepthStencilAttachment( } fbo.UnBind() + fbo.ClearFlags |= gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT fbo.Attachments = append(fbo.Attachments, a) } diff --git a/main.go b/main.go index 03f3562..08fdd9e 100755 --- a/main.go +++ b/main.go @@ -7,16 +7,15 @@ import ( 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/entity" "github.com/bloeys/nmage/input" "github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/meshes" - "github.com/bloeys/nmage/registry" "github.com/bloeys/nmage/renderer/rend3dgl" "github.com/bloeys/nmage/timing" nmageimgui "github.com/bloeys/nmage/ui/imgui" @@ -104,9 +103,13 @@ var ( fboRenderDirectly = true fboScale = gglm.NewVec2(0.25, 0.25) fboOffset = gglm.NewVec2(0.75, -0.75) - fbo buffers.Framebuffer + demoFbo buffers.Framebuffer + depthMapFbo buffers.Framebuffer + + screenQuadVao buffers.VertexArray screenQuadMat *materials.Material + unlitMat *materials.Material whiteMat *materials.Material containerMat *materials.Material @@ -186,64 +189,14 @@ var ( ) type Game struct { + WinWidth int32 + WinHeight int32 Win *engine.Window ImGUIInfo nmageimgui.ImguiInfo } -type TransformComp struct { - entity.BaseComp - - Pos *gglm.Vec3 - Rot *gglm.Quat - Scale *gglm.Vec3 -} - -func (t *TransformComp) Name() string { - return "Transform Component" -} - -func Test() { - - // lvl := level.NewLevel("test level") - testRegistry := registry.NewRegistry[int](100) - - e1, e1Handle := testRegistry.New() - e1CompContainer := entity.NewCompContainer() - fmt.Printf("Entity 1: %+v; Handle: %+v; Index: %+v; Gen: %+v; Flags: %+v\n", e1, e1Handle, e1Handle.Index(), e1Handle.Generation(), e1Handle.Flags()) - - trComp := entity.GetComp[*TransformComp](&e1CompContainer) - fmt.Println("Get comp before adding any:", trComp) - - entity.AddComp(e1Handle, &e1CompContainer, &TransformComp{ - Pos: gglm.NewVec3(0, 0, 0), - Rot: gglm.NewQuatEulerXYZ(0, 0, 0), - Scale: gglm.NewVec3(0, 0, 0), - }) - trComp = entity.GetComp[*TransformComp](&e1CompContainer) - fmt.Println("Get transform comp:", trComp) - - e2, e2Handle := testRegistry.New() - e3, e3Handle := testRegistry.New() - e4, e4Handle := testRegistry.New() - fmt.Printf("Entity 2: %+v; Handle: %+v; Index: %+v; Gen: %+v; Flags: %+v\n", e2, e2Handle, e2Handle.Index(), e2Handle.Generation(), e2Handle.Flags()) - fmt.Printf("Entity 3: %+v; Handle: %+v; Index: %+v; Gen: %+v; Flags: %+v\n", e3, e3Handle, e3Handle.Index(), e3Handle.Generation(), e3Handle.Flags()) - fmt.Printf("Entity 4: %+v; Handle: %+v; Index: %+v; Gen: %+v; Flags: %+v\n", e4, e4Handle, e4Handle.Index(), e4Handle.Generation(), e4Handle.Flags()) - - *e2 = 1000 - fmt.Printf("Entity 2 value after registry get: %+v\n", *testRegistry.Get(e2Handle)) - - testRegistry.Free(e2Handle) - fmt.Printf("Entity 2 value after free: %+v\n", testRegistry.Get(e2Handle)) - - e5, e5Handle := testRegistry.New() - fmt.Printf("Entity 5: %+v; Handle: %+v; Index: %+v; Gen: %+v; Flags: %+v\n", e5, e5Handle, e5Handle.Index(), e5Handle.Generation(), e5Handle.Flags()) -} - func main() { - // Test() - // return - //Init engine err := engine.Init() if err != nil { @@ -264,6 +217,8 @@ func main() { 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) @@ -277,13 +232,11 @@ func (g *Game) handleWindowEvents(e sdl.Event) { case *sdl.WindowEvent: if e.Event == sdl.WINDOWEVENT_SIZE_CHANGED { - width := e.Data1 - height := e.Data2 - cam.AspectRatio = float32(width) / float32(height) - cam.Update() + g.WinWidth = e.Data1 + g.WinHeight = e.Data2 + cam.AspectRatio = float32(g.WinWidth) / float32(g.WinHeight) - palleteMat.SetUnifMat4("projMat", &cam.ProjMat) - debugDepthMat.SetUnifMat4("projMat", &cam.ProjMat) + updateProjViewMat() } } } @@ -394,15 +347,16 @@ func (g *Game) Init() { 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", fboScale) screenQuadMat.SetUnifVec2("offset", fboOffset) - screenQuadMat.SetUnifInt32("material.diffuse", 0) + screenQuadMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) unlitMat = materials.NewMaterial("Unlit mat", "./res/shaders/simple-unlit.glsl") - unlitMat.SetUnifInt32("material.diffuse", 0) - unlitMat.SetUnifMat4("projMat", &cam.ProjMat) + unlitMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) whiteMat = materials.NewMaterial("White mat", "./res/shaders/simple.glsl") whiteMat.Shininess = 64 @@ -410,11 +364,10 @@ func (g *Game) Init() { whiteMat.SpecularTex = blackTex.TexID whiteMat.NormalTex = blackTex.TexID whiteMat.EmissionTex = blackTex.TexID - whiteMat.SetUnifInt32("material.diffuse", 0) - whiteMat.SetUnifInt32("material.specular", 1) - // whiteMat.SetUnifInt32("material.normal", 2) - whiteMat.SetUnifInt32("material.emission", 3) - whiteMat.SetUnifMat4("projMat", &cam.ProjMat) + 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) @@ -427,11 +380,10 @@ func (g *Game) Init() { containerMat.SpecularTex = containerSpecularTex.TexID containerMat.NormalTex = blackTex.TexID containerMat.EmissionTex = blackTex.TexID - containerMat.SetUnifInt32("material.diffuse", 0) - containerMat.SetUnifInt32("material.specular", 1) - // containerMat.SetUnifInt32("material.normal", 2) - containerMat.SetUnifInt32("material.emission", 3) - containerMat.SetUnifMat4("projMat", &cam.ProjMat) + 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) @@ -444,51 +396,66 @@ func (g *Game) Init() { palleteMat.SpecularTex = blackTex.TexID palleteMat.NormalTex = blackTex.TexID palleteMat.EmissionTex = blackTex.TexID - palleteMat.SetUnifInt32("material.diffuse", 0) - palleteMat.SetUnifInt32("material.specular", 1) - // palleteMat.SetUnifInt32("material.normal", 2) - palleteMat.SetUnifInt32("material.emission", 3) - palleteMat.SetUnifMat4("projMat", &cam.ProjMat) + 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.diffuseColor", &dirLight.DiffuseColor) palleteMat.SetUnifVec3("dirLight.specularColor", &dirLight.SpecularColor) debugDepthMat = materials.NewMaterial("Debug depth mat", "./res/shaders/debug-depth.glsl") - debugDepthMat.SetUnifMat4("projMat", &cam.ProjMat) skyboxMat = materials.NewMaterial("Skybox mat", "./res/shaders/skybox.glsl") + skyboxMat.CubemapTex = skyboxCmap.TexID + skyboxMat.SetUnifInt32("skybox", int32(materials.TextureSlot_Cubemap)) - // Movement, scale and rotation + // Cube model mat translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0)) scaleMat := gglm.NewScaleMat(gglm.NewVec3(1, 1, 1)) rotMat := gglm.NewRotMat(gglm.NewQuatEuler(gglm.NewVec3(-90, -90, 0).AsRad())) cubeModelMat.Mul(translationMat.Mul(rotMat.Mul(scaleMat))) - g.updateLights() - updateViewMat() + // 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) - g.initFbo() + g.initFbos() + g.updateLights() + updateProjViewMat() } -func (g *Game) initFbo() { +func (g *Game) initFbos() { - fbWidth, fbHeight := g.Win.SDLWin.GLGetDrawableSize() - if fbWidth <= 0 || fbHeight <= 0 { - panic("what?") - } + // Demo fbo + demoFbo = buffers.NewFramebuffer(uint32(g.WinWidth), uint32(g.WinHeight)) - fbo = buffers.NewFramebuffer(uint32(fbWidth), uint32(fbHeight)) - - fbo.NewColorAttachment( + demoFbo.NewColorAttachment( buffers.FramebufferAttachmentType_Texture, buffers.FramebufferAttachmentDataFormat_SRGBA, ) - fbo.NewDepthStencilAttachment( + demoFbo.NewDepthStencilAttachment( buffers.FramebufferAttachmentType_Renderbuffer, buffers.FramebufferAttachmentDataFormat_Depth24Stencil8, ) + + assert.T(demoFbo.IsComplete(), "Demo fbo is not complete after init") + + // Depth map fbo + depthMapFbo = buffers.NewFramebuffer(1024, 1024) + depthMapFbo.SetNoColorBuffer() + depthMapFbo.NewDepthAttachment( + buffers.FramebufferAttachmentType_Texture, + buffers.FramebufferAttachmentDataFormat_DepthF32, + ) + + assert.T(depthMapFbo.IsComplete(), "Depth map fbo is not complete after init") } func (g *Game) updateLights() { @@ -586,10 +553,10 @@ func (g *Game) showDebugWindow() { // Camera imgui.Text("Camera") if imgui.DragFloat3("Cam Pos", &cam.Pos.Data) { - updateViewMat() + updateProjViewMat() } if imgui.DragFloat3("Cam Forward", &cam.Forward.Data) { - updateViewMat() + updateProjViewMat() } imgui.Spacing() @@ -779,7 +746,7 @@ func (g *Game) updateCameraLookAround() { // Update cam forward cam.UpdateRotation(pitch, yaw) - updateViewMat() + updateProjViewMat() } func (g *Game) updateCameraPos() { @@ -810,17 +777,32 @@ func (g *Game) updateCameraPos() { } if update { - updateViewMat() + updateProjViewMat() } } func (g *Game) Render() { - if renderToFbo { - fbo.Bind() - gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) + if !renderToFbo { + g.RenderScene() + return } + demoFbo.Bind() + demoFbo.Clear() + g.RenderScene() + demoFbo.UnBind() + + if fboRenderDirectly { + g.RenderScene() + } + + screenQuadMat.DiffuseTex = demoFbo.Attachments[0].Id + window.Rend.DrawVertexArray(screenQuadMat, &screenQuadVao, 0, 6) +} + +func (g *Game) RenderScene() { + tempModelMatrix := cubeModelMat.Clone() whiteMat.SetUnifVec3("camPos", &cam.Pos) @@ -837,27 +819,27 @@ func (g *Game) Render() { } // Draw dir light - window.Rend.Draw(sphereMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(0, 10, 0)).Scale(gglm.NewVec3(0.1, 0.1, 0.1)), sunMat) + window.Rend.DrawMesh(sphereMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(0, 10, 0)).Scale(gglm.NewVec3(0.1, 0.1, 0.1)), sunMat) // Draw point lights for i := 0; i < len(pointLights); i++ { pl := &pointLights[i] - window.Rend.Draw(cubeMesh, gglm.NewTrMatId().Translate(&pl.Pos).Scale(gglm.NewVec3(0.1, 0.1, 0.1)), sunMat) + window.Rend.DrawMesh(cubeMesh, gglm.NewTrMatId().Translate(&pl.Pos).Scale(gglm.NewVec3(0.1, 0.1, 0.1)), sunMat) } // Chair - window.Rend.Draw(chairMesh, tempModelMatrix, chairMat) + window.Rend.DrawMesh(chairMesh, tempModelMatrix, chairMat) // Ground - window.Rend.Draw(cubeMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(0, -3, 0)).Scale(gglm.NewVec3(20, 1, 20)), cubeMat) + window.Rend.DrawMesh(cubeMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(0, -3, 0)).Scale(gglm.NewVec3(20, 1, 20)), cubeMat) // Cubes rowSize := 1 for y := 0; y < rowSize; y++ { for x := 0; x < rowSize; x++ { tempModelMatrix.Translate(gglm.NewVec3(-6, 0, 0)) - window.Rend.Draw(cubeMesh, tempModelMatrix, cubeMat) + window.Rend.DrawMesh(cubeMesh, tempModelMatrix, cubeMat) } tempModelMatrix.Translate(gglm.NewVec3(float32(rowSize), -1, 0)) } @@ -865,46 +847,14 @@ func (g *Game) Render() { if drawSkybox { g.DrawSkybox() } - - if renderToFbo { - - fbo.UnBind() - - if fboRenderDirectly { - renderToFbo = false - g.Render() - renderToFbo = true - } - - screenQuadMat.DiffuseTex = fbo.Attachments[0].Id - screenQuadMat.Bind() - gl.DrawArrays(gl.TRIANGLES, 0, 6) - } } func (g *Game) DrawSkybox() { gl.Disable(gl.CULL_FACE) gl.DepthFunc(gl.LEQUAL) - skyboxMesh.Vao.Bind() - skyboxMat.Bind() - gl.ActiveTexture(gl.TEXTURE0) - gl.BindTexture(gl.TEXTURE_CUBE_MAP, skyboxCmap.TexID) - viewMat := cam.ViewMat.Clone() - viewMat.Set(0, 3, 0) - viewMat.Set(1, 3, 0) - viewMat.Set(2, 3, 0) - viewMat.Set(3, 0, 0) - viewMat.Set(3, 1, 0) - viewMat.Set(3, 2, 0) - viewMat.Set(3, 3, 0) - - skyboxMat.SetUnifMat4("viewMat", viewMat) - skyboxMat.SetUnifMat4("projMat", &cam.ProjMat) - for i := 0; i < len(skyboxMesh.SubMeshes); i++ { - gl.DrawElementsBaseVertexWithOffset(gl.TRIANGLES, skyboxMesh.SubMeshes[i].IndexCount, gl.UNSIGNED_INT, uintptr(skyboxMesh.SubMeshes[i].BaseIndex), skyboxMesh.SubMeshes[i].BaseVertex) - } + window.Rend.DrawCubemap(skyboxMesh, skyboxMat) gl.DepthFunc(gl.LESS) gl.Enable(gl.CULL_FACE) @@ -917,11 +867,28 @@ func (g *Game) DeInit() { g.Win.Destroy() } -func updateViewMat() { +func updateProjViewMat() { + cam.Update() - unlitMat.SetUnifMat4("viewMat", &cam.ViewMat) - whiteMat.SetUnifMat4("viewMat", &cam.ViewMat) - containerMat.SetUnifMat4("viewMat", &cam.ViewMat) - palleteMat.SetUnifMat4("viewMat", &cam.ViewMat) - debugDepthMat.SetUnifMat4("viewMat", &cam.ViewMat) + + projViewMat := cam.ProjMat.Clone() + projViewMat.Mul(&cam.ViewMat) + + unlitMat.SetUnifMat4("projViewMat", projViewMat) + whiteMat.SetUnifMat4("projViewMat", projViewMat) + containerMat.SetUnifMat4("projViewMat", projViewMat) + palleteMat.SetUnifMat4("projViewMat", projViewMat) + debugDepthMat.SetUnifMat4("projViewMat", projViewMat) + + // Update skybox projViewMat + viewMat := cam.ViewMat.Clone() + viewMat.Set(0, 3, 0) + viewMat.Set(1, 3, 0) + viewMat.Set(2, 3, 0) + viewMat.Set(3, 0, 0) + viewMat.Set(3, 1, 0) + viewMat.Set(3, 2, 0) + viewMat.Set(3, 3, 0) + skyboxMat.SetUnifMat4("projViewMat", cam.ProjMat.Clone().Mul(viewMat)) + } diff --git a/materials/material.go b/materials/material.go index b23c36f..21311a6 100755 --- a/materials/material.go +++ b/materials/material.go @@ -8,6 +8,16 @@ import ( "github.com/go-gl/gl/v4.1-core/gl" ) +type TextureSlot uint32 + +const ( + TextureSlot_Diffuse TextureSlot = 0 + TextureSlot_Specular TextureSlot = 1 + TextureSlot_Normal TextureSlot = 2 + TextureSlot_Emission TextureSlot = 3 + TextureSlot_Cubemap TextureSlot = 10 +) + type Material struct { Name string ShaderProg shaders.ShaderProgram @@ -21,41 +31,45 @@ type Material struct { NormalTex uint32 EmissionTex uint32 + // Shininess of specular highlights Shininess float32 + + // Cubemap + CubemapTex uint32 } func (m *Material) Bind() { gl.UseProgram(m.ShaderProg.ID) - gl.ActiveTexture(gl.TEXTURE0) - gl.BindTexture(gl.TEXTURE_2D, m.DiffuseTex) + if m.DiffuseTex != 0 { + gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_Diffuse)) + gl.BindTexture(gl.TEXTURE_2D, m.DiffuseTex) + } - gl.ActiveTexture(gl.TEXTURE1) - gl.BindTexture(gl.TEXTURE_2D, m.SpecularTex) + if m.SpecularTex != 0 { + gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_Specular)) + gl.BindTexture(gl.TEXTURE_2D, m.SpecularTex) + } - gl.ActiveTexture(gl.TEXTURE2) - gl.BindTexture(gl.TEXTURE_2D, m.NormalTex) + if m.NormalTex != 0 { + gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_Normal)) + gl.BindTexture(gl.TEXTURE_2D, m.NormalTex) + } - gl.ActiveTexture(gl.TEXTURE3) - gl.BindTexture(gl.TEXTURE_2D, m.EmissionTex) + if m.EmissionTex != 0 { + gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_Emission)) + gl.BindTexture(gl.TEXTURE_2D, m.EmissionTex) + } + + if m.CubemapTex != 0 { + gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_Cubemap)) + gl.BindTexture(gl.TEXTURE_CUBE_MAP, m.CubemapTex) + } } func (m *Material) UnBind() { gl.UseProgram(0) - - //TODO: Should we unbind textures here? Are these two lines needed? - // gl.ActiveTexture(gl.TEXTURE0) - // gl.BindTexture(gl.TEXTURE_2D, 0) - - // gl.ActiveTexture(gl.TEXTURE1) - // gl.BindTexture(gl.TEXTURE_2D, 0) - - // gl.ActiveTexture(gl.TEXTURE2) - // gl.BindTexture(gl.TEXTURE_2D, 0) - - // gl.ActiveTexture(gl.TEXTURE3) - // gl.BindTexture(gl.TEXTURE_2D, 0) } func (m *Material) GetAttribLoc(attribName string) int32 { @@ -132,7 +146,7 @@ func NewMaterial(matName, shaderPath string) *Material { shdrProg, err := shaders.LoadAndCompileCombinedShader(shaderPath) if err != nil { - logging.ErrLog.Fatalln("Failed to create new material. Err: ", err) + logging.ErrLog.Fatalf("Failed to create new material '%s'. Err: %s\n", matName, err.Error()) } return &Material{Name: matName, ShaderProg: shdrProg, UnifLocs: make(map[string]int32), AttribLocs: make(map[string]int32)} @@ -142,7 +156,7 @@ func NewMaterialSrc(matName string, shaderSrc []byte) *Material { shdrProg, err := shaders.LoadAndCompileCombinedShaderSrc(shaderSrc) if err != nil { - logging.ErrLog.Fatalln("Failed to create new material. Err: ", err) + logging.ErrLog.Fatalf("Failed to create new material '%s'. Err: %s\n", matName, err.Error()) } return &Material{Name: matName, ShaderProg: shdrProg, UnifLocs: make(map[string]int32), AttribLocs: make(map[string]int32)} diff --git a/renderer/rend3dgl/rend3dgl.go b/renderer/rend3dgl/rend3dgl.go index be6d200..aa2da34 100755 --- a/renderer/rend3dgl/rend3dgl.go +++ b/renderer/rend3dgl/rend3dgl.go @@ -2,6 +2,7 @@ package rend3dgl import ( "github.com/bloeys/gglm/gglm" + "github.com/bloeys/nmage/buffers" "github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/meshes" "github.com/bloeys/nmage/renderer" @@ -11,23 +12,56 @@ import ( var _ renderer.Render = &Rend3DGL{} type Rend3DGL struct { + BoundVao *buffers.VertexArray BoundMesh *meshes.Mesh BoundMat *materials.Material } -func (r3d *Rend3DGL) Draw(mesh *meshes.Mesh, trMat *gglm.TrMat, mat *materials.Material) { +func (r *Rend3DGL) DrawMesh(mesh *meshes.Mesh, modelMat *gglm.TrMat, mat *materials.Material) { - if mesh != r3d.BoundMesh { + if mesh != r.BoundMesh { mesh.Vao.Bind() - r3d.BoundMesh = mesh + r.BoundMesh = mesh } - if mat != r3d.BoundMat { + if mat != r.BoundMat { mat.Bind() - r3d.BoundMat = mat + r.BoundMat = mat } - mat.SetUnifMat4("modelMat", &trMat.Mat4) + mat.SetUnifMat4("modelMat", &modelMat.Mat4) + + for i := 0; i < len(mesh.SubMeshes); i++ { + gl.DrawElementsBaseVertexWithOffset(gl.TRIANGLES, mesh.SubMeshes[i].IndexCount, gl.UNSIGNED_INT, uintptr(mesh.SubMeshes[i].BaseIndex), mesh.SubMeshes[i].BaseVertex) + } +} + +func (r *Rend3DGL) DrawVertexArray(mat *materials.Material, vao *buffers.VertexArray, firstElement int32, elementCount int32) { + + if vao != r.BoundVao { + vao.Bind() + r.BoundVao = vao + } + + if mat != r.BoundMat { + mat.Bind() + r.BoundMat = mat + } + + gl.DrawArrays(gl.TRIANGLES, firstElement, elementCount) +} + +func (r *Rend3DGL) DrawCubemap(mesh *meshes.Mesh, mat *materials.Material) { + + if mesh != r.BoundMesh { + mesh.Vao.Bind() + r.BoundMesh = mesh + } + + if mat != r.BoundMat { + mat.Bind() + r.BoundMat = mat + } for i := 0; i < len(mesh.SubMeshes); i++ { gl.DrawElementsBaseVertexWithOffset(gl.TRIANGLES, mesh.SubMeshes[i].IndexCount, gl.UNSIGNED_INT, uintptr(mesh.SubMeshes[i].BaseIndex), mesh.SubMeshes[i].BaseVertex) diff --git a/renderer/renderer.go b/renderer/renderer.go index 333a348..2b9ea81 100755 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -2,11 +2,14 @@ package renderer import ( "github.com/bloeys/gglm/gglm" + "github.com/bloeys/nmage/buffers" "github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/meshes" ) type Render interface { - Draw(mesh *meshes.Mesh, trMat *gglm.TrMat, mat *materials.Material) + DrawMesh(mesh *meshes.Mesh, trMat *gglm.TrMat, mat *materials.Material) + DrawVertexArray(mat *materials.Material, vao *buffers.VertexArray, firstElement int32, count int32) + DrawCubemap(mesh *meshes.Mesh, mat *materials.Material) FrameEnd() } diff --git a/res/shaders/debug-depth.glsl b/res/shaders/debug-depth.glsl index 42dda90..740a861 100755 --- a/res/shaders/debug-depth.glsl +++ b/res/shaders/debug-depth.glsl @@ -13,17 +13,18 @@ out vec3 fragPos; //MVP = Model View Projection uniform mat4 modelMat; -uniform mat4 viewMat; -uniform mat4 projMat; +uniform mat4 projViewMat; void main() { vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn; vertUV0 = vertUV0In; vertColor = vertColorIn; - fragPos = vec3(modelMat * vec4(vertPosIn, 1.0)); - gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0); + vec4 modelVert = modelMat * vec4(vertPosIn, 1); + fragPos = modelVert.xyz; + + gl_Position = projViewMat * modelVert; } //shader:fragment diff --git a/res/shaders/simple-unlit.glsl b/res/shaders/simple-unlit.glsl index 51097c7..e65c234 100755 --- a/res/shaders/simple-unlit.glsl +++ b/res/shaders/simple-unlit.glsl @@ -13,22 +13,22 @@ out vec3 fragPos; //MVP = Model View Projection uniform mat4 modelMat; -uniform mat4 viewMat; -uniform mat4 projMat; +uniform mat4 projViewMat; void main() { // @TODO: Calculate this on the CPU and send it as a uniform - + // // This produces the normal matrix that multiplies with the model normal to produce the // world space normal. Based on 'One last thing' section from: https://learnopengl.com/Lighting/Basic-Lighting vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn; vertUV0 = vertUV0In; vertColor = vertColorIn; - fragPos = vec3(modelMat * vec4(vertPosIn, 1.0)); - gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0); + vec4 modelVert = modelMat * vec4(vertPosIn, 1); + fragPos = modelVert.xyz; + gl_Position = projViewMat * modelVert; } //shader:fragment diff --git a/res/shaders/simple.glsl b/res/shaders/simple.glsl index 08d3fc7..6bd1aa0 100755 --- a/res/shaders/simple.glsl +++ b/res/shaders/simple.glsl @@ -13,22 +13,22 @@ out vec3 fragPos; //MVP = Model View Projection uniform mat4 modelMat; -uniform mat4 viewMat; -uniform mat4 projMat; +uniform mat4 projViewMat; void main() { // @TODO: Calculate this on the CPU and send it as a uniform - + // // This produces the normal matrix that multiplies with the model normal to produce the // world space normal. Based on 'One last thing' section from: https://learnopengl.com/Lighting/Basic-Lighting vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn; vertUV0 = vertUV0In; vertColor = vertColorIn; - fragPos = vec3(modelMat * vec4(vertPosIn, 1.0)); - gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0); + vec4 modelVert = modelMat * vec4(vertPosIn, 1); + fragPos = modelVert.xyz; + gl_Position = projViewMat * modelVert; } //shader:fragment diff --git a/res/shaders/skybox.glsl b/res/shaders/skybox.glsl index 149825b..fb45fab 100755 --- a/res/shaders/skybox.glsl +++ b/res/shaders/skybox.glsl @@ -8,13 +8,12 @@ layout(location=3) in vec3 vertColorIn; out vec3 vertUV0; -uniform mat4 viewMat; -uniform mat4 projMat; +uniform mat4 projViewMat; void main() { vertUV0 = vec3(vertPosIn.x, vertPosIn.y, -vertPosIn.z); - vec4 pos = projMat * viewMat * vec4(vertPosIn, 1.0); + vec4 pos = projViewMat * vec4(vertPosIn, 1.0); gl_Position = pos.xyww; }