#include "SceneRenderer.h" #include "src/Rendering/Shaders/ShaderManager.h" #include "src/Resource/Project.h" #include "src/Resource/ResourceManager.h" #include "src/Resource/UI.h" #include "src/Scene/Components/BSPBrushComponent.h" #include "src/Scene/Components/ModelComponent.h" #include "src/Scene/Components/ParentComponent.h" #include "src/Scene/Components/ParticleEmitterComponent.h" #include "src/Scene/Components/SpriteComponent.h" #include #include "src/Scene/Components/UIComponent.h" #include #include #include #include #include "src/UI/Inspector.h" #include #include "src/Resource/SkyResource.h" namespace Nuake { std::vector SceneRenderer::mJitterOffsets = []{ return std::vector { Vector2(0.500000, 0.333333), Vector2(0.250000, 0.666667), Vector2(0.750000, 0.111111), Vector2(0.125000, 0.444444), Vector2(0.625000, 0.777778), Vector2(0.375000, 0.222222), Vector2(0.875000, 0.555556), Vector2(0.062500, 0.888889), Vector2(0.562500, 0.037037), Vector2(0.312500, 0.370370), Vector2(0.812500, 0.703704), Vector2(0.187500, 0.148148), Vector2(0.687500, 0.481481), Vector2(0.437500, 0.814815), Vector2(0.937500, 0.259259), Vector2(0.031250, 0.592593) }; }(); SceneRenderer::SceneRenderer() { Init(); } void SceneRenderer::Init() { const auto defaultResolution = Vector2(1920, 1080); auto entityTexture = CreateRef(defaultResolution, GL_RED_INTEGER, GL_R32I, GL_INT); entityTexture->SetParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); mGBuffer = CreateScope(false, defaultResolution); mGBuffer->SetTexture(CreateRef(defaultResolution, GL_DEPTH_COMPONENT), GL_DEPTH_ATTACHMENT); // Depth mGBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT0); // Albedo mGBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT1); // Normal mGBuffer->SetTexture(CreateRef(defaultResolution, GL_RGBA), GL_COLOR_ATTACHMENT2); // Material + unlit mGBuffer->SetTexture(entityTexture, GL_COLOR_ATTACHMENT3); // Entity ID mGBuffer->SetTexture(CreateRef(defaultResolution, GL_RED, GL_R16F, GL_FLOAT), GL_COLOR_ATTACHMENT4); // Emissive mGBuffer->SetTexture(CreateRef(defaultResolution, GL_RG, GL_RG16F, GL_FLOAT), GL_COLOR_ATTACHMENT5); // Velocity mGBuffer->SetTexture(CreateRef(defaultResolution, GL_RG, GL_RG, GL_UNSIGNED_BYTE), GL_COLOR_ATTACHMENT6); // UV mShadingBuffer = CreateScope(true, defaultResolution); mShadingBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB, GL_RGB16F, GL_FLOAT)); mShadingBuffer->SetTexture(CreateRef(defaultResolution, GL_DEPTH_COMPONENT), GL_DEPTH_ATTACHMENT); // Depth mTempFrameBuffer = CreateScope(false, defaultResolution); mTempFrameBuffer->SetTexture(CreateRef(defaultResolution, GL_RGBA), GL_COLOR_ATTACHMENT0); mToneMapBuffer = CreateScope(false, defaultResolution); mToneMapBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT0); mBarrelDistortionBuffer = CreateScope(false, defaultResolution); mBarrelDistortionBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT0); mVignetteBuffer = CreateScope(false, defaultResolution); mVignetteBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT0); mDOFBuffer = CreateScope(false, defaultResolution); mDOFBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT0); mOutlineBuffer = CreateScope(false, defaultResolution); mOutlineBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT0); mDisplayDepthBuffer = CreateScope(false, defaultResolution); mDisplayDepthBuffer->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT0); mDisplayMotionVector = CreateScope(false, defaultResolution); mDisplayMotionVector->SetTexture(CreateRef(defaultResolution, GL_RGB), GL_COLOR_ATTACHMENT0); mPreviousFrame = CreateScope(false, defaultResolution); mPreviousFrame->SetTexture(CreateRef(defaultResolution, GL_RGBA), GL_COLOR_ATTACHMENT0); // Generate debug meshes std::vector lineVertices { { Vector3(0, 0, 0), Vector2(0, 0), Vector3(0, 0, 0) }, { Vector3(1, 1, 1), Vector2(0, 0), Vector3(0, 0, 0) } }; std::vector lineIndices { 0, 1 }; // Debug shapes mLineMesh = CreateRef(); mLineMesh->AddSurface(lineVertices, lineIndices); mBoxGizmo = CreateRef(); mBoxGizmo->CreateMesh(); mSphereGizmo = CreateRef(); mSphereGizmo->CreateMesh(); mCylinderGizmo = CreateRef(); mCylinderGizmo->CreateMesh(); mCapsuleGizmo = CreateRef(); mCapsuleGizmo->CreateMesh(); mDebugLines = std::vector(); mDebugShapes = std::vector(); mScaledJitterOffets.reserve(mJitterOffsets.size()); UpdateJitterOffsets(defaultResolution); } void SceneRenderer::Cleanup() { } void SceneRenderer::Update(const Timestep time, bool isEditorUpdate) { // Delete debug shapes that are dead if (mDebugLines.size() > 0) { std::erase_if(mDebugLines, [](const DebugLine& line) { return line.Life < 0.0f; }); for (auto& line : mDebugLines) { line.Life -= time; } } if (mDebugShapes.size() > 0) { std::erase_if(mDebugShapes, [](const DebugShape& shape) { return shape.Life < 0.0f; }); for (auto& shape : mDebugShapes) { shape.Life -= time; } } } void SceneRenderer::BeginRenderScene(const Matrix4& projection, const Matrix4& view, const Vector3& camPos) { mProjection = projection; mView = view; mCamPos = camPos; } /// /// Renders a scene to a framebuffer. The size of the framebuffer will be used. /// /// Scene to render /// Framebuffer to render the scene to. Should be in the right size void SceneRenderer::RenderScene(Scene& scene, FrameBuffer& framebuffer, bool renderUI) { return; ZoneScoped; // Renders all shadow maps ShadowPass(scene); const Vector2 framebufferResolution = framebuffer.GetSize(); if (framebufferResolution != GetGBuffer().GetSize()) { UpdateJitterOffsets(framebuffer.GetSize()); } if (renderUI) { mGBuffer->QueueResize(framebufferResolution); } GBufferPass(scene); if (mPreviousFrame->GetSize() != GetGBuffer().GetSize()) { mPreviousFrame->QueueResize(framebufferResolution); // UpdateJitterOffsets(framebuffer.GetSize()); } mDisplayMotionVector->QueueResize(framebufferResolution); mDisplayMotionVector->Bind(); { auto shader = ShaderManager::GetShader("Resources/Shaders/displayVelocity.shader"); shader->Bind(); GetGBuffer().GetTexture(GL_COLOR_ATTACHMENT5)->Bind(0); shader->SetUniform("u_Source", 0); shader->SetUniform("u_Resolution", framebufferResolution); mDisplayMotionVector->Clear(); Renderer::DrawQuad(); } mDisplayMotionVector->Unbind(); // Save previous Matrix auto modelView = scene.m_Registry.view(); for (auto e : modelView) { auto [transform, mesh, visibility] = modelView.get(e); Entity entity = { (entt::entity)e, Engine::GetCurrentScene().get() }; if (!entity.IsValid()) continue; if (mesh.ModelResource && visibility.Visible) { transform.PreviousTransform = mProjection * mView * transform.GetGlobalTransform(); } } if (renderUI) { mTempFrameBuffer->QueueResize(framebuffer.GetSize()); } // SSAO const auto& sceneEnv = scene.GetEnvironment(); if (sceneEnv->SSAOEnabled) { sceneEnv->mSSAO->Resize(framebuffer.GetSize()); sceneEnv->mSSAO->Draw(mGBuffer.get(), mProjection, mView); } else { sceneEnv->mSSAO->Clear(); } if (renderUI) { mShadingBuffer->QueueResize(framebufferResolution); } ShadingPass(scene, framebuffer.GetTexture(GL_COLOR_ATTACHMENT0)); // Blit depth buffer glBindFramebuffer(GL_READ_FRAMEBUFFER, mGBuffer->GetRenderID()); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShadingBuffer->GetRenderID()); glBlitFramebuffer( 0, 0, mShadingBuffer->GetSize().x, mShadingBuffer->GetSize().y, // Source rectangle (x0, y0, x1, y1) 0, 0, mShadingBuffer->GetSize().x, mShadingBuffer->GetSize().y, // Destination rectangle (x0, y0, x1, y1) GL_DEPTH_BUFFER_BIT, // Bitmask indicating which buffers to copy GL_NEAREST // Filtering mode (NEAREST or LINEAR) ); // World Space UI if (renderUI) { DebugRendererPass(scene); } Ref finalOutput = mShadingBuffer->GetTexture(); if (scene.GetEnvironment()->BloomEnabled) { sceneEnv->mBloom->SetSource(mShadingBuffer->GetTexture()); sceneEnv->mBloom->Resize(framebuffer.GetSize()); sceneEnv->mBloom->Draw(); finalOutput = sceneEnv->mBloom->GetOutput(); } const auto view = scene.m_Registry.view(); auto lightList = std::vector(); for (auto l : view) { auto& lc = view.get(l); if (lc.Type == Directional && lc.IsVolumetric && lc.CastShadows) lightList.push_back(lc); } glDepthMask(false); if (sceneEnv->VolumetricEnabled) { sceneEnv->mVolumetric->Resize(framebuffer.GetSize()); sceneEnv->mVolumetric->SetDepth(mGBuffer->GetTexture(GL_DEPTH_ATTACHMENT).get()); sceneEnv->mVolumetric->Draw(mProjection, mView, mCamPos, lightList); // combine framebuffer.Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/combine.shader"); shader->Bind(); shader->SetUniform("u_Source", finalOutput.get(), 0); shader->SetUniform("u_Source2", sceneEnv->mVolumetric->GetFinalOutput().get(), 1); Renderer::DrawQuad(); } framebuffer.Unbind(); } else { framebuffer.Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/copy.shader"); shader->Bind(); shader->SetUniform("u_Source", finalOutput.get(), 0); Renderer::DrawQuad(); } } finalOutput = framebuffer.GetTexture(); // Copy final output to target framebuffer if (renderUI) { mToneMapBuffer->QueueResize(framebufferResolution); } mToneMapBuffer->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/tonemap.shader"); shader->Bind(); shader->SetUniform("u_Exposure", sceneEnv->Exposure); shader->SetUniform("u_Gamma", sceneEnv->Gamma); Ref previousTexture = mPreviousFrame->GetTexture(GL_COLOR_ATTACHMENT0); previousTexture->SetMagnificationFilter(SamplerFilter::LINEAR); previousTexture->SetMinificationFilter(SamplerFilter::LINEAR); previousTexture->SetWrapping(SamplerWrapping::CLAMP_TO_EDGE); shader->SetUniform("u_PreviousFrame", previousTexture.get(), 4); shader->SetUniform("u_VelocityFrame", GetGBuffer().GetTexture(GL_COLOR_ATTACHMENT5).get(), 7); shader->SetUniform("u_TAAFactor", TAAFactor); shader->SetUniform("u_Source", finalOutput.get()); Renderer::DrawQuad(); } mToneMapBuffer->Unbind(); mPreviousFrame->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/copy.shader"); shader->Bind(); shader->SetUniform("u_Source", mToneMapBuffer->GetTexture(GL_COLOR_ATTACHMENT0).get(), 1); Renderer::DrawQuad(); } mPreviousFrame->Unbind(); ProjectSettings projectSettings = Engine::GetProject()->Settings; mOutlineBuffer->QueueResize(framebufferResolution); mOutlineBuffer->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/outline.shader"); shader->Bind(); shader->SetUniform("u_EntityID", mOutlineEntityID == -1 ? -1 : mOutlineEntityID + 1); shader->SetUniform("u_EntityTexture", mGBuffer->GetTexture(GL_COLOR_ATTACHMENT3).get(), 0); shader->SetUniform("u_OutlineColor", projectSettings.PrimaryColor); shader->SetUniform("u_Depth", mGBuffer->GetTexture(GL_DEPTH_ATTACHMENT).get(), 1); shader->SetUniform("u_Radius", projectSettings.OutlineRadius * projectSettings.ResolutionScale); Renderer::DrawQuad(); } mOutlineBuffer->Unbind(); framebuffer.Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/add.shader"); shader->Bind(); shader->SetUniform("u_Source", mToneMapBuffer->GetTexture().get(), 0); shader->SetUniform("u_Source2", mOutlineBuffer->GetTexture().get(), 1); Renderer::DrawQuad(); } framebuffer.Unbind(); mToneMapBuffer->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/copy.shader"); shader->Bind(); shader->SetUniform("u_Source", framebuffer.GetTexture().get(), 1); Renderer::DrawQuad(); } mToneMapBuffer->Unbind(); if (sceneEnv->SSREnabled && renderUI) { sceneEnv->mSSR->Resize(framebufferResolution); sceneEnv->mSSR->Draw(mGBuffer.get(), framebuffer.GetTexture(), mView, mProjection, scene.GetCurrentCamera()); framebuffer.Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/combine.shader"); shader->Bind(); shader->SetUniform("u_Source", mToneMapBuffer->GetTexture().get(), 0); shader->SetUniform("u_Source2", sceneEnv->mSSR->OutputFramebuffer->GetTexture().get(), 1); Renderer::DrawQuad(); } framebuffer.Unbind(); } else { framebuffer.Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/copy.shader"); shader->Bind(); shader->SetUniform("u_Source", mToneMapBuffer->GetTexture().get(), 0); Renderer::DrawQuad(); } framebuffer.Unbind(); } mDOFBuffer->QueueResize(framebufferResolution); mDOFBuffer->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/dof.shader"); shader->Bind(); auto cam = scene.GetCurrentCamera(); shader->SetUniform("znear", cam->GetNear()); shader->SetUniform("zfar", cam->GetFar()); shader->SetUniform("focalDepth", sceneEnv->DOFFocalDepth); shader->SetUniform("focalLength", sceneEnv->DOFFocalLength); shader->SetUniform("fstop", sceneEnv->DOFFstop); shader->SetUniform("showFocus", sceneEnv->DOFShowFocus); shader->SetUniform("autofocus", sceneEnv->DOFAutoFocus); shader->SetUniform("samples", sceneEnv->DOFSamples); shader->SetUniform("manualdof", sceneEnv->DOFManualFocus); shader->SetUniform("rings", static_cast(sceneEnv->DOFrings)); shader->SetUniform("ndofstart", sceneEnv->DOFStart); shader->SetUniform("ndofdist", sceneEnv->DOFDist); shader->SetUniform("fdofstart", sceneEnv->DOFStart); shader->SetUniform("fdofdist", sceneEnv->DOFDist); shader->SetUniform("CoC", sceneEnv->DOFCoc); shader->SetUniform("maxblur", sceneEnv->DOFMaxBlue); shader->SetUniform("threshold", sceneEnv->DOFThreshold); shader->SetUniform("gain", sceneEnv->DOFGain); shader->SetUniform("bias", sceneEnv->DOFBias); shader->SetUniform("fringe", sceneEnv->DOFFringe); shader->SetUniform("namount", sceneEnv->DOFNAmmount); shader->SetUniform("dbsize", sceneEnv->DOFDbSize); shader->SetUniform("feather", sceneEnv->DOFFeather); shader->SetUniform("height", static_cast(finalOutput->GetHeight())); shader->SetUniform("width", static_cast(finalOutput->GetWidth())); shader->SetUniform("depthTex", mGBuffer->GetTexture(GL_DEPTH_ATTACHMENT).get(), 0); shader->SetUniform("renderTex", finalOutput.get(), 1); Renderer::DrawQuad(); } mDOFBuffer->Unbind(); mBarrelDistortionBuffer->QueueResize(framebufferResolution); mBarrelDistortionBuffer->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/barrel_distortion.shader"); shader->Bind(); shader->SetUniform("u_Distortion", sceneEnv->BarrelDistortion); shader->SetUniform("u_DistortionEdge", sceneEnv->BarrelEdgeDistortion); shader->SetUniform("u_Scale", sceneEnv->BarrelScale); if (sceneEnv->DOFEnabled) { shader->SetUniform("u_Source", mDOFBuffer->GetTexture().get(), 0); } else { shader->SetUniform("u_Source", finalOutput.get(), 0); } Renderer::DrawQuad(); } mBarrelDistortionBuffer->Unbind(); framebuffer.Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/copy.shader"); shader->Bind(); shader->SetUniform("u_Source", mBarrelDistortionBuffer->GetTexture().get(), 0); Renderer::DrawQuad(); } framebuffer.Unbind(); if (renderUI) { mVignetteBuffer->QueueResize(framebufferResolution); } mVignetteBuffer->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/vignette.shader"); shader->Bind(); shader->SetUniform("u_Intensity", sceneEnv->VignetteIntensity); shader->SetUniform("u_Extend", sceneEnv->VignetteEnabled ? sceneEnv->VignetteExtend : 0.0f); shader->SetUniform("u_Source", framebuffer.GetTexture().get(), 0); Renderer::DrawQuad(); } mVignetteBuffer->Unbind(); framebuffer.Bind(); { RenderCommand::Clear(); if (sceneEnv->PosterizationEnabled) { Shader* shader = ShaderManager::GetShader("Resources/Shaders/posterization.shader"); shader->Bind(); shader->SetUniform("u_Source", mVignetteBuffer->GetTexture().get(), 0); shader->SetUniform("u_Levels", sceneEnv->PosterizationLevels); } else { Shader* shader = ShaderManager::GetShader("Resources/Shaders/copy.shader"); shader->Bind(); shader->SetUniform("u_Source", mVignetteBuffer->GetTexture().get(), 0); } Renderer::DrawQuad(); } framebuffer.Unbind(); mTempFrameBuffer->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/copy.shader"); shader->Bind(); shader->SetUniform("u_Source", framebuffer.GetTexture().get(), 0); Renderer::DrawQuad(); } mTempFrameBuffer->Unbind(); if (sceneEnv->PixelizationEnabled) { framebuffer.Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/pixelization.shader"); shader->Bind(); shader->SetUniform("u_Source", mTempFrameBuffer->GetTexture().get(), 0); shader->SetUniform("u_SourceSize", framebufferResolution); shader->SetUniform("u_PixelSize", sceneEnv->PixelSize); Renderer::DrawQuad(); } framebuffer.Unbind(); mTempFrameBuffer->Bind(); { RenderCommand::Clear(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/copy.shader"); shader->Bind(); shader->SetUniform("u_Source", framebuffer.GetTexture().get(), 0); Renderer::DrawQuad(); } mTempFrameBuffer->Unbind(); } //ImGui::Begin("SDF Params"); //{ // static auto renderer = NuakeUI::Renderer::Get(); // ImGui::DragFloat("Subpixel threshold", &renderer.subpixelThreshold, 0.01f, 0.0f); // ImGui::DragFloat("Subpixel curve tolerance", &renderer.curveTolerance, 0.01f, 0.0f); // ImGui::DragFloat("Subpixel amount", &renderer.subpixelAmount, 0.01f, 0.0f); //} //ImGui::End(); const auto uiView = scene.m_Registry.view(); for (auto ui : uiView) { const UIComponent& uiComponent = uiView.get(ui); if (!ResourceManager::IsResourceLoaded(uiComponent.UIResource)) { continue; } if (!uiComponent.IsWorldSpace) { // Fetch resource from resource manager using UUID Ref uiResource = ResourceManager::GetResource(uiComponent.UIResource); if (!uiResource) { continue; } uiResource->Resize(framebuffer.GetSize()); // Copy UI onto final viewport framebuffer.Bind(); { Shader* shader = ShaderManager::GetShader("Resources/Shaders/add.shader"); shader->Bind(); shader->SetUniform("u_Source", mTempFrameBuffer->GetTexture().get(), 0); shader->SetUniform("u_Source2", uiResource->GetOutputTexture().get(), 1); Renderer::DrawQuad(); } framebuffer.Unbind(); } } glDepthMask(true); RenderCommand::Enable(RendererEnum::DEPTH_TEST); Renderer::EndDraw(); } void SceneRenderer::DrawTemporaryModel(const std::string & name, Ref model, Matrix4 transform) { if (IsTempModelLoaded(name)) { mTempModels[name].Transform = transform; return; } mTempModels[name] = TemporaryModels{ model, transform }; } void SceneRenderer::DrawDebugLine(const Vector3& start, const Vector3& end, const Color& color, float life, float width) { DebugLine debugLine = { .Start = start, .End = end, .LineColor = color, .Life = life, .Width = width, .DepthTest = true }; mDebugLines.push_back(debugLine); } void SceneRenderer::DrawDebugShape(const Vector3& position, const Quat& rotation, Ref shape, const Color& color, float life, float width) { DebugShape debugShape = { .Position = position, .Rotation = rotation, .LineColor = color, .Life = life, .Width = width, .DepthTest = true, .Shape = shape, }; mDebugShapes.push_back(debugShape); } void SceneRenderer::ShadowPass(Scene& scene) { ZoneScoped; RenderCommand::Enable(RendererEnum::DEPTH_TEST); Shader* shader = ShaderManager::GetShader("Resources/Shaders/shadowMap.shader"); shader->Bind(); RenderCommand::Enable(RendererEnum::FACE_CULL); //glCullFace(GL_BACK); auto meshView = scene.m_Registry.view(); auto quakeView = scene.m_Registry.view(); auto view = scene.m_Registry.view(); LightComponent lightDebug; for (auto l : view) { auto [lightTransform, light, visibility] = view.get(l); if (!light.CastShadows || !visibility.Visible) { continue; } if (light.Type == LightType::Directional) { light.CalculateViewProjection(mView, mProjection); for (int i = 0; i < CSM_AMOUNT; i++) { light.m_Framebuffers[i]->Bind(); light.m_Framebuffers[i]->Clear(); { shader->SetUniform("u_LightTransform", light.mViewProjections[i]); for (auto e : meshView) { auto [transform, mesh, visibility] = meshView.get(e); if (mesh.ModelResource != nullptr && visibility.Visible) { for (auto& m : mesh.ModelResource->GetMeshes()) Renderer::SubmitMesh(m, transform.GetGlobalTransform()); } } for (auto e : quakeView) { auto [transform, model, visibility] = quakeView.get(e); if (model.IsTransparent || !visibility.Visible) continue; } Renderer::Flush(shader, true); auto spriteView = scene.m_Registry.view(); for (auto e : spriteView) { auto [transform, sprite, visibility] = spriteView.get(e); if (!visibility.Visible || !sprite.SpriteMesh) continue; auto finalQuadTransform = transform.GetGlobalTransform(); if (sprite.Billboard) { if (sprite.PositionFacing) { const Matrix4& invView = glm::inverse(mView); const Vector3& cameraPosition = Vector3(invView[3][0], invView[3][1], invView[3][2]); const Vector3& spritePosition = Vector3(finalQuadTransform[3][0], finalQuadTransform[3][1], finalQuadTransform[3][2]); const Vector3& direction = cameraPosition - spritePosition; finalQuadTransform = glm::inverse(glm::lookAt(Vector3(), direction, Vector3(0, 1, 0))); } else { finalQuadTransform = glm::inverse(mView); } if (sprite.LockYRotation) { // This locks the pitch rotation on the billboard, useful for trees, lamps, etc. finalQuadTransform[1] = Vector4(0, 1, 0, 0); finalQuadTransform[2] = Vector4(finalQuadTransform[2][0], 0, finalQuadTransform[2][2], 0); finalQuadTransform = finalQuadTransform; } finalQuadTransform[3] = Vector4(Vector3(transform.GetGlobalTransform()[3]), 1.0f); // Scale finalQuadTransform = glm::scale(finalQuadTransform, transform.GetGlobalScale()); } Renderer::SubmitMesh(sprite.SpriteMesh, finalQuadTransform, (uint32_t)e); } Renderer::Flush(shader, true); } } } else if (light.Type == LightType::Spot) { RenderCommand::Disable(RendererEnum::FACE_CULL); light.m_Framebuffers[0]->Bind(); light.m_Framebuffers[0]->Clear(); { Matrix4 spotLightTransform = Matrix4(1.0f); Vector3 pos = lightTransform.GetGlobalPosition(); pos.y *= -1.0f; pos.x *= -1.0f; pos.z *= -1.0f; spotLightTransform = glm::translate(spotLightTransform, pos); Vector3 direction = lightTransform.GetGlobalRotation() * Vector3(0, 0, 1); auto lookatAt = lookAt(Vector3(), direction, Vector3(0, 1, 0)); Quat offset = QuatFromEuler(0, -90.0f, 0); Quat offset2 = QuatFromEuler(180.0f, 0.0f, 0); const Quat& globalRotation = glm::normalize(lightTransform.GetGlobalRotation()); const Matrix4& rotationMatrix = glm::mat4_cast(globalRotation); shader->SetUniform("u_LightTransform", light.GetProjection() * glm::inverse(lightTransform.GetGlobalTransform())); for (auto e : meshView) { auto [transform, mesh, visibility] = meshView.get(e); if (mesh.ModelResource != nullptr && visibility.Visible) { for (auto& m : mesh.ModelResource->GetMeshes()) Renderer::SubmitMesh(m, transform.GetGlobalTransform()); } } for (auto e : quakeView) { auto [transform, model, visibility] = quakeView.get(e); if (model.IsTransparent || !visibility.Visible) continue; } Renderer::Flush(shader, true); auto spriteView = scene.m_Registry.view(); for (auto e : spriteView) { auto [transform, sprite, visibility] = spriteView.get(e); if (!visibility.Visible || !sprite.SpriteMesh) continue; auto finalQuadTransform = transform.GetGlobalTransform(); if (sprite.Billboard) { if (sprite.PositionFacing) { const Matrix4& invView = glm::inverse(mView); const Vector3& cameraPosition = Vector3(invView[3][0], invView[3][1], invView[3][2]); const Vector3& spritePosition = Vector3(finalQuadTransform[3][0], finalQuadTransform[3][1], finalQuadTransform[3][2]); const Vector3& direction = cameraPosition - spritePosition; finalQuadTransform = glm::inverse(glm::lookAt(Vector3(), direction, Vector3(0, 1, 0))); } else { finalQuadTransform = glm::inverse(mView); } if (sprite.LockYRotation) { // This locks the pitch rotation on the billboard, useful for trees, lamps, etc. finalQuadTransform[1] = Vector4(0, 1, 0, 0); finalQuadTransform[2] = Vector4(finalQuadTransform[2][0], 0, finalQuadTransform[2][2], 0); finalQuadTransform = finalQuadTransform; } finalQuadTransform[3] = Vector4(Vector3(transform.GetGlobalTransform()[3]), 1.0f); // Scale finalQuadTransform = glm::scale(finalQuadTransform, transform.GetGlobalScale()); } Renderer::SubmitMesh(sprite.SpriteMesh, finalQuadTransform, (uint32_t)e); } Renderer::Flush(shader, true); } lightDebug = light; } } Shader* gBufferSkinnedMeshShader = ShaderManager::GetShader("Resources/Shaders/shadowMap_skinned.shader"); gBufferSkinnedMeshShader->Bind(); const uint32_t modelMatrixUniformLocation = gBufferSkinnedMeshShader->FindUniformLocation("u_Model"); gBufferSkinnedMeshShader->SetUniform(modelMatrixUniformLocation, Matrix4(1.0f)); auto skinnedView = scene.m_Registry.view(); for (auto l : view) { auto [lightTransform, light, visibility] = view.get(l); if (light.Type != LightType::Directional || !light.CastShadows || !visibility.Visible) { continue; } for (int i = 0; i < CSM_AMOUNT; i++) { light.m_Framebuffers[i]->Bind(); { gBufferSkinnedMeshShader->SetUniform("u_LightTransform", light.mViewProjections[i]); for (auto e : skinnedView) { auto [transform, mesh, visibility] = skinnedView.get(e); if (mesh.ModelResource != nullptr && visibility.Visible) { auto& rootBoneNode = mesh.ModelResource->GetSkeletonRootNode(); SetSkeletonBoneTransformRecursive(scene, rootBoneNode, gBufferSkinnedMeshShader); for (auto& m : mesh.ModelResource->GetMeshes()) { m->Draw(gBufferSkinnedMeshShader, false); } } } } } } mDisplayDepthBuffer->QueueResize(GetGBuffer().GetTexture(GL_DEPTH_ATTACHMENT)->GetSize()); mDisplayDepthBuffer->Bind(); mDisplayDepthBuffer->Clear(); Shader* displayDepthShader = ShaderManager::GetShader("Resources/Shaders/display_depth.shader"); displayDepthShader->Bind(); GetGBuffer().GetTexture(GL_DEPTH_ATTACHMENT)->Bind(5); displayDepthShader->SetUniform("u_Source", 5); RenderCommand::Disable(RendererEnum::DEPTH_TEST); Renderer::DrawQuad(Matrix4(1.0f)); RenderCommand::Enable(RendererEnum::DEPTH_TEST); mDisplayDepthBuffer->Unbind(); } void SceneRenderer::GBufferPass(Scene& scene) { ZoneScoped; mGBuffer->Bind(); mGBuffer->Clear(); { RenderCommand::Disable(RendererEnum::BLENDING); // Init RenderCommand::Disable(RendererEnum::FACE_CULL); Shader* gBufferShader = ShaderManager::GetShader("Resources/Shaders/gbuffer.shader"); Shader* gBufferSkinnedMeshShader = ShaderManager::GetShader("Resources/Shaders/gbuffer_skinned.shader"); gBufferShader->Bind(); gBufferShader->SetUniform("u_Projection", mProjection); gBufferShader->SetUniform("u_View", mView); gBufferShader->SetUniform("u_Jitter", mScaledJitterOffets[mJitterIndex]); mJitterIndex++; mJitterIndex = mJitterIndex % mScaledJitterOffets.size(); // Models { ZoneScopedN("Render Models"); auto view = scene.m_Registry.view(); for (auto e : view) { auto [transform, mesh, visibility] = view.get(e); if (mesh.ModelResource && visibility.Visible) { for (auto& m : mesh.ModelResource->GetMeshes()) { Renderer::SubmitMesh(m, transform.GetGlobalTransform(), (uint32_t)e, transform.PreviousTransform); } } } Renderer::Flush(gBufferShader, false); } { ZoneScopedN("Render Temp Models"); for (auto& mesh : mTempModels) { if (mesh.second.ModelResource) { for (auto& m : mesh.second.ModelResource->GetMeshes()) { Renderer::SubmitMesh(m, mesh.second.Transform, (uint32_t)-1); } } } } glEnable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); Renderer::Flush(gBufferShader, true); glDepthMask(GL_TRUE); // Quake BSPs { ZoneScopedN("Render Trenchbroom Brushes"); auto quakeView = scene.m_Registry.view(); for (auto e : quakeView) { auto [transform, model, visibility] = quakeView.get(e); if (model.IsTransparent || !visibility.Visible) continue; } glEnable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glCullFace(GL_FRONT); Renderer::Flush(gBufferShader, true); } glDisable(GL_CULL_FACE); // Sprites { ZoneScopedN("Render Sprites"); auto spriteView = scene.m_Registry.view(); for (auto& e : spriteView) { auto [transform, sprite, visibility] = spriteView.get(e); if (!visibility.Visible || !sprite.SpriteMesh) continue; auto finalQuadTransform = transform.GetGlobalTransform(); if (sprite.Billboard) { if (sprite.PositionFacing) { const Matrix4& invView = glm::inverse(mView); const Vector3& cameraPosition = Vector3(invView[3][0], invView[3][1], invView[3][2]); const Vector3& spritePosition = Vector3(finalQuadTransform[3][0], finalQuadTransform[3][1], finalQuadTransform[3][2]); const Vector3& direction = cameraPosition - spritePosition; finalQuadTransform = glm::inverse(glm::lookAt(Vector3(), direction, Vector3(0, 1, 0))); } else { finalQuadTransform = glm::inverse(mView); } if (sprite.LockYRotation) { // This locks the pitch rotation on the billboard, useful for trees, lamps, etc. finalQuadTransform[1] = Vector4(0, 1, 0, 0); finalQuadTransform[2] = Vector4(finalQuadTransform[2][0], 0, finalQuadTransform[2][2], 0); finalQuadTransform = finalQuadTransform; } finalQuadTransform[3] = Vector4(Vector3(transform.GetGlobalTransform()[3]), 1.0f); // Scale finalQuadTransform = glm::scale(finalQuadTransform, transform.GetGlobalScale()); } Renderer::SubmitMesh(sprite.SpriteMesh, finalQuadTransform, (uint32_t)e); } Renderer::Flush(gBufferShader, false); } // Particles { ZoneScopedN("Render Particles"); Ref previousMaterial = Renderer::QuadMesh->GetMaterial(); auto particleEmitterView = scene.m_Registry.view(); for (auto& e : particleEmitterView) { auto [transform, emitterComponent, visibility] = particleEmitterView.get(e); if (!visibility.Visible) continue; if (emitterComponent.resFile.dirty) { emitterComponent.resFile.dirty = false; if (emitterComponent.resFile.Exist()) { Ref material = Nuake::ResourceLoader::LoadMaterial(emitterComponent.resFile.GetRelativePath()); emitterComponent.ParticleMaterial = material; } } if (emitterComponent.ParticleMaterial == nullptr) { continue; } Renderer::QuadMesh->SetMaterial(emitterComponent.ParticleMaterial); Vector3 oldColor = Renderer::QuadMesh->GetMaterial()->data.m_AlbedoColor; auto initialTransform = transform.GetGlobalTransform(); for (auto& p : emitterComponent.Emitter.Particles) { Matrix4 particleTransform = initialTransform; particleTransform = glm::inverse(mView); // Translation Vector3 particleGlobalPosition; if (emitterComponent.GlobalSpace) { particleGlobalPosition = p.Position; } else { particleGlobalPosition = Vector3(initialTransform[3]) + p.Position; } particleTransform[3] = Vector4(particleGlobalPosition, 1.0f); // Scale Vector3 finalScale = emitterComponent.ParticleScale; if (p.Scale != 1.0f) { finalScale += emitterComponent.ParticleScale * p.Scale; } particleTransform = glm::scale(particleTransform, finalScale); Renderer::SubmitMesh(Renderer::QuadMesh, particleTransform, (uint32_t)e); } Renderer::QuadMesh->SetMaterial(emitterComponent.ParticleMaterial); Renderer::Flush(gBufferShader, false); Renderer::QuadMesh->SetMaterial(previousMaterial); } } { ZoneScopedN("Render UI Worldspace"); Ref previousMaterial = Renderer::QuadMesh->GetMaterial(); auto uiView = scene.m_Registry.view(); for (auto e : uiView) { auto [transform, uiComponent] = uiView.get(e); if (!uiComponent.IsWorldSpace) { continue; } Ref uiResource = ResourceManager::GetResource(uiComponent.UIResource); if (!uiResource) { continue; } Matrix4 finalTransform = transform.GetGlobalTransform(); Renderer::QuadMesh->GetMaterial()->SetAlbedo(uiResource->GetOutputTexture()); Renderer::SubmitMesh(Renderer::QuadMesh, finalTransform, (uint32_t)e); Renderer::Flush(gBufferShader, false); NuakeUI::DrawInspector(uiResource->GetCanvas()); //Renderer::DrawQuad(finalTransform); } // Reset material on quadmesh Renderer::QuadMesh->SetMaterial(previousMaterial); } { ZoneScopedN("Render Skinned mesh"); // Skinned mesh at the end because we switch shader gBufferSkinnedMeshShader->Bind(); gBufferSkinnedMeshShader->SetUniform("u_Projection", mProjection); gBufferSkinnedMeshShader->SetUniform("u_View", mView); RenderCommand::Disable(RendererEnum::FACE_CULL); // Skinned Models const uint32_t entityIdUniformLocation = gBufferSkinnedMeshShader->FindUniformLocation("u_EntityID"); const uint32_t modelMatrixUniformLocation = gBufferSkinnedMeshShader->FindUniformLocation("u_Model"); gBufferSkinnedMeshShader->SetUniform(modelMatrixUniformLocation, Matrix4(1.0f)); auto skinnedModelView = scene.m_Registry.view(); for (auto e : skinnedModelView) { auto [transform, mesh, visibility] = skinnedModelView.get(e); auto& meshResource = mesh.ModelResource; if (meshResource && visibility.Visible) { auto& rootBoneNode = meshResource->GetSkeletonRootNode(); SetSkeletonBoneTransformRecursive(scene, rootBoneNode, gBufferSkinnedMeshShader); for (auto& m : mesh.ModelResource->GetMeshes()) { m->GetMaterial()->Bind(gBufferSkinnedMeshShader); gBufferSkinnedMeshShader->SetUniform(entityIdUniformLocation, (uint32_t)e + 1); m->Draw(gBufferSkinnedMeshShader, true); } } } } } RenderCommand::Enable(RendererEnum::BLENDING); } void SceneRenderer::ShadingPass(Scene& scene, Ref previousFrame) { ZoneScoped; mShadingBuffer->Bind(); mShadingBuffer->Clear(); { RenderCommand::Disable(RendererEnum::DEPTH_TEST); RenderCommand::Disable(RendererEnum::FACE_CULL); Ref environment = scene.GetEnvironment(); bool hasSky = false; auto skyView = scene.m_Registry.view(); for (auto l : skyView) { SkyComponent& sky = skyView.get(l); Shader* cubemapShader = ShaderManager::GetShader("Resources/Shaders/skybox.shader"); cubemapShader->Bind(); if (sky.SkyResourceFilePath.Exist() && sky.SkyResource == UUID(0)) { sky.SkyResource = ResourceLoader::LoadSky(sky.SkyResourceFilePath.GetRelativePath())->ID; } if (ResourceManager::IsResourceLoaded(sky.SkyResource)) { auto skyResource = ResourceManager::GetResource(sky.SkyResource); skyResource->GetCubemap()->Bind(1); cubemapShader->SetUniform("skybox", 1); } hasSky = true; cubemapShader->SetUniform("skybox", 1); cubemapShader->SetUniforms({ { "projection", mProjection}, { "view", mView} }); Renderer::DrawCube(Matrix4()); } if (!hasSky && environment->CurrentSkyType == SkyType::ProceduralSky) { RenderCommand::Clear(); RenderCommand::SetClearColor(Color(0, 0, 0, 1)); environment->ProceduralSkybox->Draw(mProjection, mView); } else if (!hasSky && environment->CurrentSkyType == SkyType::ClearColor) { RenderCommand::SetClearColor(environment->AmbientColor); RenderCommand::Clear(); RenderCommand::SetClearColor(Color(0, 0, 0, 1)); } Shader* shadingShader = ShaderManager::GetShader("Resources/Shaders/deferred.shader"); shadingShader->Bind(); shadingShader->SetUniform("u_Projection", mProjection); shadingShader->SetUniform("u_View", mView); shadingShader->SetUniform("u_EyePosition", scene.GetCurrentCamera()->Translation); shadingShader->SetUniform("u_AmbientTerm", environment->AmbientTerm); shadingShader->SetUniform("m_SSAO", scene.GetEnvironment()->mSSAO->GetOuput()->GetTexture().get(), 9); Ref env = scene.GetEnvironment(); struct LightDistance { TransformComponent transform; LightComponent light; float distance; }; std::vector lightDistances; auto view = scene.m_Registry.view(); lightDistances.reserve(view.size_hint()); const Vector3 camPosition = scene.GetCurrentCamera()->Translation; for (auto l : view) { auto [transform, light, parent] = view.get(l); if (light.Type == Directional) { if (light.SyncDirectionWithSky) { light.Direction = env->ProceduralSkybox->GetSunDirection(); } else { light.Direction = transform.GetGlobalRotation() * Vector3(0, 0, 1); } } Vector3 lightPosition = transform.GetGlobalPosition(); float distanceFromCam = glm::length(camPosition - lightPosition); lightDistances.push_back({transform, light, distanceFromCam}); } std::sort(lightDistances.begin(), lightDistances.end(), [](const LightDistance& a, const LightDistance& b) { return a.distance < b.distance; } ); for (const auto& l : lightDistances) { Renderer::RegisterDeferredLight(l.transform, l.light); } mGBuffer->GetTexture(GL_DEPTH_ATTACHMENT)->Bind(5); mGBuffer->GetTexture(GL_COLOR_ATTACHMENT0)->Bind(6); mGBuffer->GetTexture(GL_COLOR_ATTACHMENT1)->Bind(7); mGBuffer->GetTexture(GL_COLOR_ATTACHMENT2)->Bind(8); mGBuffer->GetTexture(GL_COLOR_ATTACHMENT4)->Bind(10); shadingShader->SetUniform("m_Depth", 5); shadingShader->SetUniform("m_Albedo", 6); shadingShader->SetUniform("m_Normal", 7); shadingShader->SetUniform("m_Material", 8); shadingShader->SetUniform("m_Emissive", 10); RenderCommand::Disable(RendererEnum::FACE_CULL); Renderer::DrawQuad(Matrix4()); } } void SceneRenderer::PostProcessPass(const Scene& scene) { } void SceneRenderer::DebugRendererPass(Scene& scene) { ZoneScoped; mShadingBuffer->Bind(); { // Lines mLineMesh->Bind(); Shader* shader = ShaderManager::GetShader("Resources/Shaders/debugLine.shader"); shader->Bind(); shader->SetUniform("u_Projection", mProjection); shader->SetUniform("u_View", mView); bool depthTestState = true; for (auto& l : mDebugLines) { shader->SetUniform("u_Color", l.LineColor); shader->SetUniform("u_StartPos", l.Start); shader->SetUniform("u_EndPos", l.End); if (l.DepthTest) { } glLineWidth(l.Width); RenderCommand::DrawLines(0, 2); } shader->Unbind(); shader = Nuake::ShaderManager::GetShader("Resources/Shaders/line.shader"); shader->Bind(); shader->SetUniform("u_Opacity", 0.5f); shader->SetUniform("u_Projection", mProjection); for (auto& shape : mDebugShapes) { if(shape.DepthTest) { glEnable(GL_DEPTH_TEST); } else { RenderCommand::Disable(RendererEnum::DEPTH_TEST); } shader->SetUniform("u_Color", shape.LineColor); glLineWidth(shape.Width); Matrix4 view = mView; Physics::RigidbodyShapes shapeType = shape.Shape->GetType(); switch (shapeType) { case Physics::RigidbodyShapes::BOX: { const Quat& globalRotation = glm::normalize(shape.Rotation); const Matrix4& rotationMatrix = glm::mat4_cast(globalRotation); view = glm::translate(view, shape.Position) * rotationMatrix; view = glm::scale(view, reinterpret_cast(shape.Shape.get())->GetSize()); shader->SetUniform("u_View", view); mBoxGizmo->Bind(); RenderCommand::DrawLines(0, 26); break; } case Physics::RigidbodyShapes::SPHERE: { const Quat& globalRotation = glm::normalize(shape.Rotation); const Matrix4& rotationMatrix = glm::mat4_cast(globalRotation); view = glm::translate(view, shape.Position) * rotationMatrix; view = glm::scale(view, Vector3(reinterpret_cast(shape.Shape.get())->GetRadius())); shader->SetUniform("u_View", view); mSphereGizmo->Bind(); RenderCommand::DrawLines(0, 128); break; } case Physics::RigidbodyShapes::CAPSULE: { const Quat& globalRotation = glm::normalize(shape.Rotation); const Matrix4& rotationMatrix = glm::mat4_cast(globalRotation); view = glm::translate(view, shape.Position) * rotationMatrix; shader->SetUniform("u_View", view); const Physics::Capsule* capsule = reinterpret_cast(shape.Shape.get()); mCapsuleGizmo->UpdateShape(capsule->GetRadius(), capsule->GetHeight()); mCapsuleGizmo->Bind(); Nuake::RenderCommand::DrawLines(0, 264); break; } case Physics::RigidbodyShapes::CYLINDER: { const Quat& globalRotation = glm::normalize(shape.Rotation); const Matrix4& rotationMatrix = glm::mat4_cast(globalRotation); view = glm::translate(view, shape.Position) * rotationMatrix; const Physics::Cylinder* cylinder = reinterpret_cast(shape.Shape.get()); shader->SetUniform("u_View", view); mCylinderGizmo->Bind(); mCylinderGizmo->UpdateShape(cylinder->GetRadius(), cylinder->GetHeight()); Nuake::RenderCommand::DrawLines(0, 264); break; } } } } mShadingBuffer->Unbind(); } void SceneRenderer::SetSkeletonBoneTransformRecursive(Scene& scene, SkeletonNode& skeletonNode, Shader* shader) { for (auto& child : skeletonNode.Children) { if (auto entity = scene.GetEntity(child.Name); entity.GetHandle() != -1) { const std::string boneMatrixUniformName = "u_FinalBonesMatrice[" + std::to_string(child.Id) + "]"; shader->SetUniform(boneMatrixUniformName, child.FinalTransform); } SetSkeletonBoneTransformRecursive(scene, child, shader); } } void SceneRenderer::UpdateJitterOffsets(const Vector2& viewportSize) { mScaledJitterOffets.clear(); uint32_t index = 0; for (auto& jitterPoint : mJitterOffsets) { mScaledJitterOffets.push_back({ ((jitterPoint.x - 0.5f) / viewportSize.x) * 2.0f, ((jitterPoint.y - 0.5f) / viewportSize.y) * 2.0f }); index++; } } }