/* =========================================================================== Copyright (C) 2024 Gian 'myT' Schellenbaum This file is part of Challenge Quake 3 (CNQ3). Challenge Quake 3 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Challenge Quake 3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Challenge Quake 3. If not, see . =========================================================================== */ // Cinematic Rendering Pipeline - G-Buffer texture visualization #include "crp_local.h" #include "../client/cl_imgui.h" #include "compshaders/crp/fullscreen.h" #include "compshaders/crp/gbufferviz_depth.h" #include "compshaders/crp/gbufferviz_normal.h" #include "compshaders/crp/gbufferviz_position.h" #pragma pack(push, 4) struct LinearizeDepthRC { uint32_t depthTextureIndex; float linearDepthA; float linearDepthB; float zFarInv; }; struct DecodeNormalsRC { uint32_t normalTextureIndex; }; struct DecodePositionRC { uint32_t textureIndex; uint32_t coloredDelta; }; #pragma pack(pop) void GBufferViz::Init() { { GraphicsPipelineDesc desc("G-Buffer Depth"); desc.shortLifeTime = true; desc.vertexShader = ShaderByteCode(g_fullscreen_vs); desc.pixelShader = ShaderByteCode(g_gbufferviz_depth_ps); desc.depthStencil.DisableDepth(); desc.rasterizer.cullMode = CT_TWO_SIDED; desc.AddRenderTarget(0, crp.renderTargetFormat); linearizeDepthPipeline = CreateGraphicsPipeline(desc); } { GraphicsPipelineDesc desc("G-Buffer Normal"); desc.shortLifeTime = true; desc.vertexShader = ShaderByteCode(g_fullscreen_vs); desc.pixelShader = ShaderByteCode(g_gbufferviz_normal_ps); desc.depthStencil.DisableDepth(); desc.rasterizer.cullMode = CT_TWO_SIDED; desc.AddRenderTarget(0, crp.renderTargetFormat); decodeNormalsPipeline = CreateGraphicsPipeline(desc); } { GraphicsPipelineDesc desc("G-Buffer Position"); desc.shortLifeTime = true; desc.vertexShader = ShaderByteCode(g_fullscreen_vs); desc.pixelShader = ShaderByteCode(g_gbufferviz_position_ps); desc.depthStencil.DisableDepth(); desc.rasterizer.cullMode = CT_TWO_SIDED; desc.AddRenderTarget(0, crp.renderTargetFormat); decodeShadingPositionPipeline = CreateGraphicsPipeline(desc); } } void GBufferViz::DrawGUI() { GUI_AddMainMenuItem(GUI_MainMenu::Tools, "Show G-Buffer", "", &windowActive); if(!windowActive) { return; } const HTexture renderTarget = crp.GetReadRenderTarget(); if(textureIndex == GBufferTexture::Depth) { srp.renderMode = RenderMode::None; SCOPED_RENDER_PASS("Debug Depth", 1.0f, 1.0f, 1.0f); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdBeginBarrier(); CmdTextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); LinearizeDepthRC rc = {}; rc.depthTextureIndex = GetTextureIndexSRV(crp.depthTexture); RB_LinearDepthConstants(&rc.linearDepthA, &rc.linearDepthB); rc.zFarInv = 1.0f / backEnd.viewParms.zFar; CmdBindRenderTargets(1, &renderTarget, NULL); CmdBindPipeline(linearizeDepthPipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); } else if(textureIndex == GBufferTexture::Normal) { srp.renderMode = RenderMode::None; SCOPED_RENDER_PASS("Debug Normal", 1.0f, 1.0f, 1.0f); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdBeginBarrier(); CmdTextureBarrier(crp.normalTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); DecodeNormalsRC rc = {}; rc.normalTextureIndex = GetTextureIndexSRV(crp.normalTexture); CmdBindRenderTargets(1, &renderTarget, NULL); CmdBindPipeline(decodeNormalsPipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); } else if(textureIndex == GBufferTexture::ShadingPositionDelta) { srp.renderMode = RenderMode::None; SCOPED_RENDER_PASS("Debug Position", 1.0f, 1.0f, 1.0f); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdBeginBarrier(); CmdTextureBarrier(crp.shadingPositionTexture, ResourceStates::PixelShaderAccessBit); CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); DecodePositionRC rc = {}; rc.textureIndex = GetTextureIndexSRV(crp.shadingPositionTexture); rc.coloredDelta = coloredPositionDelta ? 1 : 0; CmdBindRenderTargets(1, &renderTarget, NULL); CmdBindPipeline(decodeShadingPositionPipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); } if(ImGui::Begin("G-Buffer", &windowActive, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::RadioButton("Depth", &textureIndex, GBufferTexture::Depth); ImGui::SameLine(); ImGui::RadioButton("Normal", &textureIndex, GBufferTexture::Normal); ImGui::SameLine(); ImGui::RadioButton("Light", &textureIndex, GBufferTexture::Light); ImGui::SameLine(); ImGui::RadioButton("Shading Position Delta", &textureIndex, GBufferTexture::ShadingPositionDelta); if(textureIndex == GBufferTexture::ShadingPositionDelta) { ImGui::SameLine(); ImGui::Checkbox("Per-axis delta", &coloredPositionDelta); } const ImVec2 resolution = ImVec2(glConfig.vidWidth / 2, glConfig.vidHeight / 2); HTexture texture; switch(textureIndex) { case GBufferTexture::Depth: texture = renderTarget; break; case GBufferTexture::Normal: texture = renderTarget; break; case GBufferTexture::Light: texture = crp.lightTexture; break; case GBufferTexture::ShadingPositionDelta: texture = renderTarget; break; default: Q_assert(!"Invalid G-Buffer texture index"); texture = crp.lightTexture; break; } ImGui::Image((ImTextureID)GetTextureIndexSRV(texture), resolution); CmdBeginBarrier(); CmdTextureBarrier(texture, ResourceStates::PixelShaderAccessBit); CmdEndBarrier(); } ImGui::End(); }