45 if (tex->LoadFromMemory(data, w, h, fmt))
return tex;
60 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
61 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
62 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
63 SDL_GL_CONTEXT_PROFILE_CORE);
64 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
65 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
67 m_GLContext = SDL_GL_CreateContext(m_Window->GetSDLWindow());
69 SDL_Log(
"Failed to create OpenGL context: %s", SDL_GetError());
73 if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
74 SDL_Log(
"Failed to initialize Glad (OpenGL loader)");
79 SDL_GetWindowSize(m_Window->GetSDLWindow(), &
width, &
height);
82 glEnable(GL_DEPTH_TEST);
84 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
87 SDL_GL_SetSwapInterval(
m_vsync ? 1 : 0);
91 glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
100 m_Initialized =
true;
105 CreateGBufferResources();
108 SLEAK_INFO(
"OpenGL renderer has been initialized successfully!");
110 (
const char*)glGetString(GL_VERSION));
115void OpenGLRenderer::SetupVertexLayout() {
116 glGenVertexArrays(1, &m_VAO);
126 if (m_hasPendingLightVP) {
127 memcpy(m_lightVP, m_pendingLightVP,
sizeof(m_lightVP));
137 glBindFramebuffer(GL_FRAMEBUFFER, m_gbufferFBO);
138 GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
139 glDrawBuffers(3, drawBuffers);
140 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
141 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
144 m_inGeometryPass =
true;
148 glBindFramebuffer(GL_FRAMEBUFFER, m_msaaFBO);
150 glBindFramebuffer(GL_FRAMEBUFFER, 0);
152 glClearColor(0.39f, 0.58f, 0.93f, 1.0f);
153 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
156 glBindVertexArray(m_VAO);
159 ImGui_ImplOpenGL3_NewFrame();
160 ImGui_ImplSDL3_NewFrame();
170 SDL_GetWindowSize(m_Window->GetSDLWindow(), &
width, &
height);
171 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_msaaFBO);
172 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
175 GL_COLOR_BUFFER_BIT, GL_NEAREST);
176 glBindFramebuffer(GL_FRAMEBUFFER, 0);
181 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
184 glBindVertexArray(0);
185 SDL_GL_SwapWindow(m_Window->GetSDLWindow());
192 CleanupGBufferResources();
193 if (m_shadowTransformUBO) { glDeleteBuffers(1, &m_shadowTransformUBO); m_shadowTransformUBO = 0; }
194 if (m_shadowUBO) { glDeleteBuffers(1, &m_shadowUBO); m_shadowUBO = 0; }
195 if (m_shadowDepthTex) { glDeleteTextures(1, &m_shadowDepthTex); m_shadowDepthTex = 0; }
196 if (m_shadowFBO) { glDeleteFramebuffers(1, &m_shadowFBO); m_shadowFBO = 0; }
197 m_shadowMapCreated =
false;
198 m_shadowUBOCreated =
false;
199 CleanupMSAAFramebuffer();
201 ImGui_ImplOpenGL3_Shutdown();
202 ImGui_ImplSDL3_Shutdown();
203 ImGui::DestroyContext();
207 glDeleteVertexArrays(1, &m_VAO);
211 SDL_GL_DestroyContext(m_GLContext);
212 m_GLContext =
nullptr;
214 m_Initialized =
false;
221 if (m_msaaFBO != 0) {
222 CleanupMSAAFramebuffer();
223 CreateMSAAFramebuffer();
226 if (m_gbufferCreated) {
227 RecreateGBufferOnResize(
static_cast<int>(
width),
static_cast<int>(
height));
232 GLenum mode = m_debugLineMode ? GL_LINES : GL_TRIANGLES;
233 glDrawArrays(mode, 0, vertexCount);
239 GLenum mode = m_debugLineMode ? GL_LINES : GL_TRIANGLES;
240 glDrawElements(mode, indexCount, GL_UNSIGNED_INT, 0);
246 uint32_t vertexPerInstance) {
247 GLenum mode = m_debugLineMode ? GL_LINES : GL_TRIANGLES;
248 glDrawArraysInstanced(mode, 0, vertexPerInstance,
255 uint32_t indexPerInstance) {
256 GLenum mode = m_debugLineMode ? GL_LINES : GL_TRIANGLES;
257 glDrawElementsInstanced(mode, indexPerInstance,
258 GL_UNSIGNED_INT, 0, instanceCount);
264 m_debugLineMode =
true;
268 m_debugLineMode =
false;
282 float height,
float minDepth,
284 glViewport(
static_cast<GLint
>(x),
static_cast<GLint
>(y),
285 static_cast<GLsizei
>(
width),
286 static_cast<GLsizei
>(
height));
287 glDepthRangef(minDepth, maxDepth);
292 glClearColor(r, g, b, a);
293 glClear(GL_COLOR_BUFFER_BIT);
297 float depth, uint8_t stencil) {
300 glClearDepthf(depth);
301 mask |= GL_DEPTH_BUFFER_BIT;
304 glClearStencil(stencil);
305 mask |= GL_STENCIL_BUFFER_BIT;
307 if (mask) glClear(mask);
311 glDepthMask(enabled ? GL_TRUE : GL_FALSE);
315 GLenum glFunc = GL_LESS;
331 glEnable(GL_CULL_FACE);
333 glDisable(GL_CULL_FACE);
346 glBindBuffer(GL_ARRAY_BUFFER, glBuf->GetGLBuffer());
349 const uint32_t customFormat = buffer->GetVertexFormat();
355 const GLsizei stride =
static_cast<GLsizei
>(customDesc->
stride);
356 uint32_t enabledMask = 0;
357 for (
const auto& attr : customDesc->
attributes) {
358 const void* off =
reinterpret_cast<const void*
>(
359 static_cast<uintptr_t
>(attr.offset));
360 glEnableVertexAttribArray(attr.location);
361 switch (attr.format) {
363 glVertexAttribPointer(attr.location, 1, GL_FLOAT, GL_FALSE, stride, off);
366 glVertexAttribPointer(attr.location, 2, GL_FLOAT, GL_FALSE, stride, off);
369 glVertexAttribPointer(attr.location, 3, GL_FLOAT, GL_FALSE, stride, off);
372 glVertexAttribPointer(attr.location, 4, GL_FLOAT, GL_FALSE, stride, off);
375 glVertexAttribIPointer(attr.location, 1, GL_UNSIGNED_INT, stride, off);
378 glVertexAttribIPointer(attr.location, 1, GL_INT, stride, off);
381 if (attr.location < 32) enabledMask |= (1u << attr.location);
384 constexpr uint32_t TRACKED_ATTRIB_SLOTS = 8;
385 for (uint32_t loc = 0; loc < TRACKED_ATTRIB_SLOTS; ++loc) {
386 if (!(enabledMask & (1u << loc))) glDisableVertexAttribArray(loc);
391 glEnableVertexAttribArray(0);
392 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
sizeof(
Vertex),
393 (
void*)offsetof(
Vertex, px));
396 glEnableVertexAttribArray(1);
397 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
sizeof(
Vertex),
398 (
void*)offsetof(
Vertex, nx));
401 glEnableVertexAttribArray(2);
402 glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE,
sizeof(
Vertex),
403 (
void*)offsetof(
Vertex, tx));
406 glEnableVertexAttribArray(3);
407 glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE,
sizeof(
Vertex),
408 (
void*)offsetof(
Vertex, r));
411 glEnableVertexAttribArray(4);
412 glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE,
sizeof(
Vertex),
413 (
void*)offsetof(
Vertex, u));
416 glEnableVertexAttribArray(5);
417 glVertexAttribIPointer(5, 4, GL_INT,
sizeof(
Vertex),
418 (
void*)offsetof(
Vertex, boneIDs));
421 glEnableVertexAttribArray(6);
422 glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE,
sizeof(
Vertex),
423 (
void*)offsetof(
Vertex, boneWeights));
432 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glBuf->GetGLBuffer());
441 if (m_inShadowPass && slot == 0 && glBuf->GetCPUShadowCopySize() >= 128) {
442 const float* srcWorld =
reinterpret_cast<const float*
>(
443 static_cast<const char*
>(glBuf->GetCPUShadowCopy()) + 64);
446 for (
int r = 0; r < 4; ++r) {
447 for (
int c = 0; c < 4; ++c) {
449 for (
int k = 0; k < 4; ++k) {
450 sum += srcWorld[r * 4 + k] * m_lightVP[k * 4 + c];
452 shadowPC[r * 4 + c] = sum;
455 memcpy(&shadowPC[16], srcWorld,
sizeof(
float) * 16);
458 if (m_shadowTransformUBO == 0) {
459 glGenBuffers(1, &m_shadowTransformUBO);
460 glBindBuffer(GL_UNIFORM_BUFFER, m_shadowTransformUBO);
461 glBufferData(GL_UNIFORM_BUFFER, 128,
nullptr, GL_DYNAMIC_DRAW);
462 glBindBuffer(GL_UNIFORM_BUFFER, 0);
465 glBindBuffer(GL_UNIFORM_BUFFER, m_shadowTransformUBO);
466 glBufferSubData(GL_UNIFORM_BUFFER, 0,
sizeof(shadowPC), shadowPC);
467 glBindBuffer(GL_UNIFORM_BUFFER, 0);
468 glBindBufferBase(GL_UNIFORM_BUFFER, slot, m_shadowTransformUBO);
472 glBindBufferBase(GL_UNIFORM_BUFFER, slot, glBuf->GetGLBuffer());
482 if (!buffer->Initialize(data)) {
491 if (shader->compile(shaderSource)) {
500 if (texture->LoadFromFile(TexturePath)) {
519void OpenGLRenderer::CreateMSAAFramebuffer() {
526 glGenFramebuffers(1, &m_msaaFBO);
527 glBindFramebuffer(GL_FRAMEBUFFER, m_msaaFBO);
530 glGenRenderbuffers(1, &m_msaaColorRBO);
531 glBindRenderbuffer(GL_RENDERBUFFER, m_msaaColorRBO);
534 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
535 GL_RENDERBUFFER, m_msaaColorRBO);
538 glGenRenderbuffers(1, &m_msaaDepthRBO);
539 glBindRenderbuffer(GL_RENDERBUFFER, m_msaaDepthRBO);
542 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
543 GL_RENDERBUFFER, m_msaaDepthRBO);
545 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
546 SLEAK_ERROR(
"OpenGL MSAA framebuffer is not complete!");
548 glBindFramebuffer(GL_FRAMEBUFFER, 0);
551void OpenGLRenderer::CleanupMSAAFramebuffer() {
552 if (m_msaaDepthRBO) { glDeleteRenderbuffers(1, &m_msaaDepthRBO); m_msaaDepthRBO = 0; }
553 if (m_msaaColorRBO) { glDeleteRenderbuffers(1, &m_msaaColorRBO); m_msaaColorRBO = 0; }
554 if (m_msaaFBO) { glDeleteFramebuffers(1, &m_msaaFBO); m_msaaFBO = 0; }
563 CleanupMSAAFramebuffer();
564 CreateMSAAFramebuffer();
573 SDL_GL_SetSwapInterval(
m_vsync ? 1 : 0);
580 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
583 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
586 glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
589 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
597 glDisable(GL_CULL_FACE);
600 glEnable(GL_CULL_FACE);
604 glEnable(GL_CULL_FACE);
605 glCullFace(GL_FRONT);
608 glDisable(GL_CULL_FACE);
615 if (texture->LoadCubemap(facePaths)) {
624 if (texture->LoadEquirectangular(panoramaPath)) {
632 IMGUI_CHECKVERSION();
633 ImGui::CreateContext();
634 ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
635 ImGui::StyleColorsDark();
637 if (!ImGui_ImplSDL3_InitForOpenGL(m_Window->GetSDLWindow(),
641 if (!ImGui_ImplOpenGL3_Init(
"#version 450"))
648void OpenGLRenderer::SetLightVP(
const float* mat) {
650 memcpy(m_pendingLightVP, mat,
sizeof(m_pendingLightVP));
651 m_hasPendingLightVP =
true;
655bool OpenGLRenderer::CreateShadowUBO() {
656 if (m_shadowUBOCreated)
return true;
657 glGenBuffers(1, &m_shadowUBO);
658 glBindBuffer(GL_UNIFORM_BUFFER, m_shadowUBO);
659 glBufferData(GL_UNIFORM_BUFFER,
sizeof(RenderEngine::PCSSShadowGPUData),
nullptr, GL_DYNAMIC_DRAW);
660 glBindBuffer(GL_UNIFORM_BUFFER, 0);
661 m_shadowUBOCreated =
true;
665void OpenGLRenderer::UpdateShadowLightUBO(
const void* data, uint32_t size) {
666 if (!m_shadowUBOCreated && !CreateShadowUBO())
return;
669 const auto* ubo =
static_cast<const RenderEngine::ShadowLightUBO*
>(data);
671 RenderEngine::PCSSShadowGPUData shadowData{};
672 memcpy(shadowData.LightVP, ubo->LightVP,
sizeof(
float) * 16);
673 memcpy(shadowData.NdcToShadow, ubo->NdcToShadow,
sizeof(
float) * 16);
674 shadowData.ShadowBias = ubo->ShadowBias;
675 shadowData.ShadowStrength = ubo->ShadowStrength;
676 shadowData.ShadowTexelSize = ubo->ShadowTexelSize;
677 shadowData.ShadowLightSize = ubo->LightSize;
681 glBindBuffer(GL_UNIFORM_BUFFER, m_shadowUBO);
682 glBufferSubData(GL_UNIFORM_BUFFER, 0,
sizeof(shadowData), &shadowData);
683 glBindBuffer(GL_UNIFORM_BUFFER, 0);
686 glBindBufferBase(GL_UNIFORM_BUFFER, 5, m_shadowUBO);
689bool OpenGLRenderer::CreateShadowMapResources() {
690 if (m_shadowMapCreated)
return true;
693 glGenTextures(1, &m_shadowDepthTex);
694 glBindTexture(GL_TEXTURE_2D, m_shadowDepthTex);
695 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F,
697 GL_DEPTH_COMPONENT, GL_FLOAT,
nullptr);
698 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
699 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
700 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
701 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
702 float borderColor[] = {1.0f, 1.0f, 1.0f, 1.0f};
703 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
704 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
705 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
706 glBindTexture(GL_TEXTURE_2D, 0);
709 glGenFramebuffers(1, &m_shadowFBO);
710 glBindFramebuffer(GL_FRAMEBUFFER, m_shadowFBO);
711 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadowDepthTex, 0);
712 glDrawBuffer(GL_NONE);
713 glReadBuffer(GL_NONE);
714 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
716 glBindFramebuffer(GL_FRAMEBUFFER, 0);
719 glBindFramebuffer(GL_FRAMEBUFFER, 0);
721 m_shadowMapCreated =
true;
727void OpenGLRenderer::RenderShadowPass() {
730 if (!queue || !queue->HasCachedShadowDraws())
return;
732 if (!m_shadowMapCreated) {
733 if (!CreateShadowMapResources())
return;
737 GLint savedViewport[4];
738 glGetIntegerv(GL_VIEWPORT, savedViewport);
741 glBindFramebuffer(GL_FRAMEBUFFER, m_shadowFBO);
743 glClear(GL_DEPTH_BUFFER_BIT);
746 glEnable(GL_POLYGON_OFFSET_FILL);
747 glPolygonOffset(2.0f, 4.0f);
752 glEnable(GL_CULL_FACE);
756 glBindVertexArray(m_VAO);
759 m_inShadowPass =
true;
761 queue->ExecuteShadowPass(
this);
763 m_inShadowPass =
false;
766 glDisable(GL_POLYGON_OFFSET_FILL);
767 glEnable(GL_CULL_FACE);
772 glBindFramebuffer(GL_FRAMEBUFFER, m_msaaFBO);
774 glBindFramebuffer(GL_FRAMEBUFFER, 0);
776 glViewport(savedViewport[0], savedViewport[1], savedViewport[2], savedViewport[3]);
779 glActiveTexture(GL_TEXTURE3);
780 glBindTexture(GL_TEXTURE_2D, m_shadowDepthTex);
781 glActiveTexture(GL_TEXTURE0);
792bool OpenGLRenderer::CreateGBufferResources() {
793 if (m_gbufferCreated)
return true;
796 SDL_GetWindowSize(m_Window->GetSDLWindow(), &w, &h);
801 glGenFramebuffers(1, &m_gbufferFBO);
802 glBindFramebuffer(GL_FRAMEBUFFER, m_gbufferFBO);
804 auto makeColorTex = [](GLuint& tex, GLenum internalFmt,
int w,
int h) {
805 glGenTextures(1, &tex);
806 glBindTexture(GL_TEXTURE_2D, tex);
807 GLenum fmt = GL_RGBA;
808 GLenum type = GL_UNSIGNED_BYTE;
809 if (internalFmt == GL_RGBA16F || internalFmt == GL_RGBA32F) {
812 glTexImage2D(GL_TEXTURE_2D, 0, internalFmt, w, h, 0, fmt, type,
nullptr);
813 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
814 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
815 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
816 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
820 makeColorTex(m_gbufferAlbedoAO, GL_RGBA8, w, h);
821 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_gbufferAlbedoAO, 0);
824 makeColorTex(m_gbufferNormalRough, GL_RGBA16F, w, h);
825 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_gbufferNormalRough, 0);
828 makeColorTex(m_gbufferMetalEmit, GL_RGBA8, w, h);
829 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, m_gbufferMetalEmit, 0);
835 glGenTextures(1, &m_gbufferDepth);
836 glBindTexture(GL_TEXTURE_2D, m_gbufferDepth);
837 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, w, h, 0,
838 GL_DEPTH_COMPONENT, GL_FLOAT,
nullptr);
839 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
840 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
841 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
842 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
843 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_gbufferDepth, 0);
845 GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
846 GL_COLOR_ATTACHMENT2 };
847 glDrawBuffers(3, drawBuffers);
849 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
850 SLEAK_ERROR(
"OpenGL GBuffer FBO is not complete!");
851 glBindFramebuffer(GL_FRAMEBUFFER, 0);
854 glBindFramebuffer(GL_FRAMEBUFFER, 0);
857 m_gbufferShader =
new OpenGLShader();
858 if (!m_gbufferShader->compile(
"assets/shaders/gbuffer_gl.vert",
859 "assets/shaders/gbuffer_gl.frag")) {
860 SLEAK_ERROR(
"Failed to compile GBuffer geometry shader!");
861 delete m_gbufferShader;
862 m_gbufferShader =
nullptr;
863 CleanupGBufferResources();
868 m_lightingShader =
new OpenGLShader();
869 if (!m_lightingShader->compile(
"assets/shaders/lighting_pass_gl.vert",
870 "assets/shaders/lighting_pass_gl.frag")) {
871 SLEAK_ERROR(
"Failed to compile deferred lighting pass shader!");
872 delete m_lightingShader;
873 m_lightingShader =
nullptr;
874 CleanupGBufferResources();
879 glGenVertexArrays(1, &m_lightingVAO);
882 glGenBuffers(1, &m_deferredCBUBO);
883 glBindBuffer(GL_UNIFORM_BUFFER, m_deferredCBUBO);
884 glBufferData(GL_UNIFORM_BUFFER,
sizeof(RenderEngine::DeferredCBData),
nullptr, GL_DYNAMIC_DRAW);
885 glBindBuffer(GL_UNIFORM_BUFFER, 0);
886 glBindBufferBase(GL_UNIFORM_BUFFER, 6, m_deferredCBUBO);
887 m_deferredCBCreated =
true;
889 m_gbufferCreated =
true;
890 SLEAK_INFO(
"OpenGL GBuffer created ({}x{}) — deferred rendering active", w, h);
892 if (!CreateSSAOResources()) {
893 SLEAK_WARN(
"OpenGL SSAO resources failed to initialize — SSAO disabled");
898void OpenGLRenderer::CleanupGBufferResources() {
899 CleanupSSAOResources();
901 if (m_gbufferAlbedoAO) { glDeleteTextures(1, &m_gbufferAlbedoAO); m_gbufferAlbedoAO = 0; }
902 if (m_gbufferNormalRough) { glDeleteTextures(1, &m_gbufferNormalRough); m_gbufferNormalRough = 0; }
903 if (m_gbufferMetalEmit) { glDeleteTextures(1, &m_gbufferMetalEmit); m_gbufferMetalEmit = 0; }
904 if (m_gbufferDepth) { glDeleteTextures(1, &m_gbufferDepth); m_gbufferDepth = 0; }
905 if (m_gbufferFBO) { glDeleteFramebuffers(1, &m_gbufferFBO); m_gbufferFBO = 0; }
906 if (m_lightingVAO) { glDeleteVertexArrays(1, &m_lightingVAO); m_lightingVAO = 0; }
907 if (m_deferredCBUBO) { glDeleteBuffers(1, &m_deferredCBUBO); m_deferredCBUBO = 0; }
908 delete m_gbufferShader; m_gbufferShader =
nullptr;
909 delete m_lightingShader; m_lightingShader =
nullptr;
910 m_gbufferCreated =
false;
911 m_deferredCBCreated =
false;
916void OpenGLRenderer::EnsureIBL() {
919 auto* game = app->GetGame();
921 auto* scene = game->GetActiveScene();
923 auto* skybox = scene->GetSkybox();
924 if (!skybox || !skybox->IsInitialized())
return;
925 auto* cube = skybox->GetCubemapTexture();
927 auto* glCube =
dynamic_cast<OpenGLCubemapTexture*
>(cube);
930 GLuint cubeHandle = glCube->GetGLTexture();
931 if (cubeHandle == 0)
return;
933 if (!m_ibl) m_ibl =
new OpenGLIBL();
934 if (cubeHandle != m_iblBoundCubemap) {
936 if (m_ibl->Initialize(cubeHandle)) {
937 m_iblBoundCubemap = cubeHandle;
939 m_iblBoundCubemap = 0;
943 if (m_iblSettingsUBO == 0) {
944 glGenBuffers(1, &m_iblSettingsUBO);
945 glBindBuffer(GL_UNIFORM_BUFFER, m_iblSettingsUBO);
946 glBufferData(GL_UNIFORM_BUFFER,
947 sizeof(IBLSettingsGPUData),
948 nullptr, GL_DYNAMIC_DRAW);
949 glBindBuffer(GL_UNIFORM_BUFFER, 0);
953void OpenGLRenderer::CleanupIBL() {
954 if (m_ibl) {
delete m_ibl; m_ibl =
nullptr; }
955 if (m_iblSettingsUBO) {
956 glDeleteBuffers(1, &m_iblSettingsUBO);
957 m_iblSettingsUBO = 0;
959 m_iblBoundCubemap = 0;
962void OpenGLRenderer::RecreateGBufferOnResize(
int width,
int height) {
963 if (!m_gbufferCreated)
return;
965 m_gbufferWidth =
width;
969 glBindTexture(GL_TEXTURE_2D, m_gbufferAlbedoAO);
970 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
width,
height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
973 glBindTexture(GL_TEXTURE_2D, m_gbufferNormalRough);
974 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F,
width,
height, 0, GL_RGBA, GL_FLOAT,
nullptr);
977 glBindTexture(GL_TEXTURE_2D, m_gbufferMetalEmit);
978 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
width,
height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
981 glBindTexture(GL_TEXTURE_2D, m_gbufferDepth);
982 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F,
width,
height, 0,
983 GL_DEPTH_COMPONENT, GL_FLOAT,
nullptr);
985 glBindTexture(GL_TEXTURE_2D, 0);
993 if (m_gbufferShader) {
996 glBindFramebuffer(GL_FRAMEBUFFER, m_gbufferFBO);
997 GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
998 GL_COLOR_ATTACHMENT2 };
999 glDrawBuffers(3, drawBuffers);
1001 glEnable(GL_DEPTH_TEST);
1002 glDepthMask(GL_TRUE);
1007 glActiveTexture(GL_TEXTURE3);
1008 glBindTexture(GL_TEXTURE_2D, 0);
1009 glActiveTexture(GL_TEXTURE0);
1011 m_gbufferShader->bind();
1012 glBindVertexArray(m_VAO);
1014 static bool warned =
false;
1016 SLEAK_ERROR(
"BindGBufferShader: m_gbufferShader is NULL!");
1022void OpenGLRenderer::BindPBRMaterial(Sleak::Material* material) {
1030void OpenGLRenderer::UpdateDeferredCB(
const void* data, uint32_t size) {
1031 if (!m_deferredCBCreated)
return;
1032 glBindBuffer(GL_UNIFORM_BUFFER, m_deferredCBUBO);
1033 glBufferSubData(GL_UNIFORM_BUFFER, 0, size, data);
1034 glBindBuffer(GL_UNIFORM_BUFFER, 0);
1035 glBindBufferBase(GL_UNIFORM_BUFFER, 6, m_deferredCBUBO);
1039 m_inGeometryPass =
false;
1041 if (!m_gbufferCreated || !m_lightingShader)
return;
1054 constexpr GLuint targetFBO = 0;
1055 glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
1058 glClearColor(0.39f, 0.58f, 0.93f, 1.0f);
1059 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1064 glEnable(GL_DEPTH_TEST);
1065 glDepthFunc(GL_ALWAYS);
1066 glDepthMask(GL_TRUE);
1067 glDisable(GL_BLEND);
1070 m_lightingShader->bind();
1073 glActiveTexture(GL_TEXTURE8); glBindTexture(GL_TEXTURE_2D, m_gbufferAlbedoAO);
1074 glActiveTexture(GL_TEXTURE9); glBindTexture(GL_TEXTURE_2D, m_gbufferNormalRough);
1075 glActiveTexture(GL_TEXTURE10); glBindTexture(GL_TEXTURE_2D, m_gbufferMetalEmit);
1076 glActiveTexture(GL_TEXTURE11); glBindTexture(GL_TEXTURE_2D, m_gbufferDepth);
1078 glActiveTexture(GL_TEXTURE13);
1079 glBindTexture(GL_TEXTURE_2D, m_ssaoCreated ? m_ssaoBlurTex : 0);
1081 GLint ssaoLoc = glGetUniformLocation(
1082 m_lightingShader->GetProgram(),
"uSSAOEnabled");
1084 glUniform1i(ssaoLoc, (
m_ssaoEnabled && m_ssaoCreated) ? 1 : 0);
1086 glActiveTexture(GL_TEXTURE0);
1090 if (m_shadowMapCreated) {
1091 glActiveTexture(GL_TEXTURE3);
1092 glBindTexture(GL_TEXTURE_2D, m_shadowDepthTex);
1093 glActiveTexture(GL_TEXTURE0);
1098 bool iblReady = (m_ibl && m_ibl->IsInitialized());
1099 if (iblReady) m_ibl->Bind();
1100 if (m_iblSettingsUBO) {
1101 IBLSettingsGPUData ibl{};
1106 glBindBuffer(GL_UNIFORM_BUFFER, m_iblSettingsUBO);
1107 glBufferSubData(GL_UNIFORM_BUFFER, 0,
sizeof(ibl), &ibl);
1108 glBindBuffer(GL_UNIFORM_BUFFER, 0);
1109 glBindBufferBase(GL_UNIFORM_BUFFER, 10, m_iblSettingsUBO);
1113 glBindVertexArray(m_lightingVAO);
1114 glDrawArrays(GL_TRIANGLES, 0, 3);
1117 glDepthFunc(GL_LESS);
1119 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1122 glBindVertexArray(m_VAO);
1126 m_inForwardTransparentPass =
true;
1129 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1133 m_inForwardTransparentPass =
false;
1142bool OpenGLRenderer::CreateSSAOResources() {
1143 if (m_ssaoCreated)
return true;
1144 if (!m_gbufferCreated)
return false;
1146 int w = m_gbufferWidth;
1147 int h = m_gbufferHeight;
1149 auto makeR8Tex = [](GLuint& tex,
int w,
int h) {
1150 glGenTextures(1, &tex);
1151 glBindTexture(GL_TEXTURE_2D, tex);
1152 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE,
nullptr);
1153 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1154 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1155 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1156 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1160 makeR8Tex(m_ssaoTexture, w, h);
1161 glGenFramebuffers(1, &m_ssaoFBO);
1162 glBindFramebuffer(GL_FRAMEBUFFER, m_ssaoFBO);
1163 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1164 GL_TEXTURE_2D, m_ssaoTexture, 0);
1165 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
1167 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1168 CleanupSSAOResources();
1173 makeR8Tex(m_ssaoBlurTex, w, h);
1174 glGenFramebuffers(1, &m_ssaoBlurFBO);
1175 glBindFramebuffer(GL_FRAMEBUFFER, m_ssaoBlurFBO);
1176 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1177 GL_TEXTURE_2D, m_ssaoBlurTex, 0);
1178 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
1180 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1181 CleanupSSAOResources();
1184 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1187 SSAOKernel kernelGen;
1188 kernelGen.Generate();
1190 glGenTextures(1, &m_ssaoNoiseTex);
1191 glBindTexture(GL_TEXTURE_2D, m_ssaoNoiseTex);
1192 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F,
1194 GL_RGBA, GL_FLOAT, kernelGen.noiseData.data());
1195 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1196 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1197 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1198 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1201 m_ssaoShader =
new OpenGLShader();
1202 if (!m_ssaoShader->compile(
"assets/shaders/ssao_gl.vert",
1203 "assets/shaders/ssao_gl.frag")) {
1205 CleanupSSAOResources();
1208 m_ssaoBlurShader =
new OpenGLShader();
1209 if (!m_ssaoBlurShader->compile(
"assets/shaders/ssao_blur_gl.vert",
1210 "assets/shaders/ssao_blur_gl.frag")) {
1211 SLEAK_ERROR(
"Failed to compile SSAO blur shader");
1212 CleanupSSAOResources();
1218 glGenBuffers(1, &m_ssaoSettingsUBO);
1219 glBindBuffer(GL_UNIFORM_BUFFER, m_ssaoSettingsUBO);
1220 glBufferData(GL_UNIFORM_BUFFER,
sizeof(SSAOSettingsGPUData),
nullptr, GL_DYNAMIC_DRAW);
1223 glGenBuffers(1, &m_ssaoKernelUBO);
1224 glBindBuffer(GL_UNIFORM_BUFFER, m_ssaoKernelUBO);
1225 glBufferData(GL_UNIFORM_BUFFER,
1227 kernelGen.samples.data(), GL_STATIC_DRAW);
1230 glGenBuffers(1, &m_ssaoCameraUBO);
1231 glBindBuffer(GL_UNIFORM_BUFFER, m_ssaoCameraUBO);
1232 glBufferData(GL_UNIFORM_BUFFER,
sizeof(
float) * 16 * 3,
nullptr, GL_DYNAMIC_DRAW);
1234 glBindBuffer(GL_UNIFORM_BUFFER, 0);
1236 m_ssaoCreated =
true;
1237 SLEAK_INFO(
"OpenGL SSAO ready ({}x{})", w, h);
1241void OpenGLRenderer::CleanupSSAOResources() {
1242 if (m_ssaoFBO) { glDeleteFramebuffers(1, &m_ssaoFBO); m_ssaoFBO = 0; }
1243 if (m_ssaoBlurFBO) { glDeleteFramebuffers(1, &m_ssaoBlurFBO); m_ssaoBlurFBO = 0; }
1244 if (m_ssaoTexture) { glDeleteTextures(1, &m_ssaoTexture); m_ssaoTexture = 0; }
1245 if (m_ssaoBlurTex) { glDeleteTextures(1, &m_ssaoBlurTex); m_ssaoBlurTex = 0; }
1246 if (m_ssaoNoiseTex) { glDeleteTextures(1, &m_ssaoNoiseTex); m_ssaoNoiseTex = 0; }
1247 if (m_ssaoSettingsUBO) { glDeleteBuffers(1, &m_ssaoSettingsUBO); m_ssaoSettingsUBO = 0; }
1248 if (m_ssaoKernelUBO) { glDeleteBuffers(1, &m_ssaoKernelUBO); m_ssaoKernelUBO = 0; }
1249 if (m_ssaoCameraUBO) { glDeleteBuffers(1, &m_ssaoCameraUBO); m_ssaoCameraUBO = 0; }
1250 delete m_ssaoShader; m_ssaoShader =
nullptr;
1251 delete m_ssaoBlurShader; m_ssaoBlurShader =
nullptr;
1252 m_ssaoCreated =
false;
1255void OpenGLRenderer::RecreateSSAOOnResize(
int width,
int height) {
1256 if (!m_ssaoCreated)
return;
1258 glBindTexture(GL_TEXTURE_2D, m_ssaoTexture);
1259 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8,
width,
height, 0, GL_RED, GL_UNSIGNED_BYTE,
nullptr);
1261 glBindTexture(GL_TEXTURE_2D, m_ssaoBlurTex);
1262 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8,
width,
height, 0, GL_RED, GL_UNSIGNED_BYTE,
nullptr);
1264 glBindTexture(GL_TEXTURE_2D, 0);
1267void OpenGLRenderer::ExecuteSSAOPass() {
1268 if (!m_ssaoCreated || !m_ssaoShader || !m_ssaoBlurShader)
return;
1273 glBindFramebuffer(GL_FRAMEBUFFER, m_ssaoBlurFBO);
1274 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
1275 glClear(GL_COLOR_BUFFER_BIT);
1276 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1281 SSAOSettingsGPUData s{};
1286 s.ScreenWidth =
static_cast<float>(m_gbufferWidth);
1287 s.ScreenHeight =
static_cast<float>(m_gbufferHeight);
1290 s.FarPlane = 2000.0f;
1292 glBindBuffer(GL_UNIFORM_BUFFER, m_ssaoSettingsUBO);
1293 glBufferSubData(GL_UNIFORM_BUFFER, 0,
sizeof(s), &s);
1298 float camData[16 * 3];
1299 std::memcpy(camData, &P(0,0),
sizeof(
float) * 16);
1300 std::memcpy(camData + 16, &V(0,0),
sizeof(
float) * 16);
1307 const float* m = &VP(0,0);
1308 float i0 = m[5]*m[10]*m[15] - m[5]*m[11]*m[14] - m[9]*m[6]*m[15] + m[9]*m[7]*m[14] + m[13]*m[6]*m[11] - m[13]*m[7]*m[10];
1309 float i4 = -m[4]*m[10]*m[15] + m[4]*m[11]*m[14] + m[8]*m[6]*m[15] - m[8]*m[7]*m[14] - m[12]*m[6]*m[11] + m[12]*m[7]*m[10];
1310 float i8 = m[4]*m[9] *m[15] - m[4]*m[11]*m[13] - m[8]*m[5]*m[15] + m[8]*m[7]*m[13] + m[12]*m[5]*m[11] - m[12]*m[7]*m[9];
1311 float i12 = -m[4]*m[9] *m[14] + m[4]*m[10]*m[13] + m[8]*m[5]*m[14] - m[8]*m[6]*m[13] - m[12]*m[5]*m[10] + m[12]*m[6]*m[9];
1312 float det = m[0]*i0 + m[1]*i4 + m[2]*i8 + m[3]*i12;
1313 if (std::fabs(det) < 1e-8f) {
1314 std::memset(invP, 0,
sizeof(invP));
1315 invP[0] = invP[5] = invP[10] = invP[15] = 1.0f;
1317 float id = 1.0f / det;
1318 invP[0] = i0 * id; invP[4] = i4 * id; invP[8] = i8 * id; invP[12] = i12 * id;
1319 invP[1] = (-m[1]*m[10]*m[15] + m[1]*m[11]*m[14] + m[9]*m[2]*m[15] - m[9]*m[3]*m[14] - m[13]*m[2]*m[11] + m[13]*m[3]*m[10]) *
id;
1320 invP[5] = ( m[0]*m[10]*m[15] - m[0]*m[11]*m[14] - m[8]*m[2]*m[15] + m[8]*m[3]*m[14] + m[12]*m[2]*m[11] - m[12]*m[3]*m[10]) *
id;
1321 invP[9] = (-m[0]*m[9] *m[15] + m[0]*m[11]*m[13] + m[8]*m[1]*m[15] - m[8]*m[3]*m[13] - m[12]*m[1]*m[11] + m[12]*m[3]*m[9] ) *
id;
1322 invP[13] = ( m[0]*m[9] *m[14] - m[0]*m[10]*m[13] - m[8]*m[1]*m[14] + m[8]*m[2]*m[13] + m[12]*m[1]*m[10] - m[12]*m[2]*m[9] ) *
id;
1323 invP[2] = ( m[1]*m[6]*m[15] - m[1]*m[7]*m[14] - m[5]*m[2]*m[15] + m[5]*m[3]*m[14] + m[13]*m[2]*m[7] - m[13]*m[3]*m[6]) *
id;
1324 invP[6] = (-m[0]*m[6]*m[15] + m[0]*m[7]*m[14] + m[4]*m[2]*m[15] - m[4]*m[3]*m[14] - m[12]*m[2]*m[7] + m[12]*m[3]*m[6]) *
id;
1325 invP[10] = ( m[0]*m[5]*m[15] - m[0]*m[7]*m[13] - m[4]*m[1]*m[15] + m[4]*m[3]*m[13] + m[12]*m[1]*m[7] - m[12]*m[3]*m[5]) *
id;
1326 invP[14] = (-m[0]*m[5]*m[14] + m[0]*m[6]*m[13] + m[4]*m[1]*m[14] - m[4]*m[2]*m[13] - m[12]*m[1]*m[6] + m[12]*m[2]*m[5]) *
id;
1327 invP[3] = (-m[1]*m[6]*m[11] + m[1]*m[7]*m[10] + m[5]*m[2]*m[11] - m[5]*m[3]*m[10] - m[9]*m[2]*m[7] + m[9]*m[3]*m[6]) *
id;
1328 invP[7] = ( m[0]*m[6]*m[11] - m[0]*m[7]*m[10] - m[4]*m[2]*m[11] + m[4]*m[3]*m[10] + m[8]*m[2]*m[7] - m[8]*m[3]*m[6]) *
id;
1329 invP[11] = (-m[0]*m[5]*m[11] + m[0]*m[7]*m[9] + m[4]*m[1]*m[11] - m[4]*m[3]*m[9] - m[8]*m[1]*m[7] + m[8]*m[3]*m[5]) *
id;
1330 invP[15] = ( m[0]*m[5]*m[10] - m[0]*m[6]*m[9] - m[4]*m[1]*m[10] + m[4]*m[2]*m[9] + m[8]*m[1]*m[6] - m[8]*m[2]*m[5]) *
id;
1333 std::memcpy(camData + 32, invP,
sizeof(
float) * 16);
1335 glBindBuffer(GL_UNIFORM_BUFFER, m_ssaoCameraUBO);
1336 glBufferSubData(GL_UNIFORM_BUFFER, 0,
sizeof(camData), camData);
1337 glBindBuffer(GL_UNIFORM_BUFFER, 0);
1342 glBindBufferBase(GL_UNIFORM_BUFFER, 7, m_ssaoSettingsUBO);
1343 glBindBufferBase(GL_UNIFORM_BUFFER, 8, m_ssaoKernelUBO);
1344 glBindBufferBase(GL_UNIFORM_BUFFER, 9, m_ssaoCameraUBO);
1347 glBindFramebuffer(GL_FRAMEBUFFER, m_ssaoFBO);
1348 glViewport(0, 0, m_gbufferWidth, m_gbufferHeight);
1349 glDisable(GL_DEPTH_TEST);
1350 glDepthMask(GL_FALSE);
1351 glDisable(GL_BLEND);
1352 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
1353 glClear(GL_COLOR_BUFFER_BIT);
1355 m_ssaoShader->bind();
1356 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_gbufferDepth);
1357 glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_gbufferNormalRough);
1358 glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_ssaoNoiseTex);
1359 glActiveTexture(GL_TEXTURE0);
1361 glBindVertexArray(m_lightingVAO);
1362 glDrawArrays(GL_TRIANGLES, 0, 3);
1365 glBindFramebuffer(GL_FRAMEBUFFER, m_ssaoBlurFBO);
1366 glClear(GL_COLOR_BUFFER_BIT);
1368 m_ssaoBlurShader->bind();
1369 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_ssaoTexture);
1371 glDrawArrays(GL_TRIANGLES, 0, 3);
1374 glEnable(GL_DEPTH_TEST);
1375 glDepthMask(GL_TRUE);
1377 glBindFramebuffer(GL_FRAMEBUFFER, 0);
static Application * GetInstance()
The one Application for this process, or null before construction.
static const Math::Matrix4 & GetMainProjectionMatrix()
static const Math::Matrix4 & GetMainViewMatrix()
void BindTexturesAndCB()
Binds textures and uploads the material CB for the deferred geometry pass, skipping the shader bind.
Backend-agnostic GPU buffer: vertex, index, constant, or resource view target.
OpenGL VBO/EBO/UBO wrapper; target is derived from BufferType at construction.
OpenGL cubemap texture, loadable from six face images, a panorama, or a procedural gradient.
static constexpr uint32_t PREFILTER_MIPS
virtual ~OpenGLRenderer()
virtual void SetDepthWrite(bool enabled) override
virtual Texture * CreateTexture(const std::string &TexturePath) override
Loads a texture from disk.
virtual void SetRenderFace(RenderFace face) override
virtual void DrawInstance(uint32_t instanceCount, uint32_t vertexPerInstance) override
Non-indexed instanced draw.
virtual void BindIndexBuffer(RefPtr< BufferBase > buffer, uint32_t slot=0) override
virtual void SetCullEnabled(bool enabled) override
virtual Shader * CreateShader(const std::string &shaderSource) override
Compiles a shader program from source or a source-file path.
virtual Texture * CreateTextureFromData(uint32_t width, uint32_t height, void *data) override
Creates a texture from an in-memory pixel buffer.
virtual void SetViewport(float x, float y, float width, float height, float minDepth=0.0f, float maxDepth=1.0f) override
Sets the active viewport rect and depth range in pixel/NDC units.
OpenGLRenderer(Window *window)
Registers this backend's buffer/shader/texture factories with ResourceManager.
virtual BufferBase * CreateBuffer(BufferType Type, uint32_t size, void *data) override
Allocates a backend buffer, optionally seeded with initial data.
virtual void BeginDebugLinePass() override
void BeginRender() override
Acquires the next frame and prepares it for command recording.
void ApplyMSAAChange() override
virtual void DrawIndexedInstance(uint32_t instanceCount, uint32_t indexPerInstance) override
Indexed instanced draw.
void EndRender() override
Submits recorded commands and presents the frame.
virtual void BindTexture(RefPtr< Sleak::Texture > texture, uint32_t slot=0) override
virtual void Resize(uint32_t width, uint32_t height) override
Recreates swapchain-dependent resources for a new surface size.
virtual void BindConstantBuffer(RefPtr< BufferBase > buffer, uint32_t slot=0) override
virtual void ClearDepthStencil(bool clearDepth, bool clearStencil, float depth, uint8_t stencil) override
virtual bool CreateImGUI() override
Initializes ImGui integration for this backend.
void ApplyVSyncChange() override
virtual void DrawIndexed(uint32_t indexCount) override
Indexed draw from the currently bound vertex/index buffers.
virtual void ClearRenderTarget(float r, float g, float b, float a) override
virtual void SetDepthCompare(DepthCompare compare) override
bool Initialize() override
Creates the device, swapchain, and backend resources.
Texture * CreateCubemapTextureFromPanorama(const std::string &panoramaPath)
Loads a cubemap by converting a single equirectangular panorama.
virtual void SetRenderMode(RenderMode mode) override
virtual void BindVertexBuffer(RefPtr< BufferBase > buffer, uint32_t slot=0) override
virtual void BindBoneBuffer(RefPtr< BufferBase > buffer) override
virtual void EndDebugLinePass() override
virtual void Draw(uint32_t vertexCount) override
Non-indexed draw from the currently bound vertex buffer.
Texture * CreateCubemapTexture(const std::array< std::string, 6 > &facePaths)
Loads a cubemap from six face image paths.
GLSL shader program compiled from either a combined source file or separate vertex/fragment files.
OpenGL 2D texture, loadable from disk or an in-memory pixel buffer.
static RenderCommandQueue * GetInstance()
Lazily creates and returns the process-wide singleton instance.
virtual void BeginForwardTransparentPass()
virtual void EndForwardTransparentPass()
virtual bool IsDeferredEnabled() const
virtual void BindGBufferShader()
virtual void ExecuteDeferredLightingPass()
virtual void ConfigureRenderMode()=0
Applies the current RenderMode (fill/wireframe/points) to backend state.
virtual void ConfigureRenderFace()=0
Applies the current RenderFace (cull mode) to backend state.
bool m_vsyncChangeRequested
void UpdateFrameMetrics()
Rolls up frame count into rate/time/triangle stats once per MetricUpdateInterval.
uint32_t m_msaaSampleCount
uint32_t m_pendingMsaaSampleCount
void SetPerformanceCounter(bool value)
bool m_msaaChangeRequested
uint32_t m_maxMsaaSampleCount
uint32_t m_shadowMapResolution
bool m_shadowResourcesCreated
static void RegisterCreateCubemapTextureFromPanorama(T *instance, Texture *(T::*method)(const std::string &))
static void RegisterCreateCubemapTexture(T *instance, Texture *(T::*method)(const std::array< std::string, 6 > &))
static void RegisterCreateBuffer(T *instance, BufferBase *(T::*method)(BufferType, uint32_t, void *))
static void RegisterCreateShader(T *instance, Shader *(T::*method)(const std::string &))
static void RegisterCreateTextureFromMemory(std::function< Texture *(const void *, uint32_t, uint32_t, TextureFormat, uint32_t)> func)
static void RegisterCreateTexture(T *instance, Texture *(T::*method)(const std::string &))
Backend-agnostic compiled shader program.
virtual bool IsValid() const
True if the pointer is non-null.
SDL_Window * GetSDLWindow()
Matrix< float, 4, 4 > Matrix4
Backend-facing rendering layer shared by the four graphics backends.
DepthCompare
Depth test comparison function.
RenderMode
Rasterizer fill style for a draw.
BufferType
GPU buffer usage kind, drives backend binding flags and layout.
RenderFace
Which triangle winding gets culled.
Root namespace for everything the engine exposes.
static constexpr uint32_t MAX_KERNEL_SIZE
static constexpr uint32_t NOISE_SIZE
std::vector< VertexAttribute > attributes