added pre-pass w/ normals, unified clip plane generation

motion vectors to be done later
This commit is contained in:
myT 2024-01-19 23:57:40 +01:00
parent 7fab6ea376
commit 0cae0a9545
23 changed files with 563 additions and 115 deletions

View file

@ -1,6 +1,6 @@
/*
===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum
Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).
@ -57,6 +57,7 @@ struct Tessellator
enum Id
{
None,
Prepass,
Opaque,
Transp,
Count
@ -90,6 +91,25 @@ struct PSOCache
uint32_t entryCount = 1; // we treat index 0 as invalid
};
struct Prepass
{
void Init();
void Draw(const drawSceneViewCommand_t& cmd);
void ProcessShader(shader_t& shader);
void TessellationOverflow();
private:
void BeginBatch(const shader_t* shader);
void EndBatch();
PSOCache::Entry psoCacheEntries[128];
PSOCache psoCache;
float clipPlane[4];
bool batchOldDepthHack;
bool batchDepthHack;
};
struct WorldOpaque
{
void Init();
@ -304,6 +324,8 @@ struct CRP : IRenderPipeline
float frameSeed;
HTexture readbackRenderTarget;
HTexture depthTexture;
HTexture normalTexture;
HTexture motionVectorTexture;
HTexture renderTarget;
TextureFormat::Id renderTargetFormat;
HTexture renderTargets[2];
@ -328,6 +350,7 @@ struct CRP : IRenderPipeline
MipMapGenerator mipMapGen;
ImGUI imgui;
Nuklear nuklear;
Prepass prepass;
WorldOpaque opaque;
WorldTransp transp;
TranspResolve transpResolve;

View file

@ -305,6 +305,28 @@ void CRP::Init()
depthTexture = RHI::CreateTexture(desc);
}
{
TextureDesc desc("GBuffer normals", glConfig.vidWidth, glConfig.vidHeight);
desc.committedResource = true;
desc.shortLifeTime = true;
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit | ResourceStates::ComputeShaderAccessBit;
desc.format = TextureFormat::RG32_SNorm;
desc.SetClearColor(vec4_zero);
normalTexture = RHI::CreateTexture(desc);
}
{
TextureDesc desc("GBuffer motion vectors", glConfig.vidWidth, glConfig.vidHeight);
desc.committedResource = true;
desc.shortLifeTime = true;
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit | ResourceStates::ComputeShaderAccessBit;
desc.format = TextureFormat::RG32_Float;
desc.SetClearColor(vec4_zero);
motionVectorTexture = RHI::CreateTexture(desc);
}
{
GraphicsPipelineDesc desc("blit LDR");
desc.vertexShader = ShaderByteCode(g_fullscreen_vs);
@ -322,6 +344,7 @@ void CRP::Init()
imgui.Init(true, ShaderByteCode(g_imgui_vs), ShaderByteCode(g_imgui_ps), renderTargetFormat, RHI_MAKE_NULL_HANDLE(), NULL);
nuklear.Init(true, ShaderByteCode(g_nuklear_vs), ShaderByteCode(g_nuklear_ps), renderTargetFormat, RHI_MAKE_NULL_HANDLE(), NULL);
mipMapGen.Init(true, ShaderByteCode(g_mip_1_cs), ShaderByteCode(g_mip_2_cs), ShaderByteCode(g_mip_3_cs));
prepass.Init();
opaque.Init();
transp.Init();
transpResolve.Init();
@ -464,6 +487,7 @@ void CRP::ProcessShader(shader_t& shader)
{
if(shader.isOpaque)
{
prepass.ProcessShader(shader);
opaque.ProcessShader(shader);
}
else
@ -553,6 +577,7 @@ void CRP::TessellationOverflow()
{
switch(tess.tessellator)
{
case Tessellator::Prepass: prepass.TessellationOverflow(); break;
case Tessellator::Opaque: opaque.TessellationOverflow(); break;
case Tessellator::Transp: transp.TessellationOverflow(); break;
default: break;
@ -598,6 +623,7 @@ void CRP::DrawSceneView(const drawSceneViewCommand_t& cmd)
CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit);
CmdEndBarrier();
CmdClearColorTarget(renderTarget, cmd.clearColor, &rect);
prepass.Draw(newCmd);
opaque.Draw(newCmd);
fog.Draw();
transp.Draw(newCmd);
@ -620,6 +646,7 @@ void CRP::DrawSceneView(const drawSceneViewCommand_t& cmd)
}
else
{
prepass.Draw(cmd);
opaque.Draw(cmd);
fog.Draw();
transp.Draw(cmd);

View file

@ -1,6 +1,6 @@
/*
===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum
Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).
@ -65,47 +65,17 @@ void WorldOpaque::Draw(const drawSceneViewCommand_t& cmd)
backEnd.refdef = cmd.refdef;
backEnd.viewParms = cmd.viewParms;
if(backEnd.viewParms.isPortal)
{
float plane[4];
plane[0] = backEnd.viewParms.portalPlane.normal[0];
plane[1] = backEnd.viewParms.portalPlane.normal[1];
plane[2] = backEnd.viewParms.portalPlane.normal[2];
plane[3] = backEnd.viewParms.portalPlane.dist;
float plane2[4];
plane2[0] = DotProduct(backEnd.viewParms.orient.axis[0], plane);
plane2[1] = DotProduct(backEnd.viewParms.orient.axis[1], plane);
plane2[2] = DotProduct(backEnd.viewParms.orient.axis[2], plane);
plane2[3] = DotProduct(plane, backEnd.viewParms.orient.origin) - plane[3];
float* o = plane;
const float* m = s_flipMatrix;
const float* v = plane2;
o[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
o[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
o[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
o[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
memcpy(clipPlane, plane, sizeof(clipPlane));
}
else
{
memset(clipPlane, 0, sizeof(clipPlane));
}
RB_CreateClipPlane(clipPlane);
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
CmdBeginBarrier();
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthReadBit);
CmdBufferBarrier(srp.traceRenderBuffer, ResourceStates::UnorderedAccessBit);
CmdEndBarrier();
CmdClearDepthStencilTarget(crp.depthTexture, true, 0.0f);
GeoBuffers& db = crp.dynBuffers[GetFrameIndex()];
db.BeginUpload();
@ -207,16 +177,13 @@ void WorldOpaque::ProcessShader(shader_t& shader)
desc.vertexShader = g_opaque_vs;
desc.pixelShader = g_opaque_ps;
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Position, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::TexCoord, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Color, DataType::UNorm8, 4, 0);
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
desc.depthStencil.depthComparison =
(stateBits & GLS_DEPTHFUNC_EQUAL) != 0 ?
ComparisonFunction::Equal :
ComparisonFunction::GreaterEqual;
desc.depthStencil.enableDepthTest = (stateBits & GLS_DEPTHTEST_DISABLE) == 0;
desc.depthStencil.enableDepthWrites = (stateBits & GLS_DEPTHMASK_TRUE) != 0;
desc.depthStencil.depthComparison = shader.isSky ? ComparisonFunction::GreaterEqual : ComparisonFunction::Equal;
desc.depthStencil.enableDepthTest = true;
desc.depthStencil.enableDepthWrites = false;
desc.rasterizer.cullMode = shader.cullType;
desc.rasterizer.polygonOffset = shader.polygonOffset != 0;
desc.rasterizer.clampDepth = clampDepth;

View file

@ -0,0 +1,294 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - full opaque pre-pass
#include "crp_local.h"
#include "compshaders/crp/prepass.h"
#pragma pack(push, 4)
struct PrepassVertexRC
{
float modelViewMatrix[16];
float projectionMatrix[16];
float normalMatrix[16];
float clipPlane[4];
};
struct PrepassPixelRC
{
uint32_t textureIndex;
uint32_t samplerIndex;
uint32_t alphaTest;
};
#pragma pack(pop)
void Prepass::Init()
{
psoCache.Init(psoCacheEntries, ARRAY_LEN(psoCacheEntries));
}
void Prepass::Draw(const drawSceneViewCommand_t& cmd)
{
if(cmd.numDrawSurfs - cmd.numTranspSurfs <= 0)
{
return;
}
srp.renderMode = RenderMode::World;
backEnd.refdef = cmd.refdef;
backEnd.viewParms = cmd.viewParms;
RB_CreateClipPlane(clipPlane);
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
CmdBeginBarrier();
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
CmdTextureBarrier(crp.normalTexture, ResourceStates::RenderTargetBit);
CmdTextureBarrier(crp.motionVectorTexture, ResourceStates::RenderTargetBit);
CmdEndBarrier();
CmdClearDepthStencilTarget(crp.depthTexture, true, 0.0f);
CmdClearColorTarget(crp.normalTexture, vec4_zero, NULL);
CmdClearColorTarget(crp.motionVectorTexture, vec4_zero, NULL);
GeoBuffers& db = crp.dynBuffers[GetFrameIndex()];
db.BeginUpload();
SCOPED_RENDER_PASS("Pre-pass", 1.0f, 0.5f, 0.5f);
const HTexture renderTargets[] = { crp.normalTexture, crp.motionVectorTexture };
CmdBindRenderTargets(ARRAY_LEN(renderTargets), renderTargets, &crp.depthTexture);
CmdBindVertexBuffers(ARRAY_LEN(db.vertexBuffers), db.vertexBuffers, db.vertexBufferStrides, NULL);
CmdBindIndexBuffer(db.indexBuffer.buffer, IndexType::UInt32, 0);
const drawSurf_t* drawSurfs = cmd.drawSurfs;
const int surfCount = cmd.numDrawSurfs - cmd.numTranspSurfs;
const double originalTime = backEnd.refdef.floatTime;
const shader_t* shader = NULL;
const shader_t* oldShader = NULL;
int oldEntityNum = -1;
backEnd.currentEntity = &tr.worldEntity;
tess.numVertexes = 0;
tess.numIndexes = 0;
int ds;
const drawSurf_t* drawSurf;
for(ds = 0, drawSurf = drawSurfs; ds < surfCount; ++ds, ++drawSurf)
{
int entityNum;
R_DecomposeSort(drawSurf->sort, &entityNum, &shader);
Q_assert(shader != NULL);
Q_assert(shader->isOpaque);
if(shader->isSky || shader->numStages <= 0)
{
continue;
}
const bool shaderChanged = shader != oldShader;
const bool entityChanged = entityNum != oldEntityNum;
if(shaderChanged || entityChanged)
{
oldShader = shader;
oldEntityNum = entityNum;
EndBatch();
BeginBatch(shader);
tess.greyscale = drawSurf->greyscale;
}
if(entityChanged)
{
UpdateEntityData(batchDepthHack, entityNum, originalTime);
}
R_TessellateSurface(drawSurf->surface);
}
backEnd.refdef.floatTime = originalTime;
EndBatch();
db.EndUpload();
// restores the potentially "hacked" depth range as well
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
}
void Prepass::ProcessShader(shader_t& shader)
{
if(shader.isSky)
{
return;
}
Q_assert(shader.isOpaque);
if(shader.numStages < 1)
{
return;
}
const bool clampDepth = r_depthClamp->integer != 0 || shader.isSky;
const shaderStage_t& stage = *shader.stages[0];
const unsigned int stateBits = stage.stateBits & (~GLS_POLYMODE_LINE);
int a = 0;
Q_assert((stateBits & GLS_DEPTHTEST_DISABLE) == 0); // depth test enabled
Q_assert((stateBits & GLS_DEPTHMASK_TRUE) != 0); // depth write enabled
Q_assert((stateBits & GLS_DEPTHFUNC_EQUAL) == 0); // depth comparison GE
// @NOTE: we are not using any CTOR because we deliberately want to 0-init the struct
// this is necessary for padding bytes not to mess up comparisons in the PSO cache
GraphicsPipelineDesc desc = {};
desc.name = "pre-pass";
desc.rootSignature = RHI_MAKE_NULL_HANDLE();
desc.shortLifeTime = true; // the PSO cache is only valid for this map!
desc.vertexShader = g_prepass_vs;
desc.pixelShader = g_prepass_ps;
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Position, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::TexCoord, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Color, DataType::UNorm8, 4, 0);
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;
desc.depthStencil.depthComparison = ComparisonFunction::GreaterEqual;
desc.depthStencil.enableDepthTest = true;
desc.depthStencil.enableDepthWrites = true;
desc.rasterizer.cullMode = shader.cullType;
desc.rasterizer.polygonOffset = shader.polygonOffset != 0;
desc.rasterizer.clampDepth = clampDepth;
desc.AddRenderTarget(0, TextureFormat::RG32_SNorm);
desc.AddRenderTarget(0, TextureFormat::RG32_Float);
pipeline_t& p = shader.prepassPipeline;
p.firstStage = 0;
p.numStages = 1;
p.pipeline = psoCache.AddPipeline(desc, va("pre-pass %d", psoCache.entryCount));
desc.rasterizer.cullMode = GetMirrorredCullType(desc.rasterizer.cullMode);
p.mirrorPipeline = psoCache.AddPipeline(desc, va("pre-pass %d mirrored", psoCache.entryCount));
}
void Prepass::TessellationOverflow()
{
EndBatch();
BeginBatch(tess.shader);
}
void Prepass::BeginBatch(const shader_t* shader)
{
tess.tessellator = Tessellator::Prepass;
tess.numVertexes = 0;
tess.numIndexes = 0;
tess.depthFade = DFT_NONE;
tess.deformsPreApplied = qfalse;
tess.xstages = (const shaderStage_t**)shader->stages;
tess.shader = shader;
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
if(tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime)
{
tess.shaderTime = tess.shader->clampTime;
}
}
void Prepass::EndBatch()
{
PrepassVertexRC vertexRC = {};
PrepassPixelRC pixelRC = {};
float tempMatrix[16];
const int vertexCount = tess.numVertexes;
const int indexCount = tess.numIndexes;
if(vertexCount <= 0 ||
indexCount <= 0 ||
tess.shader->numStages <= 0)
{
goto clean_up;
}
const shader_t* const shader = tess.shader;
GeoBuffers& db = crp.dynBuffers[GetFrameIndex()];
if(!db.CanAdd(vertexCount, indexCount, shader->numStages))
{
Q_assert(!"World surface geometry buffer too small!");
goto clean_up;
}
RB_DeformTessGeometry(0, vertexCount, 0, indexCount);
db.UploadBase();
if(batchDepthHack != batchOldDepthHack)
{
const viewParms_t& vp = backEnd.viewParms;
CmdSetViewport(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight, batchDepthHack ? 0.7f : 0.0f, 1.0f);
batchOldDepthHack = batchDepthHack;
}
memcpy(vertexRC.modelViewMatrix, backEnd.orient.modelMatrix, sizeof(vertexRC.modelViewMatrix));
memcpy(vertexRC.projectionMatrix, backEnd.viewParms.projectionMatrix, sizeof(vertexRC.projectionMatrix));
memcpy(vertexRC.clipPlane, clipPlane, sizeof(vertexRC.clipPlane));
R_InvMatrix(backEnd.modelMatrix, tempMatrix);
R_TransposeMatrix(tempMatrix, vertexRC.normalMatrix);
CmdSetGraphicsRootConstants(0, sizeof(vertexRC), &vertexRC);
const shaderStage_t* const stage = shader->stages[0];
R_ComputeColors(stage, tess.svars[0], 0, vertexCount);
R_ComputeTexCoords(stage, tess.svars[0], 0, vertexCount, qfalse);
db.UploadStage(0);
const pipeline_t& pipeline = shader->prepassPipeline;
const int psoIndex = backEnd.viewParms.isMirror ? pipeline.mirrorPipeline : pipeline.pipeline;
Q_assert(psoIndex > 0);
CmdBindPipeline(psoCache.entries[psoIndex].handle);
const image_t* image = GetBundleImage(stage->bundle);
const uint32_t texIdx = image->textureIndex;
const uint32_t sampIdx = GetSamplerIndex(image);
const uint32_t alphaTest = AlphaTestShaderConstFromStateBits(stage->stateBits);
Q_assert(sampIdx < ARRAY_LEN(crp.samplers));
pixelRC.textureIndex = texIdx;
pixelRC.samplerIndex = sampIdx;
pixelRC.alphaTest = alphaTest;
CmdSetGraphicsRootConstants(sizeof(vertexRC), sizeof(pixelRC), &pixelRC);
db.DrawStage(vertexCount, indexCount);
db.EndBaseBatch(vertexCount);
clean_up:
tess.tessellator = Tessellator::None;
tess.numVertexes = 0;
tess.numIndexes = 0;
}

View file

@ -1,6 +1,6 @@
/*
===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum
Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).

View file

@ -1,6 +1,6 @@
/*
===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum
Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).
@ -83,35 +83,7 @@ void WorldTransp::Draw(const drawSceneViewCommand_t& cmd)
backEnd.refdef = cmd.refdef;
backEnd.viewParms = cmd.viewParms;
if(backEnd.viewParms.isPortal)
{
float plane[4];
plane[0] = backEnd.viewParms.portalPlane.normal[0];
plane[1] = backEnd.viewParms.portalPlane.normal[1];
plane[2] = backEnd.viewParms.portalPlane.normal[2];
plane[3] = backEnd.viewParms.portalPlane.dist;
float plane2[4];
plane2[0] = DotProduct(backEnd.viewParms.orient.axis[0], plane);
plane2[1] = DotProduct(backEnd.viewParms.orient.axis[1], plane);
plane2[2] = DotProduct(backEnd.viewParms.orient.axis[2], plane);
plane2[3] = DotProduct(plane, backEnd.viewParms.orient.origin) - plane[3];
float* o = plane;
const float* m = s_flipMatrix;
const float* v = plane2;
o[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
o[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
o[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
o[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
memcpy(clipPlane, plane, sizeof(clipPlane));
}
else
{
memset(clipPlane, 0, sizeof(clipPlane));
}
RB_CreateClipPlane(clipPlane);
SCOPED_RENDER_PASS("Transparent", 1.0f, 0.5f, 0.5f);
@ -222,7 +194,7 @@ void WorldTransp::ProcessShader(shader_t& shader)
desc.vertexShader = g_transp_draw_vs;
desc.pixelShader = g_transp_draw_ps;
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Position, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::TexCoord, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Color, DataType::UNorm8, 4, 0);
desc.depthStencil.depthStencilFormat = TextureFormat::Depth32_Float;

View file

@ -1,6 +1,6 @@
/*
===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum
Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).

View file

@ -552,7 +552,7 @@ uint32_t GRP::CreatePSO(CachedPSO& cache, const char* name)
desc.vertexShader = vertexShaderByteCodes[cache.stageCount - 1];
desc.pixelShader = pixelShaderByteCode;
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Position, DataType::Float32, 3, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 2, 0);
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::Normal, DataType::Float32, 3, 0);
for(int s = 0; s < cache.stageCount; ++s)
{
desc.vertexLayout.AddAttribute(a++, ShaderSemantic::TexCoord, DataType::Float32, 2, 0);

View file

@ -363,34 +363,7 @@ void World::Begin()
{
srp.renderMode = RenderMode::World;
if(backEnd.viewParms.isPortal)
{
float plane[4];
plane[0] = backEnd.viewParms.portalPlane.normal[0];
plane[1] = backEnd.viewParms.portalPlane.normal[1];
plane[2] = backEnd.viewParms.portalPlane.normal[2];
plane[3] = backEnd.viewParms.portalPlane.dist;
float plane2[4];
plane2[0] = DotProduct(backEnd.viewParms.orient.axis[0], plane);
plane2[1] = DotProduct(backEnd.viewParms.orient.axis[1], plane);
plane2[2] = DotProduct(backEnd.viewParms.orient.axis[2], plane);
plane2[3] = DotProduct(plane, backEnd.viewParms.orient.origin) - plane[3];
float* o = plane;
const float* m = s_flipMatrix;
const float* v = plane2;
o[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
o[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
o[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
o[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
memcpy(clipPlane, plane, sizeof(clipPlane));
}
else
{
memset(clipPlane, 0, sizeof(clipPlane));
}
RB_CreateClipPlane(clipPlane);
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;

View file

@ -1715,6 +1715,8 @@ namespace RHI
case TextureFormat::R8_UNorm: return DXGI_FORMAT_R8_UNORM;
case TextureFormat::R10G10B10A2_UNorm: return DXGI_FORMAT_R10G10B10A2_UNORM;
case TextureFormat::R32_UInt: return DXGI_FORMAT_R32_UINT;
case TextureFormat::RG32_SNorm: return DXGI_FORMAT_R16G16_SNORM;
case TextureFormat::RG32_Float: return DXGI_FORMAT_R16G16_FLOAT;
default: Q_assert(!"Unsupported texture format"); return DXGI_FORMAT_R8G8B8A8_UNORM;
}
}

View file

@ -170,6 +170,8 @@ namespace RHI
Depth24_Stencil8,
R10G10B10A2_UNorm,
R32_UInt,
RG32_SNorm,
RG32_Float,
Count
};
};

View file

@ -1,6 +1,6 @@
/*
===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum
Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).
@ -138,7 +138,45 @@ float EaseInQuad(float x)
return x * x;
}
float SmoothStep(float x)
float smoothstep01(float x)
{
return smoothstep(0.0, 1.0, x);
}
// Oct*: octahedron normal vector encoding
// original code from "A Survey of Efficient Representations for Independent Unit Vectors"
// further improved by Krzysztof Narkowicz and Rune Stubbe
float2 OctWrap(float2 v)
{
return (1.0 - abs(v.yx)) * (v.xy >= 0.0 ? 1.0 : -1.0);
}
float2 OctEncode(float3 n)
{
n /= (abs(n.x) + abs(n.y) + abs(n.z));
n.xy = n.z >= 0.0 ? n.xy : OctWrap(n.xy);
n.xy = n.xy * 0.5 + 0.5;
return n.xy;
}
float3 OctDecode(float2 f)
{
f = f * 2.0 - 1.0;
float3 n = float3(f.x, f.y, 1.0 - abs(f.x) - abs(f.y));
float t = saturate(-n.z);
n.xy += n.xy >= 0.0 ? -t : t;
return normalize(n);
}
float3 GetPositionFromDepth(float2 tc01, float depthZW, float4x4 invMatrix)
{
float x = tc01.x * 2.0 - 1.0;
float y = (1.0 - tc01.y) * 2.0 - 1.0;
float4 position = mul(float4(x, y, depthZW, 1.0), invMatrix);
float3 result = position.xyz / position.w;
return result;
}

View file

@ -1,6 +1,6 @@
/*
===========================================================================
Copyright (C) 2023 Gian 'myT' Schellenbaum
Copyright (C) 2023-2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).
@ -76,9 +76,7 @@ VOut vs(VIn input)
return output;
}
// @TODO: enable early-Z here once the full pre-pass is implemented
// this will prevent fragments failing the depth test from writing to the shader ID buffer
//[earlydepthstencil]
[earlydepthstencil]
float4 ps(VOut input) : SV_Target
{
// @TODO: Voronoi tiling

View file

@ -0,0 +1,98 @@
/*
===========================================================================
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 <https://www.gnu.org/licenses/>.
===========================================================================
*/
// generic shader for the prepass of opaque surfaces
#include "common.hlsli"
#include "world.h.hlsli"
#include "world.hlsli"
cbuffer RootConstants
{
// geometry
matrix modelViewMatrix;
matrix projectionMatrix;
matrix normalMatrix;
float4 clipPlane;
// general
uint textureIndex;
uint samplerIndex;
uint alphaTest;
};
struct VIn
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
};
struct VOut
{
float4 position : SV_Position;
float3 normal : NORMAL;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
float clipDist : SV_ClipDistance0;
};
VOut vs(VIn input)
{
float4 positionVS = mul(modelViewMatrix, float4(input.position.xyz, 1));
VOut output;
output.position = mul(projectionMatrix, positionVS);
output.normal = mul(normalMatrix, float4(input.normal, 0)).xyz;
output.texCoords = input.texCoords;
output.color = input.color;
output.clipDist = dot(positionVS, clipPlane);
return output;
}
struct POut
{
float2 normal : SV_Target0;
float2 motionVector : SV_Target1;
};
POut ps(VOut input)
{
if(alphaTest != ATEST_NONE)
{
Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
SamplerState sampler0 = ResourceDescriptorHeap[samplerIndex];
float4 dst = texture0.Sample(sampler0, input.texCoords) * input.color;
if(FailsAlphaTest(dst.a, alphaTest))
{
discard;
}
}
POut output;
output.normal = OctEncode(normalize(input.normal));
output.motionVector = float2(0, 0); // @TODO:
return output;
}

View file

@ -181,7 +181,7 @@ void UpdateEntityData(bool& depthHack, int entityNum, double originalTime)
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
// set up the transformation matrix
R_RotateForEntity(backEnd.currentEntity, &backEnd.viewParms, &backEnd.orient);
R_RotateForEntity(backEnd.currentEntity, &backEnd.viewParms, &backEnd.orient, backEnd.modelMatrix);
if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK)
{
@ -193,6 +193,7 @@ void UpdateEntityData(bool& depthHack, int entityNum, double originalTime)
backEnd.currentEntity = &tr.worldEntity;
backEnd.refdef.floatTime = originalTime;
backEnd.orient = backEnd.viewParms.world;
R_MakeIdentityMatrix(backEnd.modelMatrix);
// we have to reset the shaderTime as well otherwise image animations on
// the world (like water) continue with the wrong frame
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;

View file

@ -64,8 +64,7 @@ void RB_PopShader()
// used when a player has predicted a teleport, but hasn't arrived yet
float RB_HyperspaceColor()
{
if ( r_teleporterFlash->integer == 0 )
{
if (r_teleporterFlash->integer == 0) {
return 0.0f;
}
@ -73,3 +72,33 @@ float RB_HyperspaceColor()
return c;
}
void RB_CreateClipPlane( float* clipPlane )
{
if (backEnd.viewParms.isPortal) {
float plane[4];
plane[0] = backEnd.viewParms.portalPlane.normal[0];
plane[1] = backEnd.viewParms.portalPlane.normal[1];
plane[2] = backEnd.viewParms.portalPlane.normal[2];
plane[3] = backEnd.viewParms.portalPlane.dist;
float plane2[4];
plane2[0] = DotProduct(backEnd.viewParms.orient.axis[0], plane);
plane2[1] = DotProduct(backEnd.viewParms.orient.axis[1], plane);
plane2[2] = DotProduct(backEnd.viewParms.orient.axis[2], plane);
plane2[3] = DotProduct(plane, backEnd.viewParms.orient.origin) - plane[3];
float* o = plane;
const float* m = s_flipMatrix;
const float* v = plane2;
o[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
o[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
o[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
o[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
memcpy(clipPlane, plane, 4 * sizeof(float));
} else {
memset(clipPlane, 0, 4 * sizeof(float));
}
}

View file

@ -435,6 +435,8 @@ struct shader_t {
pipeline_t transpPipelines[MAX_SHADER_STAGES];
int numTranspPipelines;
pipeline_t prepassPipeline;
shader_t* next;
};
@ -857,6 +859,7 @@ typedef struct {
viewParms_t viewParms;
orientationr_t orient;
trRefEntity_t* currentEntity;
float modelMatrix[16]; // real model matrix, not model-view
qbool projection2D; // if qtrue, drawstretchpic doesn't need to change modes
byte color2D[4];
@ -1121,7 +1124,7 @@ int R_CullLocalBox( const vec3_t bounds[2] );
int R_CullPointAndRadius( const vec3_t origin, float radius );
int R_CullLocalPointAndRadius( const vec3_t origin, float radius );
void R_RotateForEntity( const trRefEntity_t* ent, const viewParms_t* viewParms, orientationr_t* orient );
void R_RotateForEntity( const trRefEntity_t* ent, const viewParms_t* viewParms, orientationr_t* orient, float* modelMatrix = NULL );
void R_CreateWorldModelMatrix( const vec3_t origin, const vec3_t axis[3], float* viewMatrix );
typedef void (*updateAnimatedImage_t)( image_t* image, int w, int h, const byte* data, qbool dirty );
@ -1572,8 +1575,8 @@ void RB_TakeVideoFrameCmd( const videoFrameCommand_t* cmd );
void RB_PushSingleStageShader( int stateBits, cullType_t cullType );
void RB_PopShader();
float RB_HyperspaceColor();
void RB_CreateClipPlane( float* clipPlane );
void RB_DrawSky();
void R_BuildCloudData();

View file

@ -493,7 +493,7 @@ Does NOT produce any GL calls
Called by both the front end and the back end
=================
*/
void R_RotateForEntity( const trRefEntity_t* ent, const viewParms_t* viewParms, orientationr_t* orient )
void R_RotateForEntity( const trRefEntity_t* ent, const viewParms_t* viewParms, orientationr_t* orient, float* modelMatrix )
{
float glMatrix[16];
vec3_t delta;
@ -530,6 +530,10 @@ void R_RotateForEntity( const trRefEntity_t* ent, const viewParms_t* viewParms,
glMatrix[11] = 0;
glMatrix[15] = 1;
if ( modelMatrix != NULL ) {
Com_Memcpy( modelMatrix, glMatrix, sizeof( glMatrix ) );
}
R_MultMatrix( glMatrix, viewParms->world.modelMatrix, orient->modelMatrix );
// calculate the viewer origin in the model's space

View file

@ -388,6 +388,7 @@ void ProcessCRP()
CompileCompute("mip_1.h", "mip_1.hlsl", "mip_1");
CompileCompute("mip_2.h", "mip_2.hlsl", "mip_2");
CompileCompute("mip_3.h", "mip_3.hlsl", "mip_3");
CompileGraphics("prepass.h", "prepass.hlsl", "prepass");
CompileGraphics("opaque.h", "opaque.hlsl", "opaque");
CompileGraphics("transp_draw.h", "transp_draw.hlsl", "transp_draw");
CompilePixelShader("transp_resolve.h", "transp_resolve.hlsl", "transp_resolve");

View file

@ -134,6 +134,7 @@
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
<ClCompile Include="..\..\code\renderer\crp_tone_map.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_draw.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_resolve.cpp" />
@ -236,6 +237,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>

View file

@ -38,6 +38,7 @@
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
<ClCompile Include="..\..\code\renderer\crp_tone_map.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_draw.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_resolve.cpp" />
@ -140,6 +141,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>

View file

@ -136,6 +136,7 @@
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
<ClCompile Include="..\..\code\renderer\crp_tone_map.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_draw.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_resolve.cpp" />
@ -238,6 +239,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>

View file

@ -38,6 +38,7 @@
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
<ClCompile Include="..\..\code\renderer\crp_tone_map.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_draw.cpp" />
<ClCompile Include="..\..\code\renderer\crp_transp_resolve.cpp" />
@ -140,6 +141,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\opaque.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>