added motion blur and freeze frame commands

- fixed crash due to missing blue noise texture
- added motion vector viz
- renamed TextureFormat entries
This commit is contained in:
myT 2024-02-18 16:26:05 +01:00
parent c592ef22d6
commit 838c9a6485
39 changed files with 1707 additions and 134 deletions

View file

@ -103,10 +103,21 @@ add: Cinematic Rendering Pipeline CVars
0 - disabled
1 - 1/4 pixel count, 9 samples total
2 - 1/16 pixel count, 25 samples total
motion blur:
crp_mblur <0 to 3> (default: 0) sets the motion blur mode
0 - disabled
1 - camera blur only
2 - object blur only
3 - camera and object blur
crp_mblur_exposure <0.0 to 1.0> (default: 0.5) is the exposure time in percentage of frame time
miscellaneous:
crp_drawNormals <0|1> (default: 0) draws vertex normals as colorized wireframe lines
crp_updateRTAS <0|1> (default: 1) enables raytracing acceleration structure builds every frame
add: Cinematic Rendering Pipeline commands
cin_freezeFrame freezes the rendered frame (minus ImGUI and tone map) and G-Buffer for inspection
cin_freezeFrameMB freezes the rendered frame (minus ImGUI, tone map and motion blur)
fix: allocating enough memory for 4K screenshots and video captures
fix: the new demo player now drops zombie snapshots and snapshots from before a server time rewind

View file

@ -361,6 +361,7 @@ static qbool CL_CG_GetValue( char* value, int valueSize, const char* key )
{ "trap_CNQ3_NK_Upload", CG_EXT_NK_UPLOAD },
{ "trap_CNQ3_NK_Draw", CG_EXT_NK_DRAW },
{ "trap_CNQ3_SetCharEvents", CG_EXT_SETCHAREVENTS },
{ "trap_CNQ3_R_AddRefEntityToScene", CG_EXT_CNQ3_R_ADDREFENTITYTOSCENE },
// commands
{ "screenshotnc", 1 },
{ "screenshotncJPEG", 1 },
@ -525,7 +526,7 @@ static intptr_t CL_CgameSystemCalls( intptr_t *args )
re.ClearScene();
return 0;
case CG_R_ADDREFENTITYTOSCENE:
re.AddRefEntityToScene( VMA(1), qfalse );
re.AddRefEntityToScene( VMA(1), REV_ORIGINAL );
return 0;
case CG_R_ADDPOLYTOSCENE:
re.AddPolyToScene( args[1], args[2], VMA(3), 1 );
@ -648,7 +649,7 @@ static intptr_t CL_CgameSystemCalls( intptr_t *args )
return 0;
case CG_EXT_R_ADDREFENTITYTOSCENE2:
re.AddRefEntityToScene( VMA(1), qtrue );
re.AddRefEntityToScene( VMA(1), REV_INTSHADERTIME );
return 0;
case CG_EXT_R_FORCEFIXEDDLIGHTS:
@ -728,6 +729,10 @@ static intptr_t CL_CgameSystemCalls( intptr_t *args )
cls.cgameCharEvents = (qbool)args[1];
return 0;
case CG_EXT_CNQ3_R_ADDREFENTITYTOSCENE:
re.AddRefEntityToScene( VMA(1), REV_MOTIONBLUR );
return 0;
default:
Com_Error( ERR_DROP, "Bad cgame system trap: %i", args[0] );
}

View file

@ -885,7 +885,7 @@ static intptr_t CL_UISystemCalls( intptr_t* args )
return 0;
case UI_R_ADDREFENTITYTOSCENE:
re.AddRefEntityToScene( VMA(1), qfalse );
re.AddRefEntityToScene( VMA(1), REV_ORIGINAL );
return 0;
case UI_R_ADDPOLYTOSCENE:
@ -1127,7 +1127,7 @@ static intptr_t CL_UISystemCalls( intptr_t* args )
return 0;
case UI_EXT_R_ADDREFENTITYTOSCENE2:
re.AddRefEntityToScene( VMA(1), qtrue );
re.AddRefEntityToScene( VMA(1), REV_INTSHADERTIME );
return 0;
case UI_EXT_CVAR_SETRANGE:

View file

@ -206,7 +206,8 @@ typedef enum {
CG_EXT_NK_CREATEFONTATLAS,
CG_EXT_NK_UPLOAD,
CG_EXT_NK_DRAW,
CG_EXT_SETCHAREVENTS
CG_EXT_SETCHAREVENTS,
CG_EXT_CNQ3_R_ADDREFENTITYTOSCENE
} cgameImport_t;

View file

@ -40,8 +40,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// animation without needing to know the frame count
// refdef flags
#define RDF_NOWORLDMODEL 1 // used for player configuration screen
#define RDF_HYPERSPACE 4 // teleportation effect
#define RDF_NOWORLDMODEL 1 // used for player configuration screen
#define RDF_HYPERSPACE 4 // teleportation effect
#define RDF_CNQ3_TELEPORTED 8 // the camera teleported since the last submitted scene
typedef struct {
vec3_t xyz;
@ -79,7 +80,7 @@ typedef struct {
float shadowPlane; // projection shadows go here, stencils go slightly lower
vec3_t axis[3]; // rotation vectors
qbool nonNormalizedAxes; // axis are not normalized, i.e. they have scale
qbool nonNormalizedAxes; // axis are not normalized, i.e. they have scale
float origin[3]; // also used as MODEL_BEAM's "from"
int frame; // also used as MODEL_BEAM's diameter
@ -97,15 +98,64 @@ typedef struct {
byte shaderRGBA[4]; // colors used by rgbgen entity shaders
float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers
union {
float fShaderTime; // subtracted from refdef time to control effect start times
int iShaderTime; // qvm float have 23bit mantissa. After 8388607ms (02:20) we lost data.
float fShaderTime; // subtracted from refdef time to control effect start times
int iShaderTime; // qvm float have 23bit mantissa. After 8388607ms (02:20) we lost data.
} shaderTime;
// extra sprite information
float radius;
float rotation;
} refEntityBase_t;
typedef struct {
refEntityType_t reType;
int renderfx;
qhandle_t hModel; // opaque type outside refresh
// most recent data
vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN)
float shadowPlane; // projection shadows go here, stencils go slightly lower
vec3_t axis[3]; // rotation vectors
qbool nonNormalizedAxes; // axis are not normalized, i.e. they have scale
float origin[3]; // also used as MODEL_BEAM's "from"
int frame; // also used as MODEL_BEAM's diameter
// previous data for frame interpolation
float oldorigin[3]; // also used as MODEL_BEAM's "to"
int oldframe;
float backlerp; // 0.0 = current, 1.0 = old
// texturing
int skinNum; // inline skin index
qhandle_t customSkin; // NULL for default skin
qhandle_t customShader; // use one image for the entire thing
// misc
byte shaderRGBA[4]; // colors used by rgbgen entity shaders
float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers
union {
float fShaderTime; // subtracted from refdef time to control effect start times
int iShaderTime; // qvm float have 23bit mantissa. After 8388607ms (02:20) we lost data.
} shaderTime;
// extra sprite information
float radius;
float rotation;
// CNQ3 extensions
int uniqueId; // for tracking refresh entities across frames, 0 is untracked
float motionBlurScale; // 1.0f by default but can tweak for e.g. first-person weapon
} refEntity_t;
typedef enum {
REV_ORIGINAL,
REV_INTSHADERTIME,
REV_MOTIONBLUR,
REV_LATEST = REV_MOTIONBLUR
} refEntityVersion_t;
#define MAX_RENDER_STRINGS 8
#define MAX_RENDER_STRING_LENGTH 32

View file

@ -138,10 +138,10 @@ struct DOFCombineRC
void GatherDepthOfField::Init()
{
const TextureFormat::Id renderTargetFormat = TextureFormat::RGBA64_Float;
const TextureFormat::Id renderTargetFormat = TextureFormat::R16G16B16A16_Float;
tileWidth = (uint32_t)(glConfig.vidWidth + 15) / 16;
tileHeight = (uint32_t)(glConfig.vidHeight + 15) / 16;
tileTextureWidth = (uint32_t)(glConfig.vidWidth + 15) / 16;
tileTextureHeight = (uint32_t)(glConfig.vidHeight + 15) / 16;
{
ComputePipelineDesc desc("DOF split");
@ -223,8 +223,8 @@ void GatherDepthOfField::Init()
desc.name = "DOF far field CoC";
farCocTexture = CreateTexture(desc);
desc.width = tileWidth;
desc.height = tileHeight;
desc.width = tileTextureWidth;
desc.height = tileTextureHeight;
desc.name = "DOF near field CoC tile #1";
nearCocTileTexture = CreateTexture(desc);
desc.name = "DOF near field CoC tile #2";
@ -334,7 +334,7 @@ void GatherDepthOfField::DrawNearCocTileGen()
CmdBindPipeline(nearCocTileGenPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((tileWidth + 7) / 8, (tileHeight + 7) / 8, 1);
CmdDispatch((tileTextureWidth + 7) / 8, (tileTextureHeight + 7) / 8, 1);
}
void GatherDepthOfField::DrawNearCocTileMax()
@ -353,7 +353,7 @@ void GatherDepthOfField::DrawNearCocTileMax()
CmdBindPipeline(nearCocTileMaxPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((tileWidth + 7) / 8, (tileHeight + 7) / 8, 1);
CmdDispatch((tileTextureWidth + 7) / 8, (tileTextureHeight + 7) / 8, 1);
}
void GatherDepthOfField::DrawBlur()

View file

@ -27,6 +27,7 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
#include "compshaders/crp/gbufferviz_depth.h"
#include "compshaders/crp/gbufferviz_normal.h"
#include "compshaders/crp/gbufferviz_position.h"
#include "compshaders/crp/gbufferviz_motion.h"
#pragma pack(push, 4)
@ -50,6 +51,11 @@ struct DecodePositionRC
uint32_t coloredDelta;
};
struct MotionVectorRC
{
uint32_t textureIndex;
};
#pragma pack(pop)
@ -87,6 +93,17 @@ void GBufferViz::Init()
desc.AddRenderTarget(0, crp.renderTargetFormat);
decodeShadingPositionPipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("G-Buffer Motion");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(g_fullscreen_vs);
desc.pixelShader = ShaderByteCode(g_gbufferviz_motion_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, crp.renderTargetFormat);
motionVectorPipeline = CreateGraphicsPipeline(desc);
}
}
void GBufferViz::DrawGUI()
@ -166,14 +183,47 @@ void GBufferViz::DrawGUI()
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}
else if(textureIndex == GBufferTexture::MotionVectorRaw ||
textureIndex == GBufferTexture::MotionVectorMB)
{
srp.renderMode = RenderMode::None;
const HTexture texture =
textureIndex == GBufferTexture::MotionVectorRaw ?
crp.motionVectorTexture :
crp.motionVectorMBTexture;
SCOPED_RENDER_PASS("Debug Motion", 1.0f, 1.0f, 1.0f);
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
CmdBeginBarrier();
CmdTextureBarrier(texture, ResourceStates::PixelShaderAccessBit);
CmdTextureBarrier(renderTarget, ResourceStates::RenderTargetBit);
CmdEndBarrier();
MotionVectorRC rc = {};
rc.textureIndex = GetTextureIndexSRV(texture);
CmdBindRenderTargets(1, &renderTarget, NULL);
CmdBindPipeline(motionVectorPipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}
if(ImGui::Begin("G-Buffer", &windowActive, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Checkbox("Full-res", &fullResolution);
ImGui::SameLine();
ImGui::RadioButton("Depth", &textureIndex, GBufferTexture::Depth);
ImGui::SameLine();
ImGui::RadioButton("Normal", &textureIndex, GBufferTexture::Normal);
ImGui::SameLine();
ImGui::RadioButton("Light", &textureIndex, GBufferTexture::Light);
ImGui::RadioButton("Motion Raw", &textureIndex, GBufferTexture::MotionVectorRaw);
ImGui::SameLine();
ImGui::RadioButton("Motion MB", &textureIndex, GBufferTexture::MotionVectorMB);
ImGui::SameLine();
ImGui::RadioButton("Shading Position Delta", &textureIndex, GBufferTexture::ShadingPositionDelta);
if(textureIndex == GBufferTexture::ShadingPositionDelta)
@ -182,7 +232,13 @@ void GBufferViz::DrawGUI()
ImGui::Checkbox("Per-axis delta", &coloredPositionDelta);
}
const ImVec2 resolution = ImVec2(glConfig.vidWidth / 2, glConfig.vidHeight / 2);
ImVec2 resolution = ImVec2(glConfig.vidWidth, glConfig.vidHeight);
if(!fullResolution)
{
resolution.x /= 2;
resolution.y /= 2;
}
HTexture texture;
switch(textureIndex)
{
@ -190,6 +246,8 @@ void GBufferViz::DrawGUI()
case GBufferTexture::Normal: texture = renderTarget; break;
case GBufferTexture::Light: texture = crp.lightTexture; break;
case GBufferTexture::ShadingPositionDelta: texture = renderTarget; break;
case GBufferTexture::MotionVectorRaw: texture = renderTarget; break;
case GBufferTexture::MotionVectorMB: texture = renderTarget; break;
default: Q_assert(!"Invalid G-Buffer texture index"); texture = crp.lightTexture; break;
}
ImGui::Image((ImTextureID)GetTextureIndexSRV(texture), resolution);

View file

@ -40,6 +40,8 @@ extern cvar_t* crp_accumDof_focusDist;
extern cvar_t* crp_accumDof_radius;
extern cvar_t* crp_accumDof_samples;
extern cvar_t* crp_accumDof_preview;
extern cvar_t* crp_mblur;
extern cvar_t* crp_mblur_exposure;
extern cvar_t* crp_drawNormals;
extern cvar_t* crp_updateRTAS;
extern cvar_t* crp_debug0;
@ -58,6 +60,15 @@ struct DOFMethod
};
};
struct MotionBlurModes
{
enum Flags
{
CameraBit = 1 << 0,
ObjectBit = 1 << 1
};
};
struct Tessellator
{
enum Id
@ -111,6 +122,10 @@ private:
bool batchOldDepthHack;
bool batchDepthHack;
int batchEntityId;
float batchMotionScale;
HPipeline skyboxMotionPipeline;
};
struct WorldOpaque
@ -237,8 +252,30 @@ private:
HTexture nearCocTileTexture;
HTexture nearCocTileTexture2;
HTexture farCocTexture;
uint32_t tileWidth;
uint32_t tileHeight;
uint32_t tileTextureWidth;
uint32_t tileTextureHeight;
};
struct MotionBlur
{
void Init();
void Draw();
private:
void DrawPack();
void DrawTileGen();
void DrawTileMax();
void DrawBlur();
HPipeline packPipeline;
HPipeline tileGenPipeline;
HPipeline tileMaxPipeline;
HPipeline blurPipeline;
HTexture tileTexture;
HTexture tileTexture2;
HTexture packedTexture;
uint32_t tileTextureWidth;
uint32_t tileTextureHeight;
};
struct Magnifier
@ -266,6 +303,8 @@ private:
Normal,
Light,
ShadingPositionDelta,
MotionVectorRaw,
MotionVectorMB,
Count
};
};
@ -273,9 +312,11 @@ private:
HPipeline linearizeDepthPipeline;
HPipeline decodeNormalsPipeline;
HPipeline decodeShadingPositionPipeline;
HPipeline motionVectorPipeline;
bool windowActive = false;
int textureIndex = 0;
bool coloredPositionDelta = false;
bool fullResolution = false;
};
struct DynamicLights
@ -405,6 +446,18 @@ struct GeoBuffers
uint32_t vertexBufferStrides[BaseBufferId::Count + StageBufferId::Count];
};
struct FreezeFrame
{
enum Id
{
Inactive,
Pending,
Active,
PendingBeforeMB,
ActiveBeforeMB
};
};
struct CRP : IRenderPipeline
{
void Init() override;
@ -445,7 +498,8 @@ struct CRP : IRenderPipeline
HTexture readbackRenderTarget;
HTexture depthTexture;
HTexture normalTexture;
HTexture motionVectorTexture;
HTexture motionVectorTexture; // raw, for TAA/denoisers/etc
HTexture motionVectorMBTexture; // mangled, for motion blur only
HTexture noisyLightTexture;
HTexture lightTexture;
HTexture shadingPositionTexture;
@ -456,6 +510,8 @@ struct CRP : IRenderPipeline
HSampler samplers[BASE_SAMPLER_COUNT]; // all base samplers
uint32_t samplerIndices[BASE_SAMPLER_COUNT]; // descriptor heap indices
HTexture blueNoise2D;
FreezeFrame::Id freezeFrame;
HTexture frozenTexture;
// blit
HPipeline blitPipelineLDR;
@ -486,6 +542,7 @@ struct CRP : IRenderPipeline
ToneMap toneMap;
GatherDepthOfField gatherDof;
AccumDepthOfField accumDof;
MotionBlur motionBlur;
Fog fog;
Magnifier magnifier;
DynamicLights dynamicLights;

View file

@ -64,6 +64,8 @@ cvar_t* crp_accumDof_focusDist;
cvar_t* crp_accumDof_radius;
cvar_t* crp_accumDof_samples;
cvar_t* crp_accumDof_preview;
cvar_t* crp_mblur;
cvar_t* crp_mblur_exposure;
cvar_t* crp_drawNormals;
cvar_t* crp_updateRTAS;
cvar_t* crp_debug0;
@ -150,6 +152,25 @@ static const cvarTableItem_t crp_cvars[] =
&crp_gatherDof_brightness, "crp_gatherDof_brightness", "2", CVAR_ARCHIVE, CVART_FLOAT, "0", "8", "blur brightness weight",
"Gather DoF bokeh brightness", CVARCAT_GRAPHICS, "Blur brightness weight", ""
},
{
&crp_mblur, "crp_mblur", "0", CVAR_ARCHIVE, CVART_INTEGER, "0", "3",
"motion blur mode\n"
S_COLOR_VAL " 0 " S_COLOR_HELP "= Disabled\n"
S_COLOR_VAL " 1 " S_COLOR_HELP "= Camera only\n"
S_COLOR_VAL " 2 " S_COLOR_HELP "= Object only\n"
S_COLOR_VAL " 3 " S_COLOR_HELP "= Camera + Object",
"Motion blur mode", CVARCAT_GRAPHICS, "", "",
CVAR_GUI_VALUE("0", "Disabled", "")
CVAR_GUI_VALUE("1", "Camera only", "")
CVAR_GUI_VALUE("2", "Object only", "")
CVAR_GUI_VALUE("3", "Camera + Object", "")
},
{
&crp_mblur_exposure, "crp_mblur_exposure", "0.5", CVAR_ARCHIVE, CVART_FLOAT, "0", "1",
"motion blur scale\n"
"This is the exposure time in percentage of frame time.",
"Motion blur exposure", CVARCAT_GRAPHICS, "Exposure time in percentage of frame time", ""
},
{
&crp_drawNormals, "crp_drawNormals", "0", CVAR_TEMP, CVART_BOOL, NULL, NULL, "draws vertex normals",
"Draw vertex normals", CVARCAT_GRAPHICS | CVARCAT_DEBUGGING, "", ""
@ -179,6 +200,52 @@ static const cvarTableItem_t crp_cvars[] =
#endif
};
static void FreezeFrame_f();
static void FreezeFrameMB_f();
static const cmdTableItem_t crp_cmds[] =
{
{ "cin_freezeFrame", FreezeFrame_f, NULL, "toggles freeze frame mode" },
{ "cin_freezeFrameMB", FreezeFrameMB_f, NULL, "toggles freeze frame mode for MB" }
};
static void FreezeFrame_f()
{
if(crp.freezeFrame == FreezeFrame::Active ||
crp.freezeFrame == FreezeFrame::ActiveBeforeMB)
{
crp.freezeFrame = FreezeFrame::Inactive;
}
else
{
crp.freezeFrame = FreezeFrame::Pending;
}
}
static void FreezeFrameMB_f()
{
if(crp.freezeFrame == FreezeFrame::Active ||
crp.freezeFrame == FreezeFrame::ActiveBeforeMB)
{
crp.freezeFrame = FreezeFrame::Inactive;
}
else
{
crp.freezeFrame = FreezeFrame::PendingBeforeMB;
}
}
static HTexture LoadTexture(const char* name, int flags, textureWrap_t glWrapClampMode, const image_t* onFail = tr.defaultImage)
{
image_t* const image = R_FindImageFile(name, flags, glWrapClampMode);
if(image == NULL)
{
return onFail->texture;
}
return image->texture;
}
void PSOCache::Init(Entry* entries_, uint32_t maxEntryCount_)
{
@ -215,12 +282,8 @@ int PSOCache::AddPipeline(const GraphicsPipelineDesc& desc, const char* name)
void CRP::Init()
{
static bool veryFirstInit = true;
if(veryFirstInit)
{
ri.Cvar_RegisterTable(crp_cvars, ARRAY_LEN(crp_cvars));
veryFirstInit = false;
}
ri.Cvar_RegisterTable(crp_cvars, ARRAY_LEN(crp_cvars));
ri.Cmd_RegisterTable(crp_cmds, ARRAY_LEN(crp_cmds));
InitDesc initDesc;
initDesc.directDescriptorHeapIndexing = true;
@ -262,13 +325,12 @@ void CRP::Init()
}
{
renderTargetFormat = TextureFormat::RGBA64_Float;
renderTargetFormat = TextureFormat::R16G16B16A16_Float;
TextureDesc desc("render target #1", glConfig.vidWidth, glConfig.vidHeight);
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit;
Vector4Clear(desc.clearColor);
desc.usePreferredClearValue = true;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit | ResourceStates::ComputeShaderAccessBit;
desc.SetClearColor(vec4_zero);
desc.committedResource = true;
desc.format = renderTargetFormat;
desc.shortLifeTime = true;
@ -279,6 +341,17 @@ void CRP::Init()
renderTarget = renderTargets[0];
}
{
TextureDesc desc("frozen frame", glConfig.vidWidth, glConfig.vidHeight);
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit | ResourceStates::ComputeShaderAccessBit;
desc.format = renderTargetFormat;
desc.shortLifeTime = true;
frozenTexture = RHI::CreateTexture(desc);
freezeFrame = FreezeFrame::Inactive;
}
{
TextureDesc desc("readback render target", glConfig.vidWidth, glConfig.vidHeight);
desc.initialState = ResourceStates::RenderTargetBit;
@ -286,7 +359,7 @@ void CRP::Init()
Vector4Clear(desc.clearColor);
desc.usePreferredClearValue = true;
desc.committedResource = true;
desc.format = TextureFormat::RGBA32_UNorm;
desc.format = TextureFormat::R8G8B8A8_UNorm;
desc.shortLifeTime = true;
readbackRenderTarget = RHI::CreateTexture(desc);
}
@ -360,29 +433,40 @@ void CRP::Init()
desc.shortLifeTime = true;
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit | ResourceStates::ComputeShaderAccessBit;
desc.format = TextureFormat::RG32_SNorm;
desc.format = TextureFormat::R16G16_SNorm;
desc.SetClearColor(vec4_zero);
normalTexture = RHI::CreateTexture(desc);
}
{
TextureDesc desc("GBuffer motion vectors", glConfig.vidWidth, glConfig.vidHeight);
TextureDesc desc("GBuffer raw 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.format = TextureFormat::R16G16_Float;
desc.SetClearColor(vec4_zero);
motionVectorTexture = RHI::CreateTexture(desc);
}
{
TextureDesc desc("GBuffer MB 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::R16G16_Float;
desc.SetClearColor(vec4_zero);
motionVectorMBTexture = RHI::CreateTexture(desc);
}
{
TextureDesc desc("GBuffer direct light", 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::RGBA64_Float;
desc.format = TextureFormat::R16G16B16A16_Float;
desc.SetClearColor(colorBlack);
lightTexture = RHI::CreateTexture(desc);
desc.name = "GBuffer raw direct light";
@ -395,7 +479,7 @@ void CRP::Init()
desc.shortLifeTime = true;
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit | ResourceStates::ComputeShaderAccessBit;
desc.format = TextureFormat::RGBA128_Float;
desc.format = TextureFormat::R32G32B32A32_Float;
desc.SetClearColor(vec4_zero);
shadingPositionTexture = RHI::CreateTexture(desc);
}
@ -407,10 +491,10 @@ void CRP::Init()
desc.pixelShader = ShaderByteCode(g_blit_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, TextureFormat::RGBA32_UNorm);
desc.AddRenderTarget(0, TextureFormat::R8G8B8A8_UNorm);
blitPipelineLDR = CreateGraphicsPipeline(desc);
desc.name = "blit HDR";
desc.renderTargets[0].format = TextureFormat::RGBA64_Float;
desc.renderTargets[0].format = TextureFormat::R16G16B16A16_Float;
blitPipelineHDR = CreateGraphicsPipeline(desc);
}
@ -443,6 +527,7 @@ void CRP::Init()
toneMap.Init();
gatherDof.Init();
accumDof.Init();
motionBlur.Init();
fog.Init();
magnifier.Init();
dynamicLights.Init();
@ -455,7 +540,7 @@ void CRP::Init()
void CRP::LoadResources()
{
const int flags = IMG_NOPICMIP | IMG_NOMIPMAP | IMG_NOIMANIP | IMG_NOAF;
blueNoise2D = R_FindImageFile("textures/stbn_2d.tga", flags, TW_REPEAT)->texture;
blueNoise2D = LoadTexture("textures/stbn_2d.tga", flags, TW_REPEAT);
}
void CRP::ShutDown(bool fullShutDown)
@ -495,6 +580,22 @@ void CRP::BeginFrame()
void CRP::EndFrame()
{
if(freezeFrame == FreezeFrame::Pending)
{
crp.SwapRenderTargets();
CmdBeginBarrier();
CmdTextureBarrier(GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit);
CmdTextureBarrier(frozenTexture, ResourceStates::RenderTargetBit);
CmdEndBarrier();
Blit(frozenTexture, GetReadRenderTarget(), "Blit Frozen Frame", true, vec2_one, vec2_zero);
CmdBeginBarrier();
CmdTextureBarrier(frozenTexture, ResourceStates::PixelShaderAccessBit);
CmdEndBarrier();
srp.EndFrame();
freezeFrame = FreezeFrame::Active;
return;
}
srp.DrawGUI();
gbufferViz.DrawGUI();
magnifier.DrawGUI();
@ -605,7 +706,21 @@ void CRP::ProcessShader(shader_t& shader)
void CRP::ExecuteRenderCommands(const byte* data, bool /*readbackRequested*/)
{
// @NOTE: the CRP always blits the final result to the readback texture
// @NOTE: readbackRequested is unused because
// the CRP always blits the final result to the readback texture
if(freezeFrame == FreezeFrame::Active ||
freezeFrame == FreezeFrame::ActiveBeforeMB)
{
BeginFrame();
Blit(GetWriteRenderTarget(), frozenTexture, "Blit Frozen Frame", true, vec2_one, vec2_zero);
if(freezeFrame == FreezeFrame::ActiveBeforeMB)
{
motionBlur.Draw();
}
EndFrame();
return;
}
for(;;)
{
@ -768,6 +883,21 @@ void CRP::DrawSceneView(const drawSceneViewCommand_t& cmd)
transpResolve.Draw(cmd);
CmdSetViewportAndScissor(vp.viewportX, vp.viewportY, vp.viewportWidth, vp.viewportHeight);
gatherDof.Draw();
if(freezeFrame == FreezeFrame::PendingBeforeMB &&
IsViewportFullscreen(backEnd.viewParms))
{
crp.SwapRenderTargets();
CmdBeginBarrier();
CmdTextureBarrier(GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit);
CmdTextureBarrier(frozenTexture, ResourceStates::RenderTargetBit);
CmdEndBarrier();
Blit(frozenTexture, GetReadRenderTarget(), "Blit Frozen Frame", true, vec2_one, vec2_zero);
CmdBeginBarrier();
CmdTextureBarrier(frozenTexture, ResourceStates::PixelShaderAccessBit);
CmdEndBarrier();
freezeFrame = FreezeFrame::ActiveBeforeMB;
}
motionBlur.Draw();
}
}
@ -796,6 +926,11 @@ void CRP::UploadSceneViewData()
R_InvMatrix(dest.projectionMatrix, dest.invProjectionMatrix);
memcpy(dest.viewMatrix, vp.world.modelMatrix, sizeof(dest.viewMatrix));
R_InvMatrix(dest.viewMatrix, dest.invViewMatrix);
memcpy(dest.prevViewProjMatrix, tr.prevViewProjMatrix, sizeof(dest.prevViewProjMatrix));
memcpy(dest.prevViewMatrix, tr.prevViewMatrix, sizeof(dest.prevViewMatrix));
memcpy(dest.prevProjectionMatrix, tr.prevProjMatrix, sizeof(dest.prevProjectionMatrix));
RB_CreateClipPlane(dest.clipPlane);
#if defined(_DEBUG)
dest.debug[0] = crp_debug0->value;
@ -814,10 +949,15 @@ void CRP::UploadSceneViewData()
dest.depthTextureIndex = GetTextureIndexSRV(depthTexture);
dest.normalTextureIndex = GetTextureIndexSRV(normalTexture);
dest.shadingPositionTextureIndex = GetTextureIndexSRV(shadingPositionTexture);
dest.motionVectorTextureIndex = GetTextureIndexSRV(motionVectorTexture);
dest.motionVectorMBTextureIndex = GetTextureIndexSRV(motionVectorMBTexture);
dest.lightTextureIndex = GetTextureIndexSRV(lightTexture);
dest.tlasBufferIndex = IsNullHandle(tlasBuffer) ? 0 : GetBufferIndexSRV(tlasBuffer);
dest.tlasInstanceBufferIndex = IsNullHandle(tlasInstanceBuffer) ? 0 : GetBufferIndexSRV(tlasInstanceBuffer);
dest.lightCount = refdef.num_dlights;
RB_LinearDepthConstants(&dest.linearDepthA, &dest.linearDepthB);
dest.zNear = vp.zNear;
dest.zFar = vp.zFar;
for(int i = 0; i < refdef.num_dlights; i++)
{

View file

@ -0,0 +1,227 @@
/*
===========================================================================
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 - scatter-as-gather motion blur
#include "crp_local.h"
#include "compshaders/crp/fullscreen.h"
#include "compshaders/crp/mblur_pack.h"
#include "compshaders/crp/mblur_tile_gen.h"
#include "compshaders/crp/mblur_tile_max.h"
#include "compshaders/crp/mblur_blur.h"
#pragma pack(push, 4)
struct MBPackRC
{
float motionScale;
float maxRadiusPx;
};
struct MBTileGenRC
{
uint32_t inputTextureIndex;
uint32_t outputTextureIndex;
};
struct MBTileMaxRC
{
uint32_t inputTextureIndex;
uint32_t outputTextureIndex;
uint32_t samplerIndex; // point/clamp
};
struct MBBlurRC
{
uint32_t colorTextureIndex;
uint32_t tileTextureIndex;
uint32_t packedTextureIndex;
uint32_t blueNoiseTextureIndex;
uint32_t pointSamplerIndex; // clamp
uint32_t linearSamplerIndex; // clamp
};
#pragma pack(pop)
void MotionBlur::Init()
{
tileTextureWidth = (uint32_t)(glConfig.vidWidth + 15) / 16;
tileTextureHeight = (uint32_t)(glConfig.vidHeight + 15) / 16;
{
GraphicsPipelineDesc desc("MBlur velocity/depth packing");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(g_fullscreen_vs);
desc.pixelShader = ShaderByteCode(g_pack_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, TextureFormat::R32G32_UInt);
packPipeline = CreateGraphicsPipeline(desc);
}
{
ComputePipelineDesc desc("MBlur tile generation");
desc.shortLifeTime = true;
desc.shader = ShaderByteCode(g_tile_gen_cs);
tileGenPipeline = CreateComputePipeline(desc);
}
{
ComputePipelineDesc desc("MBlur tile dilation");
desc.shortLifeTime = true;
desc.shader = ShaderByteCode(g_tile_max_cs);
tileMaxPipeline = CreateComputePipeline(desc);
}
{
GraphicsPipelineDesc desc("MBlur reconstruction filter");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(g_fullscreen_vs);
desc.pixelShader = ShaderByteCode(g_blur_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, crp.renderTargetFormat);
blurPipeline = CreateGraphicsPipeline(desc);
}
{
TextureDesc desc("MBlur velocity tile #1", tileTextureWidth, tileTextureHeight);
desc.shortLifeTime = true;
desc.committedResource = true;
desc.initialState = ResourceStates::UnorderedAccessBit;
desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit;
desc.format = TextureFormat::R16G16_Float;
tileTexture = CreateTexture(desc);
desc.name = "MBlur velocity tile #2";
tileTexture2 = CreateTexture(desc);
}
{
TextureDesc desc("MBlur packed velocity/depth", glConfig.vidWidth, glConfig.vidHeight);
desc.shortLifeTime = true;
desc.committedResource = true;
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::ComputeShaderAccessBit | ResourceStates::PixelShaderAccessBit;
desc.format = TextureFormat::R32G32_UInt;
packedTexture = CreateTexture(desc);
}
}
void MotionBlur::Draw()
{
if(crp_mblur->integer == 0 ||
(backEnd.refdef.rdflags & RDF_CNQ3_TELEPORTED) != 0 ||
!IsViewportFullscreen(backEnd.viewParms))
{
return;
}
DrawPack();
DrawTileGen();
DrawTileMax();
DrawBlur();
}
void MotionBlur::DrawPack()
{
SCOPED_RENDER_PASS("MBlur Pack", 0.125f, 0.125f, 0.25f);
CmdBeginBarrier();
CmdTextureBarrier(crp.motionVectorMBTexture, ResourceStates::PixelShaderAccessBit);
CmdTextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit);
CmdTextureBarrier(packedTexture, ResourceStates::RenderTargetBit);
CmdEndBarrier();
MBPackRC rc = {};
rc.motionScale = crp_mblur_exposure->value;
rc.maxRadiusPx = min(glConfig.vidHeight / 25.0f, 30.0f);
CmdBindRenderTargets(1, &packedTexture, NULL);
CmdBindPipeline(packPipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}
void MotionBlur::DrawTileGen()
{
SCOPED_RENDER_PASS("MBlur Tile Gen", 0.125f, 0.125f, 0.25f);
CmdBeginBarrier();
CmdTextureBarrier(packedTexture, ResourceStates::ComputeShaderAccessBit);
CmdTextureBarrier(tileTexture, ResourceStates::UnorderedAccessBit);
CmdEndBarrier();
MBTileGenRC rc = {};
rc.inputTextureIndex = GetTextureIndexSRV(packedTexture);
rc.outputTextureIndex = GetTextureIndexUAV(tileTexture, 0);
CmdBindPipeline(tileGenPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((tileTextureWidth + 7) / 8, (tileTextureHeight + 7) / 8, 1);
}
void MotionBlur::DrawTileMax()
{
SCOPED_RENDER_PASS("MBlur Tile Max", 0.125f, 0.125f, 0.25f);
CmdBeginBarrier();
CmdTextureBarrier(tileTexture, ResourceStates::ComputeShaderAccessBit);
CmdTextureBarrier(tileTexture2, ResourceStates::UnorderedAccessBit);
CmdEndBarrier();
MBTileMaxRC rc = {};
rc.inputTextureIndex = GetTextureIndexSRV(tileTexture);
rc.outputTextureIndex = GetTextureIndexUAV(tileTexture2, 0);
rc.samplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Point);
CmdBindPipeline(tileMaxPipeline);
CmdSetComputeRootConstants(0, sizeof(rc), &rc);
CmdDispatch((tileTextureWidth + 7) / 8, (tileTextureHeight + 7) / 8, 1);
}
void MotionBlur::DrawBlur()
{
SCOPED_RENDER_PASS("MBlur Blur", 0.125f, 0.125f, 0.25f);
crp.SwapRenderTargets();
CmdBeginBarrier();
CmdTextureBarrier(crp.GetReadRenderTarget(), ResourceStates::PixelShaderAccessBit);
CmdTextureBarrier(tileTexture2, ResourceStates::PixelShaderAccessBit);
CmdTextureBarrier(packedTexture, ResourceStates::PixelShaderAccessBit);
CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit);
CmdEndBarrier();
MBBlurRC rc = {};
rc.colorTextureIndex = GetTextureIndexSRV(crp.GetReadRenderTarget());
rc.tileTextureIndex = GetTextureIndexSRV(tileTexture2);
rc.packedTextureIndex = GetTextureIndexSRV(packedTexture);
rc.blueNoiseTextureIndex = GetTextureIndexSRV(crp.blueNoise2D);
rc.pointSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Point);
rc.linearSamplerIndex = GetSamplerIndex(TW_CLAMP_TO_EDGE, TextureFilter::Linear);
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
CmdBindPipeline(blurPipeline);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDraw(3, 0);
}

View file

@ -23,6 +23,8 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
#include "crp_local.h"
#include "compshaders/crp/prepass.h"
#include "compshaders/crp/fullscreen.h"
#include "compshaders/crp/skybox_motion.h"
#pragma pack(push, 4)
@ -30,17 +32,128 @@ struct PrepassRC
{
float modelViewMatrix[16];
float modelMatrix[16];
float normalMatrix[16];
float prevModelMatrix[16];
float normalMatrix[9];
float motionBlurScale;
uint32_t textureIndex;
uint32_t samplerIndex;
uint32_t alphaTest;
uint32_t motionBlurMode;
};
#pragma pack(pop)
struct EntityData
{
float modelMatrix[16];
int uniqueId;
};
struct EntityTracker
{
void BeginFrame()
{
Q_assert(!begun);
Q_assert(!enabled);
const viewParms_t& vp = backEnd.viewParms;
enabled = IsViewportFullscreen(vp) && !vp.isPortal;
begun = true;
}
void EndFrame()
{
Q_assert(begun);
begun = false;
if(enabled)
{
enabled = false;
EntityArray* prevEntsCopy = prevEnts;
prevEnts = currEnts;
currEnts = prevEntsCopy;
currEnts->Clear();
}
}
void CompareEntity(float* prevModelMatrix, int uniqueId, const float* modelMatrix)
{
Q_assert(begun);
if(!enabled || uniqueId == 0)
{
memcpy(prevModelMatrix, modelMatrix, 16 * sizeof(float));
return;
}
EntityData* prevEnt = NULL;
for(uint32_t i = 0, end = prevEnts->count; i < end; i++)
{
EntityData& ent = (*prevEnts)[i];
if(ent.uniqueId == uniqueId)
{
prevEnt = &ent;
break;
}
}
if(prevEnt != NULL)
{
memcpy(prevModelMatrix, prevEnt->modelMatrix, 16 * sizeof(float));
}
else
{
memcpy(prevModelMatrix, modelMatrix, 16 * sizeof(float));
}
EntityData* currEnt = NULL;
for(uint32_t i = 0, end = currEnts->count; i < end; i++)
{
EntityData& ent = (*currEnts)[i];
if(ent.uniqueId == uniqueId)
{
currEnt = &ent;
break;
}
}
if(currEnt != NULL)
{
memcpy(currEnt->modelMatrix, modelMatrix, 16 * sizeof(float));
}
else
{
EntityData ent;
memcpy(ent.modelMatrix, modelMatrix, 16 * sizeof(float));
ent.uniqueId = uniqueId;
currEnts->Add(ent);
}
}
private:
typedef RHI::StaticUnorderedArray<EntityData, 16384> EntityArray;
EntityArray entities[2];
EntityArray* currEnts = &entities[0];
EntityArray* prevEnts = &entities[1];
bool enabled;
bool begun;
};
static EntityTracker et;
void Prepass::Init()
{
psoCache.Init(psoCacheEntries, ARRAY_LEN(psoCacheEntries));
{
GraphicsPipelineDesc desc("Skybox Motion Vectors");
desc.shortLifeTime = true;
desc.vertexShader.Set(g_fullscreen_vs);
desc.pixelShader.Set(g_skybox_motion_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, TextureFormat::R16G16_Float);
skyboxMotionPipeline = CreateGraphicsPipeline(desc);
}
}
void Prepass::Draw(const drawSceneViewCommand_t& cmd)
@ -57,6 +170,8 @@ void Prepass::Draw(const drawSceneViewCommand_t& cmd)
backEnd.refdef = cmd.refdef;
backEnd.viewParms = cmd.viewParms;
et.BeginFrame();
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
@ -65,18 +180,44 @@ void Prepass::Draw(const drawSceneViewCommand_t& cmd)
CmdTextureBarrier(crp.depthTexture, ResourceStates::DepthWriteBit);
CmdTextureBarrier(crp.normalTexture, ResourceStates::RenderTargetBit);
CmdTextureBarrier(crp.motionVectorTexture, ResourceStates::RenderTargetBit);
CmdTextureBarrier(crp.motionVectorMBTexture, ResourceStates::RenderTargetBit);
CmdTextureBarrier(crp.shadingPositionTexture, ResourceStates::RenderTargetBit);
CmdEndBarrier();
CmdClearDepthStencilTarget(crp.depthTexture, true, 0.0f);
CmdClearColorTarget(crp.normalTexture, vec4_zero, NULL);
CmdClearColorTarget(crp.motionVectorTexture, vec4_zero, NULL);
CmdClearColorTarget(crp.shadingPositionTexture, vec4_zero, NULL);
// clear the motion vectors with skybox values
CmdBeginDebugLabel("Skybox Motion Vectors");
{
CmdBindRenderTargets(1, &crp.motionVectorTexture, NULL);
CmdBindPipeline(skyboxMotionPipeline);
CmdDraw(3, 0);
if(crp_mblur->integer & MotionBlurModes::CameraBit)
{
CmdBindRenderTargets(1, &crp.motionVectorMBTexture, NULL);
CmdBindPipeline(skyboxMotionPipeline);
CmdDraw(3, 0);
}
else
{
CmdClearColorTarget(crp.motionVectorMBTexture, vec4_zero, NULL);
}
}
CmdEndDebugLabel();
GeoBuffers& db = crp.dynBuffers[GetFrameIndex()];
db.BeginUpload();
const HTexture renderTargets[] = { crp.normalTexture, crp.motionVectorTexture, crp.shadingPositionTexture };
const HTexture renderTargets[] =
{
crp.normalTexture,
crp.motionVectorTexture,
crp.motionVectorMBTexture,
crp.shadingPositionTexture
};
CmdBindRenderTargets(ARRAY_LEN(renderTargets), renderTargets, &crp.depthTexture);
CmdBindVertexBuffers(ARRAY_LEN(db.vertexBuffers), db.vertexBuffers, db.vertexBufferStrides, NULL);
CmdBindIndexBuffer(db.indexBuffer.buffer, IndexType::UInt32, 0);
@ -88,6 +229,7 @@ void Prepass::Draw(const drawSceneViewCommand_t& cmd)
const shader_t* shader = NULL;
const shader_t* oldShader = NULL;
int oldEntityNum = -1;
batchEntityId = -1;
backEnd.currentEntity = &tr.worldEntity;
tess.numVertexes = 0;
@ -109,13 +251,16 @@ void Prepass::Draw(const drawSceneViewCommand_t& cmd)
const bool shaderChanged = shader != oldShader;
const bool entityChanged = entityNum != oldEntityNum;
if(shaderChanged || entityChanged)
const bool idChanged = drawSurf->uniqueEntityId != batchEntityId;
if(shaderChanged || entityChanged || idChanged)
{
oldShader = shader;
oldEntityNum = entityNum;
EndBatch();
BeginBatch(shader);
tess.greyscale = drawSurf->greyscale;
batchEntityId = drawSurf->uniqueEntityId;
batchMotionScale = drawSurf->motionBlurScale;
}
if(entityChanged)
@ -136,6 +281,8 @@ void Prepass::Draw(const drawSceneViewCommand_t& cmd)
CmdSetViewportAndScissor(backEnd.viewParms);
batchOldDepthHack = false;
batchDepthHack = false;
et.EndFrame();
}
void Prepass::ProcessShader(shader_t& shader)
@ -181,9 +328,10 @@ void Prepass::ProcessShader(shader_t& shader)
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);
desc.AddRenderTarget(0, TextureFormat::RGBA128_Float);
desc.AddRenderTarget(0, TextureFormat::R16G16_SNorm);
desc.AddRenderTarget(0, TextureFormat::R16G16_Float);
desc.AddRenderTarget(0, TextureFormat::R16G16_Float);
desc.AddRenderTarget(0, TextureFormat::R32G32B32A32_Float);
pipeline_t& p = shader.prepassPipeline;
p.firstStage = 0;
@ -219,6 +367,7 @@ void Prepass::EndBatch()
{
PrepassRC rc = {};
float tempMatrix[16];
float normalMatrix[16];
const int vertexCount = tess.numVertexes;
const int indexCount = tess.numIndexes;
@ -265,13 +414,26 @@ void Prepass::EndBatch()
const uint32_t alphaTest = AlphaTestShaderConstFromStateBits(stage->stateBits);
Q_assert(sampIdx < ARRAY_LEN(crp.samplers));
R_InvMatrix(backEnd.modelMatrix, tempMatrix);
R_TransposeMatrix(tempMatrix, normalMatrix);
memcpy(rc.modelViewMatrix, backEnd.orient.modelMatrix, sizeof(rc.modelViewMatrix));
memcpy(rc.modelMatrix, backEnd.modelMatrix, sizeof(rc.modelMatrix));
R_InvMatrix(backEnd.modelMatrix, tempMatrix);
R_TransposeMatrix(tempMatrix, rc.normalMatrix);
et.CompareEntity(rc.prevModelMatrix, batchEntityId, backEnd.modelMatrix);
rc.normalMatrix[0] = normalMatrix[ 0];
rc.normalMatrix[1] = normalMatrix[ 1];
rc.normalMatrix[2] = normalMatrix[ 2];
rc.normalMatrix[3] = normalMatrix[ 4];
rc.normalMatrix[4] = normalMatrix[ 5];
rc.normalMatrix[5] = normalMatrix[ 6];
rc.normalMatrix[6] = normalMatrix[ 8];
rc.normalMatrix[7] = normalMatrix[ 9];
rc.normalMatrix[8] = normalMatrix[10];
rc.motionBlurScale = batchMotionScale;
rc.textureIndex = texIdx;
rc.samplerIndex = sampIdx;
rc.alphaTest = alphaTest;
rc.motionBlurMode = crp_mblur->integer;
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
db.DrawStage(vertexCount, indexCount);

View file

@ -172,11 +172,11 @@ void GRP::Init()
renderTargetFormat = TextureFormat::R10G10B10A2_UNorm;
break;
case RTCF_R16G16B16A16:
renderTargetFormat = TextureFormat::RGBA64_UNorm;
renderTargetFormat = TextureFormat::R16G16B16A16_UNorm;
break;
case RTCF_R8G8B8A8:
default:
renderTargetFormat = TextureFormat::RGBA32_UNorm;
renderTargetFormat = TextureFormat::R8G8B8A8_UNorm;
break;
}
@ -198,7 +198,7 @@ void GRP::Init()
Vector4Clear(desc.clearColor);
desc.usePreferredClearValue = true;
desc.committedResource = true;
desc.format = TextureFormat::RGBA32_UNorm;
desc.format = TextureFormat::R8G8B8A8_UNorm;
desc.shortLifeTime = true;
readbackRenderTarget = RHI::CreateTexture(desc);
}

View file

@ -58,9 +58,9 @@ void PostProcess::Init()
}
TextureFormat::Id rtFormats[RTCF_COUNT] = {};
rtFormats[RTCF_R8G8B8A8] = TextureFormat::RGBA32_UNorm;
rtFormats[RTCF_R8G8B8A8] = TextureFormat::R8G8B8A8_UNorm;
rtFormats[RTCF_R10G10B10A2] = TextureFormat::R10G10B10A2_UNorm;
rtFormats[RTCF_R16G16B16A16] = TextureFormat::RGBA64_UNorm;
rtFormats[RTCF_R16G16B16A16] = TextureFormat::R16G16B16A16_UNorm;
for(int i = 0; i < RTCF_COUNT; ++i)
{
Q_assert((int)rtFormats[i] > 0 && (int)rtFormats[i] < TextureFormat::Count);
@ -91,7 +91,7 @@ void PostProcess::Init()
desc.pixelShader = ShaderByteCode(g_post_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, TextureFormat::RGBA32_UNorm);
desc.AddRenderTarget(0, TextureFormat::R8G8B8A8_UNorm);
toneMapPipeline = CreateGraphicsPipeline(desc);
}

View file

@ -134,7 +134,7 @@ void SMAA::Update()
desc.initialState = ResourceStates::PixelShaderAccessBit;
desc.allowedState = ResourceStates::PixelShaderAccessBit;
desc.committedResource = true;
desc.format = TextureFormat::RG16_UNorm;
desc.format = TextureFormat::R8G8_UNorm;
areaTexture = CreateTexture(desc);
MappedTexture texture;
@ -208,7 +208,7 @@ void SMAA::Update()
Vector4Clear(desc.clearColor);
desc.usePreferredClearValue = true;
desc.committedResource = true;
desc.format = TextureFormat::RG16_UNorm;
desc.format = TextureFormat::R8G8_UNorm;
edgeTexture = CreateTexture(desc);
}
{
@ -218,7 +218,7 @@ void SMAA::Update()
Vector4Clear(desc.clearColor);
desc.usePreferredClearValue = true;
desc.committedResource = true;
desc.format = TextureFormat::RGBA32_UNorm;
desc.format = TextureFormat::R8G8B8A8_UNorm;
blendTexture = CreateTexture(desc);
}
{
@ -226,7 +226,7 @@ void SMAA::Update()
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit;
desc.committedResource = true;
desc.format = TextureFormat::RGBA32_UNorm;
desc.format = TextureFormat::R8G8B8A8_UNorm;
inputTexture = CreateTexture(desc);
}
{
@ -234,7 +234,7 @@ void SMAA::Update()
desc.initialState = ResourceStates::RenderTargetBit;
desc.allowedState = ResourceStates::RenderTargetBit | ResourceStates::PixelShaderAccessBit;
desc.committedResource = true;
desc.format = TextureFormat::RGBA32_UNorm;
desc.format = TextureFormat::R8G8B8A8_UNorm;
outputTexture = CreateTexture(desc);
grp.post.SetInverseToneMapInput(outputTexture);
@ -281,7 +281,7 @@ void SMAA::Update()
desc.depthStencil.frontFace.passOp = StencilOp::Replace;
desc.depthStencil.backFace.passOp = StencilOp::Replace;
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, TextureFormat::RG16_UNorm);
desc.AddRenderTarget(0, TextureFormat::R8G8_UNorm);
firstPassPipeline = CreateGraphicsPipeline(desc);
}
{
@ -295,7 +295,7 @@ void SMAA::Update()
desc.depthStencil.frontFace.comparison = ComparisonFunction::Equal;
desc.depthStencil.backFace.comparison = ComparisonFunction::Equal;
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, TextureFormat::RGBA32_UNorm);
desc.AddRenderTarget(0, TextureFormat::R8G8B8A8_UNorm);
secondPassPipeline = CreateGraphicsPipeline(desc);
}
{
@ -304,7 +304,7 @@ void SMAA::Update()
desc.pixelShader = GetShaderByteCode(2, true);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.AddRenderTarget(0, TextureFormat::RGBA32_UNorm);
desc.AddRenderTarget(0, TextureFormat::R8G8B8A8_UNorm);
thirdPassPipeline = CreateGraphicsPipeline(desc);
}
}

View file

@ -877,16 +877,22 @@ namespace RHI
{
switch(format)
{
case TextureFormat::RGBA128_Float:
case TextureFormat::R32G32B32A32_Float:
return 16;
case TextureFormat::RGBA64_Float:
case TextureFormat::R16G16B16A16_UNorm:
case TextureFormat::R16G16B16A16_Float:
case TextureFormat::R32G32_Float:
case TextureFormat::R32G32_UInt:
return 8;
case TextureFormat::RGBA32_UNorm:
case TextureFormat::R8G8B8A8_UNorm:
case TextureFormat::Depth32_Float:
case TextureFormat::Depth24_Stencil8:
case TextureFormat::R10G10B10A2_UNorm:
case TextureFormat::R32_UInt:
case TextureFormat::R16G16_SNorm:
case TextureFormat::R16G16_Float:
return 4;
case TextureFormat::RG16_UNorm:
case TextureFormat::R8G8_UNorm:
return 2;
case TextureFormat::R8_UNorm:
return 1;
@ -1260,7 +1266,7 @@ namespace RHI
D3D(readbackCommandList->Reset(readbackCommandAllocator, NULL));
Texture& texture = rhi.textures.Get(htexture);
Q_assert(texture.desc.format == TextureFormat::RGBA32_UNorm);
Q_assert(texture.desc.format == TextureFormat::R8G8B8A8_UNorm);
const D3D12_RESOURCE_DESC textureDesc = texture.texture->GetDesc();
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
rhi.device->GetCopyableFootprints(&textureDesc, 0, 1, 0, &layout, NULL, NULL, NULL);
@ -1804,18 +1810,20 @@ namespace RHI
{
switch(format)
{
case TextureFormat::RGBA32_UNorm: return DXGI_FORMAT_R8G8B8A8_UNORM;
case TextureFormat::RGBA64_UNorm: return DXGI_FORMAT_R16G16B16A16_UNORM;
case TextureFormat::RGBA64_Float: return DXGI_FORMAT_R16G16B16A16_FLOAT;
case TextureFormat::RGBA128_Float: return DXGI_FORMAT_R32G32B32A32_FLOAT;
case TextureFormat::R8G8B8A8_UNorm: return DXGI_FORMAT_R8G8B8A8_UNORM;
case TextureFormat::R16G16B16A16_UNorm: return DXGI_FORMAT_R16G16B16A16_UNORM;
case TextureFormat::R32G32_Float: return DXGI_FORMAT_R32G32_FLOAT;
case TextureFormat::R16G16B16A16_Float: return DXGI_FORMAT_R16G16B16A16_FLOAT;
case TextureFormat::R32G32B32A32_Float: return DXGI_FORMAT_R32G32B32A32_FLOAT;
case TextureFormat::Depth32_Float: return DXGI_FORMAT_D32_FLOAT;
case TextureFormat::Depth24_Stencil8: return DXGI_FORMAT_D24_UNORM_S8_UINT;
case TextureFormat::RG16_UNorm: return DXGI_FORMAT_R8G8_UNORM;
case TextureFormat::R8G8_UNorm: return DXGI_FORMAT_R8G8_UNORM;
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;
case TextureFormat::R32G32_UInt: return DXGI_FORMAT_R32G32_UINT;
case TextureFormat::R16G16_SNorm: return DXGI_FORMAT_R16G16_SNORM;
case TextureFormat::R16G16_Float: return DXGI_FORMAT_R16G16_FLOAT;
default: Q_assert(!"Unsupported texture format"); return DXGI_FORMAT_R8G8B8A8_UNORM;
}
}
@ -2258,7 +2266,7 @@ namespace RHI
}
{
TextureDesc desc("null RW", 1, 1);
desc.format = TextureFormat::RGBA32_UNorm;
desc.format = TextureFormat::R8G8B8A8_UNorm;
desc.initialState = ResourceStates::UnorderedAccessBit;
desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::PixelShaderAccessBit;
rhi.nullRWTexture = CreateTexture(desc);

View file

@ -162,18 +162,20 @@ namespace RHI
enum Id
{
Invalid,
RGBA32_UNorm,
RGBA64_UNorm,
RGBA64_Float,
RGBA128_Float,
R8G8B8A8_UNorm,
R16G16B16A16_UNorm,
R32G32_Float,
R16G16B16A16_Float,
R32G32B32A32_Float,
Depth32_Float,
RG16_UNorm,
R8G8_UNorm,
R8_UNorm,
Depth24_Stencil8,
R10G10B10A2_UNorm,
R32_UInt,
RG32_SNorm,
RG32_Float,
R32G32_UInt,
R16G16_SNorm,
R16G16_Float,
Count
};
};
@ -430,7 +432,7 @@ namespace RHI
struct RenderTarget
{
uint32_t q3BlendMode = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA;
TextureFormat::Id format = TextureFormat::RGBA32_UNorm;
TextureFormat::Id format = TextureFormat::R8G8B8A8_UNorm;
}
renderTargets[MaxRenderTargets];
uint32_t renderTargetCount = 0;
@ -501,7 +503,7 @@ namespace RHI
sampleCount = 1;
initialState = ResourceStates::PixelShaderAccessBit;
allowedState = ResourceStates::PixelShaderAccessBit;
format = TextureFormat::RGBA32_UNorm;
format = TextureFormat::R8G8B8A8_UNorm;
committedResource = false;
usePreferredClearValue = false;
}
@ -514,7 +516,7 @@ namespace RHI
uint32_t sampleCount = 1;
ResourceStates::Flags initialState = ResourceStates::PixelShaderAccessBit;
ResourceStates::Flags allowedState = ResourceStates::PixelShaderAccessBit;
TextureFormat::Id format = TextureFormat::RGBA32_UNorm;
TextureFormat::Id format = TextureFormat::R8G8B8A8_UNorm;
bool committedResource = false;
bool usePreferredClearValue = false; // for render targets and depth/stencil buffers
float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };

View file

@ -290,6 +290,21 @@ bool IsValueInRange(T p, T min, T max)
return all(p >= min) && all(p <= max);
}
bool IsValue01(float2 p)
{
return IsValueInRange(p, float2(0, 0), float2(1, 1));
}
bool IsValue01(float3 p)
{
return IsValueInRange(p, float3(0, 0, 0), float3(1, 1, 1));
}
bool IsValue01(float4 p)
{
return IsValueInRange(p, float4(0, 0, 0, 0), float4(1, 1, 1, 1));
}
template<typename T>
uint2 GetTextureSize(Texture2D<T> texture0)
{
@ -299,6 +314,15 @@ uint2 GetTextureSize(Texture2D<T> texture0)
return size;
}
template<typename T>
uint2 GetTextureSize(RWTexture2D<T> texture0)
{
uint2 size;
texture0.GetDimensions(size.x, size.y);
return size;
}
// by Sakib Saikia, https://sakibsaikia.github.io/graphics/2022/01/04/Nan-Checks-In-HLSL.html
bool IsNan(float x)
{
@ -316,3 +340,59 @@ float AnimateBlueNoise(float blueNoise, uint frameIndex)
{
return frac(blueNoise + float(frameIndex % 32) * 0.61803399);
}
float2 NDCToTC(float2 ndc)
{
float2 tc = ndc * float2(0.5, -0.5) + 0.5;
return tc;
}
float2 TCToNDC(float2 tc)
{
float2 ndc = (2.0 * tc - 1.0) * float2(1, -1);
return ndc;
}
// returns the longest vector
float2 vmax(float2 a, float2 b)
{
float2 result = dot(a, a) > dot(b, b) ? a : b;
return result;
}
uint PackHalf2(float2 input)
{
uint2 d = f32tof16(input);
uint result = d.x | (d.y << 16u);
return result;
}
float2 UnpackHalf2(uint input)
{
uint2 d = uint2(input & 0xFFFFu, input >> 16u);
float2 result = f16tof32(d);
return result;
}
float2 CartesianToPolar(float2 cartesian)
{
float radius = length(cartesian);
float angle = atan2(cartesian.y, cartesian.x);
float2 polar = float2(radius, angle);
return polar;
}
float2 PolarToCartesian(float2 polar)
{
float sinAngle, cosAngle;
sincos(polar.y, sinAngle, cosAngle);
float2 cartesian = polar.x * float2(cosAngle, sinAngle);
return cartesian;
}

View file

@ -0,0 +1,44 @@
/*
===========================================================================
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/>.
===========================================================================
*/
// colorizes motion vectors for debugging
#include "common.hlsli"
#include "fullscreen.hlsli"
cbuffer RootConstants
{
uint textureIndex;
};
float4 ps(VOut input) : SV_Target
{
Texture2D<float2> motionTexture = ResourceDescriptorHeap[textureIndex];
float2 size = float2(GetTextureSize(motionTexture));
float scale = size.y / size.x; // correct for aspect ratio
uint3 tc = uint3(input.position.xy, 0);
float2 raw = motionTexture.Load(tc);
float2 display = abs(raw) * 50.0 * scale;
float4 result = float4(display, 0, 1);
return result;
}

View file

@ -0,0 +1,336 @@
/*
===========================================================================
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/>.
===========================================================================
*/
// motion blur reconstruction filter
#include "common.hlsli"
#include "fullscreen.hlsli"
#include "scene_view.h.hlsli"
cbuffer RootConstants : register(b0)
{
uint colorTextureIndex;
uint tileTextureIndex;
uint packedTextureIndex;
uint blueNoiseTextureIndex;
uint pointSamplerIndex; // clamp
uint linearSamplerIndex; // clamp
};
#if 1
/*
Summary:
- sampling N times in a line along fragment's direction
- sampling N times in a line along neighborhood's strongest direction
- bilinearly interpolating neighborhood tiles in polar coordinates with clamped velocity
- jittering the lines' center points with blue noise
- binary classification of samples based on depth and distance/velocity
- weighting samples based on direction similarity
- background reconstruction using mirrored samples
References:
- "A Reconstruction Filter for Plausible Motion Blur" by McGuire et al.
- "Next-Generation Post-Processing in Call of Duty Advanced Warfare" by Jorge Jimenez
*/
void ProcessSample(
inout float4 fgAccum, inout float4 bgAccum, inout float alpha, inout float center,
float Zs, float Zc, float Vsl, float Vcl, float distTC, float3 C0s, float3 C1s, float W0d)
{
const float DepthThreshold = 0.28; // 10mm assuming 56u == 2m
if(Zs - DepthThreshold < Zc && Vsl >= distTC)
{
fgAccum += float4(C0s, 1) * W0d;
bgAccum += float4(C1s, 1);
alpha += 1.0;
}
else if(Zs >= Zc && Vcl >= distTC)
{
bgAccum += float4(C0s, 1);
}
else
{
center += 1.0;
}
}
float4 ps(VOut input) : SV_Target
{
SceneView scene = GetSceneView();
SamplerState pointSampler = SamplerDescriptorHeap[pointSamplerIndex];
SamplerState linearSampler = SamplerDescriptorHeap[linearSamplerIndex];
Texture2D colorTexture = ResourceDescriptorHeap[colorTextureIndex];
Texture2D<float2> tileTexture = ResourceDescriptorHeap[tileTextureIndex];
Texture2D<uint2> packedTexture = ResourceDescriptorHeap[packedTextureIndex];
Texture2D blueNoiseTexture = ResourceDescriptorHeap[blueNoiseTextureIndex];
float2 fullResSize = float2(GetTextureSize(colorTexture));
float2 tileSize = float2(GetTextureSize(tileTexture));
uint2 blueNoiseSize = GetTextureSize(blueNoiseTexture);
float2 tileTC = input.texCoords;
float2 Vnraw = tileTexture.SampleLevel(pointSampler, tileTC, 0);
float2 tileWeights = frac(tileTC * tileSize + 0.5);
float4 Vnx = tileTexture.GatherRed(pointSampler, tileTC);
float4 Vny = tileTexture.GatherGreen(pointSampler, tileTC);
float2 Vnpa[4];
float Vnw[4];
for(int i = 0; i < 4; i++)
{
float2 Vni = float2(Vnx[i], Vny[i]);
if(length(Vni * fullResSize) < 2.0)
{
Vni = Vnraw; // ignore tiles with small vectors
}
Vnpa[i] = CartesianToPolar(Vni);
}
Vnw[0] = (1.0 - tileWeights.x) * tileWeights.y; // (-,+)
Vnw[1] = tileWeights.x * tileWeights.y; // (+,+)
Vnw[2] = tileWeights.x * (1.0 - tileWeights.y); // (+,-)
Vnw[3] = (1.0 - tileWeights.x) * (1.0 - tileWeights.y); // (-,-)
float2 Vnp = Vnw[0] * Vnpa[0] + Vnw[1] * Vnpa[1] + Vnw[2] * Vnpa[2] + Vnw[3] * Vnpa[3];
Vnp.x = max(Vnp.x, length(Vnraw)); // make sure the lerp won't yield a smaller vector
float2 Vn = PolarToCartesian(Vnp);
float2 Vnn = normalize(Vn);
float3 color = colorTexture.SampleLevel(pointSampler, input.texCoords, 0).rgb;
uint2 packedFrag = packedTexture.Load(uint3(input.texCoords * fullResSize, 0));
float2 Vc = UnpackHalf2(packedFrag.x);
float Vcl = length(Vc);
float2 Vcn = Vc / Vcl;
float Zc = asfloat(packedFrag.y) * scene.zFar;
float lengthPx = length(Vn * fullResSize);
if(lengthPx < 0.5)
{
return float4(color, 1);
}
if(length(Vc * fullResSize) <= 4.0)
{
Vc = Vn;
Vcn = Vnn;
}
uint2 blueNoisePx = (input.texCoords * fullResSize) % blueNoiseSize;
float2 blueNoise = blueNoiseTexture.Load(uint3(blueNoisePx, 0)).xy;
float tcJitter = blueNoise.x;
uint sampleCount = min(2 * uint(ceil(lengthPx)), 64);
float2 tcStepN = 0.5 * Vn / float2(sampleCount - 1, sampleCount - 1);
float2 tcStepC = 0.5 * Vc / float2(sampleCount - 1, sampleCount - 1);
float4 fgAccum = float4(0, 0, 0, 0);
float4 bgAccum = float4(0, 0, 0, 0);
float alpha = 0.0; // foreground (1) / background (0) lerp
float center = 0.0; // center (1) / background (0) lerp
uint realSampleCount = 0;
if(length(Vc * fullResSize) >= 1.0)
{
fgAccum += float4(color, 1);
alpha += 1.0;
}
bgAccum += float4(color, 1);
uint totalSampleCount = sampleCount * 2;
for(uint i = 1; i < totalSampleCount; i++)
{
float lineStep = float(i / 2);
float i0 = tcJitter + lineStep;
float i1 = tcJitter - lineStep;
float2 tcStep, dirn;
[flatten]
if(i % 2 == 0)
{
tcStep = tcStepN;
dirn = Vnn;
}
else
{
tcStep = tcStepC;
dirn = Vcn;
}
float2 TC0 = input.texCoords + i0 * tcStep;
float2 TC1 = input.texCoords + i1 * tcStep;
[branch]
if(!IsValue01(TC0) || !IsValue01(TC1))
{
continue;
}
uint2 packedSample0 = packedTexture.Load(uint3(TC0 * fullResSize, 0));
float2 V0s = UnpackHalf2(packedSample0.x);
float V0sl = length(V0s);
float2 V0sn = V0s / V0sl;
float Z0s = asfloat(packedSample0.y) * scene.zFar;
float3 C0s = colorTexture.SampleLevel(pointSampler, TC0, 0).rgb;
float distTC0 = distance(TC0, input.texCoords);
float W0d = max(dot(V0sn, dirn), 0.0);
uint2 packedSample1 = packedTexture.Load(uint3(TC1 * fullResSize, 0));
float2 V1s = UnpackHalf2(packedSample1.x);
float V1sl = length(V1s);
float2 V1sn = V1s / V1sl;
float Z1s = asfloat(packedSample1.y) * scene.zFar;
float3 C1s = colorTexture.SampleLevel(pointSampler, TC1, 0).rgb;
float distTC1 = distance(TC1, input.texCoords);
float W1d = max(dot(V1sn, dirn), 0.0);
ProcessSample(fgAccum, bgAccum, alpha, center, Z0s, Zc, V0sl, Vcl, distTC0, C0s, C1s, W0d);
ProcessSample(fgAccum, bgAccum, alpha, center, Z1s, Zc, V1sl, Vcl, distTC1, C1s, C0s, W1d);
realSampleCount += 2;
}
if(fgAccum.w <= 0.0 || bgAccum.w <= 0.0)
{
return float4(color, 1.0);
}
fgAccum.rgb /= fgAccum.w;
bgAccum.rgb /= bgAccum.w;
alpha /= float(realSampleCount);
center /= float(realSampleCount);
float3 bg = center * color + (1.0 - center) * bgAccum.rgb;
float3 blended = alpha * fgAccum.rgb + (1.0 - alpha) * bg;
return float4(blended, 1.0);
}
#else
/*
Summary:
- sampling N times in a line along fragment's direction
- sampling N times in a line along neighborhood's strongest direction
- jittering neighborhood tiles to trade banding for blue noise
- jittering the lines' center points with blue noise
- weighting samples based on depth, distance and direction similarity
References:
- "A Reconstruction Filter for Plausible Motion Blur" by McGuire et al.
- "A Fast and Stable Feature-Aware Motion Blur Filter" by Guertin et al.
*/
float Cone(float distSamples, float velocityLength)
{
float result = saturate(1.0 - distSamples / velocityLength);
return result;
}
float Cylinder(float distSamples, float velocityLength)
{
float l = velocityLength;
float result = 1.0 - smoothstep(0.95 * l, 1.05 * l, distSamples);
return result;
}
float SoftDepthFalloff(float zA, float zB)
{
float result = saturate(1.0 - (zB - zA) / max(zB, zA));
return result;
}
float4 ps(VOut input) : SV_Target
{
SceneView scene = GetSceneView();
SamplerState pointSampler = SamplerDescriptorHeap[pointSamplerIndex];
SamplerState linearSampler = SamplerDescriptorHeap[linearSamplerIndex];
Texture2D colorTexture = ResourceDescriptorHeap[colorTextureIndex];
Texture2D<float2> tileTexture = ResourceDescriptorHeap[tileTextureIndex];
Texture2D<uint2> packedTexture = ResourceDescriptorHeap[packedTextureIndex];
Texture2D blueNoiseTexture = ResourceDescriptorHeap[blueNoiseTextureIndex];
float2 fullResSize = float2(GetTextureSize(colorTexture));
float2 tileSize = float2(GetTextureSize(tileTexture));
uint2 blueNoiseSize = GetTextureSize(blueNoiseTexture);
uint2 blueNoisePx = (input.texCoords * fullResSize) % blueNoiseSize;
float2 blueNoise = blueNoiseTexture.Load(uint3(blueNoisePx, 0)).xy;
float2 tileJitterTC = 0.25 * (blueNoise * 2.0 - 1.0) / tileSize;
float2 tileTC = input.texCoords + tileJitterTC;
float2 Vn = tileTexture.SampleLevel(pointSampler, tileTC, 0);
float2 Vnn = normalize(Vn);
float3 color = colorTexture.SampleLevel(pointSampler, input.texCoords, 0).rgb;
uint2 packedFrag = packedTexture.Load(uint3(input.texCoords * fullResSize, 0));
float2 Vc = UnpackHalf2(packedFrag.x);
float Vcl = length(Vc);
float2 Vcn = Vc / Vcl;
float Zc = asfloat(packedFrag.y) * scene.zFar;
float lengthPx = length(Vn * fullResSize);
if(lengthPx < 0.5)
{
return float4(color, 1);
}
uint sampleCount = min(4 * uint(ceil(lengthPx)), 64); // per line
float2 tcStepN = 2.0 * Vn / float2(sampleCount - 1, sampleCount - 1);
float2 tcStepC = 2.0 * Vc / float2(sampleCount - 1, sampleCount - 1);
float3 colorAccum = color;
float weightAccum = 1.0;
uint sampleCountTotal = sampleCount * 2; // across all lines
for(uint i = 0; i < sampleCount; i++)
{
float step = float(i / 2);
float2 tc;
float2 dirn;
[flatten]
if(sampleCount % 2 == 0)
{
tc = input.texCoords - 0.5 * Vn + (blueNoise.x + step) * tcStepN;
dirn = Vnn;
}
else
{
tc = input.texCoords - 0.5 * Vc + (blueNoise.y + step) * tcStepC;
dirn = Vcn;
}
if(!IsValue01(tc))
{
continue;
}
uint2 packedSample = packedTexture.Load(uint3(tc * fullResSize, 0));
float2 Vs = UnpackHalf2(packedSample.x);
float Vsl = length(Vs);
float2 Vsn = Vs / Vsl;
float Zs = asfloat(packedSample.y) * scene.zFar;
float3 colorSample = colorTexture.SampleLevel(pointSampler, tc, 0).rgb;
float fg = SoftDepthFalloff(Zc, Zs);
float bg = SoftDepthFalloff(Zs, Zc);
float distTC = distance(tc, input.texCoords);
float fgWeight = fg * Cone(distTC, Vsl) * max(0, dot(dirn, Vsn));
float bgWeight = bg * Cone(distTC, Vcl) * max(0, dot(dirn, Vcn));
float overlapWeight = 2.0 * Cylinder(distTC, Vsl) * Cylinder(distTC, Vcl);
float weight = fgWeight + bgWeight + overlapWeight;
colorAccum += colorSample * weight;
weightAccum += weight;
}
float4 result = float4(colorAccum /= weightAccum, 1.0);
return result;
}
#endif

View file

@ -0,0 +1,57 @@
/*
===========================================================================
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/>.
===========================================================================
*/
// motion blur packing of scaled velocities and linear depth into a full-screen texture
#include "common.hlsli"
#include "fullscreen.hlsli"
#include "scene_view.h.hlsli"
cbuffer RootConstants
{
float motionScale;
float maxRadiusPx;
};
uint2 ps(VOut input) : SV_Target
{
SceneView scene = GetSceneView();
Texture2D<float> depthTexture = ResourceDescriptorHeap[scene.depthTextureIndex];
float2 textureSize = float2(GetTextureSize(depthTexture));
uint3 tc = uint3(input.texCoords * textureSize, 0);
float depthZW = depthTexture.Load(tc);
float linearDepth01 = scene.LinearDepth(depthZW) / scene.zFar;
Texture2D<float2> motionTexture = ResourceDescriptorHeap[scene.motionVectorMBTextureIndex];
float2 motionTC = motionTexture.Load(tc) * motionScale;
float2 motionPx = motionTC * textureSize;
float motionLengthPx = length(motionPx);
if(motionLengthPx > maxRadiusPx)
{
motionTC *= maxRadiusPx / motionLengthPx;
}
uint motionPacked = PackHalf2(motionTC);
uint2 result = uint2(motionPacked, asuint(linearDepth01));
return result;
}

View file

@ -0,0 +1,62 @@
/*
===========================================================================
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/>.
===========================================================================
*/
// motion blur velocity tile generation
#include "common.hlsli"
cbuffer RootConstants : register(b0)
{
uint inputTextureIndex;
uint outputTextureIndex;
};
[numthreads(8, 8, 1)]
void cs(uint3 dtid : SV_DispatchThreadID, uint3 gid : SV_GroupID, uint3 gtid : SV_GroupThreadID)
{
uint2 tcOut = dtid.xy;
RWTexture2D<float2> outputTexture = ResourceDescriptorHeap[outputTextureIndex];
if(any(tcOut >= GetTextureSize(outputTexture)))
{
return;
}
Texture2D<uint2> inputTexture = ResourceDescriptorHeap[inputTextureIndex];
// This loop can read out of bounds in the inputTexture.
// Each full-res pixel has a corresponding tile pixel, but the reverse isn't always true.
// Texture.Load is specced to return 0 on OOB accesses.
// Since we max() the values, zeroes have no effect on the final result.
uint2 tcInCorner = tcOut * uint2(16, 16);
float2 maxVelocity = float2(0, 0);
for(uint y = 0; y < 16; y++)
{
for(uint x = 0; x < 16; x++)
{
uint2 tcIn = tcInCorner + uint2(x, y);
float2 velocity = UnpackHalf2(inputTexture.Load(uint3(tcIn, 0)).x);
maxVelocity = vmax(maxVelocity, velocity);
}
}
outputTexture[tcOut] = maxVelocity;
}

View file

@ -0,0 +1,62 @@
/*
===========================================================================
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/>.
===========================================================================
*/
// motion blur velocity tile dilation
#include "common.hlsli"
cbuffer RootConstants : register(b0)
{
uint inputTextureIndex;
uint outputTextureIndex;
uint samplerIndex; // point/clamp
};
[numthreads(8, 8, 1)]
void cs(uint3 dtid : SV_DispatchThreadID)
{
uint2 tc = dtid.xy;
RWTexture2D<float2> outputTexture = ResourceDescriptorHeap[outputTextureIndex];
uint2 textureSize = GetTextureSize(outputTexture);
if(any(tc >= textureSize))
{
return;
}
Texture2D<float2> inputTexture = ResourceDescriptorHeap[inputTextureIndex];
SamplerState samplerState = SamplerDescriptorHeap[samplerIndex];
float2 tcShifted = float2(tc) + float2(0.5, 0.5);
float2 pixelSize = float2(1, 1) / float2(textureSize);
float2 maxVelocity = float2(0, 0);
for(int y = -1; y <= 1; y++)
{
for(int x = -1; x <= 1; x++)
{
float2 tc01 = (tcShifted + float2(x, y)) * pixelSize;
float2 velocity = inputTexture.SampleLevel(samplerState, tc01, 0);
maxVelocity = vmax(maxVelocity, velocity);
}
}
outputTexture[tc] = maxVelocity;
}

View file

@ -30,10 +30,21 @@ cbuffer RootConstants
{
matrix modelViewMatrix;
matrix modelMatrix;
matrix normalMatrix;
matrix prevModelMatrix;
float normalMatrix0;
float normalMatrix1;
float normalMatrix2;
float normalMatrix3;
float normalMatrix4;
float normalMatrix5;
float normalMatrix6;
float normalMatrix7;
float normalMatrix8;
float motionBlurScale;
uint textureIndex;
uint samplerIndex;
uint alphaTest;
uint motionBlurMode;
};
struct VIn
@ -47,8 +58,11 @@ struct VIn
struct VOut
{
float4 position : SV_Position;
nointerpolation float3 normalWS : NORMAL;
nointerpolation float3 positionWS : POSITION;
float4 currPosition : CURRPOSITION;
float4 prevPosition : PREVPOSITION;
float4 prevPositionMB : PREVPOSITIONMB;
nointerpolation float3 normalWS : NORMALWS;
nointerpolation float3 positionWS : POSITIONWS;
float2 texCoords : TEXCOORD0;
float4 color : COLOR0;
float clipDist : SV_ClipDistance0;
@ -57,13 +71,40 @@ struct VOut
VOut vs(VIn input)
{
SceneView scene = GetSceneView();
matrix projectionMatrix = scene.projectionMatrix;
float4 positionVS = mul(modelViewMatrix, float4(input.position.xyz, 1));
float4 positionOS = float4(input.position, 1);
float4 positionWS = mul(modelMatrix, positionOS);
float4 positionVS = mul(modelViewMatrix, float4(input.position, 1));
float4 prevPositionWS = mul(prevModelMatrix, positionOS);
float3x3 normalMatrix = float3x3(
normalMatrix0, normalMatrix3, normalMatrix6,
normalMatrix1, normalMatrix4, normalMatrix7,
normalMatrix2, normalMatrix5, normalMatrix8);
float4 currPosition = mul(scene.projectionMatrix, positionVS);
float4 prevPosition = mul(scene.prevViewProjMatrix, prevPositionWS);
float4 prevPositionMB;
if(motionBlurMode == 1)
{
// camera only, ignore previous model matrix
prevPositionMB = mul(scene.prevViewProjMatrix, positionWS);
}
else if(motionBlurMode == 2)
{
// object only, ignore previous view & projection matrices
prevPositionMB = mul(scene.projectionMatrix, mul(scene.viewMatrix, prevPositionWS));
}
else
{
// both combined
prevPositionMB = prevPosition;
}
VOut output;
output.position = mul(projectionMatrix, positionVS);
output.normalWS = mul(normalMatrix, float4(input.normal, 0)).xyz;
output.positionWS = mul(modelMatrix, float4(input.position, 1)).xyz;
output.position = currPosition;
output.currPosition = currPosition;
output.prevPosition = prevPosition;
output.prevPositionMB = prevPositionMB;
output.normalWS = mul(normalMatrix, input.normal);
output.positionWS = positionWS.xyz;
output.texCoords = input.texCoords;
output.color = input.color;
output.clipDist = dot(positionVS, scene.clipPlane);
@ -75,24 +116,10 @@ struct POut
{
float2 normal : SV_Target0;
float2 motionVector : SV_Target1;
float4 shadingPosition : SV_Target2;
float2 motionVectorMB : SV_Target2;
float4 shadingPosition : SV_Target3;
};
float3 FixNormal(float3 vertexNormal, float3 faceNormal)
{
if(length(vertexNormal) < 0.5)
{
return faceNormal;
}
if(dot(vertexNormal, faceNormal) < 0.0)
{
return -vertexNormal;
}
return vertexNormal;
}
POut ps(VOut input, float3 barycentrics : SV_Barycentrics)
{
if(alphaTest != ATEST_NONE)
@ -125,9 +152,14 @@ POut ps(VOut input, float3 barycentrics : SV_Barycentrics)
float dist = saturate(distance(shadingPosition, position));
float positionDelta = asfloat(PackColor(float4(dist3, dist)));
float2 currPosTC = NDCToTC(input.currPosition.xy / input.currPosition.w);
float2 prevPosTC = NDCToTC(input.prevPosition.xy / input.prevPosition.w);
float2 prevPosMBTC = NDCToTC(input.prevPositionMB.xy / input.prevPositionMB.w);
POut output;
output.normal = OctEncode(normalize(normal));
output.motionVector = float2(0, 0); // @TODO:
output.motionVector = currPosTC - prevPosTC;
output.motionVectorMB = (currPosTC - prevPosMBTC) * motionBlurScale;
output.shadingPosition = float4(shadingPosition, positionDelta);
return output;

View file

@ -45,6 +45,9 @@ struct SceneView
matrix invProjectionMatrix;
matrix viewMatrix;
matrix invViewMatrix;
matrix prevViewProjMatrix;
matrix prevViewMatrix;
matrix prevProjectionMatrix;
float4 clipPlane;
float4 debug;
uint sceneViewIndex;
@ -52,11 +55,24 @@ struct SceneView
uint depthTextureIndex;
uint normalTextureIndex;
uint shadingPositionTextureIndex;
uint motionVectorTextureIndex;
uint motionVectorMBTextureIndex;
uint lightTextureIndex;
uint tlasBufferIndex;
uint tlasInstanceBufferIndex;
uint lightCount;
float linearDepthA;
float linearDepthB;
float zNear;
float zFar;
DynamicLight lights[SCENE_VIEW_MAX_LIGHTS];
#if !defined(__cplusplus)
float LinearDepth(float zwDepth)
{
return linearDepthB / (zwDepth - linearDepthA);
}
#endif
};
#if defined(__cplusplus)

View file

@ -0,0 +1,48 @@
/*
===========================================================================
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/>.
===========================================================================
*/
// outputs motion vectors for objects infinitely far away
#include "common.hlsli"
#include "fullscreen.hlsli"
#include "scene_view.h.hlsli"
float2 ps(VOut input) : SV_Target
{
SceneView scene = GetSceneView();
// we need the position to match for perfect translation invariance
matrix prevViewMatrix = scene.prevViewMatrix;
prevViewMatrix[0][3] = scene.viewMatrix[0][3];
prevViewMatrix[1][3] = scene.viewMatrix[1][3];
prevViewMatrix[2][3] = scene.viewMatrix[2][3];
float2 currNDC = TCToNDC(input.texCoords.xy);
float4 currPointNDC = float4(currNDC, 0, 1);
float4 pointWSw = mul(scene.invViewMatrix, mul(scene.invProjectionMatrix, currPointNDC));
float4 pointWS = pointWSw / pointWSw.w;
float4 prevPointNDC = mul(scene.prevProjectionMatrix, mul(prevViewMatrix, pointWS));
float2 prevNDC = prevPointNDC.xy / prevPointNDC.w;
float2 motionTC = NDCToTC(currNDC) - NDCToTC(prevNDC);
return motionTC;
}

View file

@ -76,11 +76,6 @@ float GetBitAsFloat(uint bits, uint bitIndex)
return (bits & (1u << bitIndex)) ? 1.0 : 0.0;
}
float2 UnpackHalf2(uint data)
{
return float2(f16tof32(data), f16tof32(data >> 16));
}
float4 DepthFadeFragmentColor(float4 color, OIT_Fragment fragment, float storedDepthZW)
{
if(((fragment.depthFadeScaleBias >> 8) & 1) == 0)

View file

@ -72,7 +72,7 @@ void MipMapGenerator::Init(bool ddhi_, const ShaderByteCode& g2l, const ShaderBy
for(int t = 0; t < 2; ++t)
{
TextureDesc desc(va("mip-map generation #%d", t + 1), MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE);
desc.format = TextureFormat::RGBA64_Float;
desc.format = TextureFormat::R16G16B16A16_Float;
desc.initialState = ResourceStates::UnorderedAccessBit;
desc.allowedState = ResourceStates::UnorderedAccessBit | ResourceStates::ComputeShaderAccessBit;
textures[MipSlice::Float16_0 + t] = CreateTexture(desc);

View file

@ -222,6 +222,24 @@ void R_AddDrawSurfCmd( drawSurf_t* drawSurfs, int numDrawSurfs, int numTranspSur
cmd->shouldClearColor = shouldClearColor;
cmd->shouldDrawScene = shouldDrawScene;
Vector4Copy( clearColor, cmd->clearColor );
// full-screen non-portal view?
if(!tr.viewParms.isPortal &&
tr.viewParms.viewportX == 0 &&
tr.viewParms.viewportY == 0 &&
tr.viewParms.viewportWidth == glConfig.vidWidth &&
tr.viewParms.viewportHeight == glConfig.vidHeight)
{
// combined view-projection matrices
memcpy(tr.prevViewProjMatrix, tr.currViewProjMatrix, sizeof(tr.prevViewProjMatrix));
R_MultMatrix(tr.viewParms.world.modelMatrix, tr.viewParms.projectionMatrix, tr.currViewProjMatrix);
// separate view and projection matrices
memcpy(tr.prevViewMatrix, tr.currViewMatrix, sizeof(tr.prevViewMatrix));
memcpy(tr.prevProjMatrix, tr.currProjMatrix, sizeof(tr.prevProjMatrix));
memcpy(tr.currViewMatrix, tr.viewParms.world.modelMatrix, sizeof(tr.currViewMatrix));
memcpy(tr.currProjMatrix, tr.viewParms.projectionMatrix, sizeof(tr.currProjMatrix));
}
}

View file

@ -534,6 +534,8 @@ struct drawSurf_t {
int zppFirstIndex;
int zppIndexCount;
float radiusOverZ;
int uniqueEntityId;
float motionBlurScale;
};
void R_TessellateSurface( const surfaceType_t* surfType );
@ -1007,11 +1009,21 @@ typedef struct {
qbool shaderParseFailed;
int shaderParseNumWarnings;
// raytracing support
rtSurf_t rtSurfs[MAX_DRAWSURFS];
int numRTSurfs;
int sceneCounterRT;
trRefdef_t rtRefdef;
// from current and last frame's non-portal full-screen scene view
// needed for motion vectors
float currViewProjMatrix[16];
float prevViewProjMatrix[16];
float currViewMatrix[16];
float prevViewMatrix[16];
float currProjMatrix[16];
float prevProjMatrix[16];
} trGlobals_t;
extern backEndState_t backEnd;
@ -1141,7 +1153,7 @@ void R_AddMD3Surfaces( trRefEntity_t *e );
void R_AddPolygonSurfaces();
void R_AddDrawSurf( const surfaceType_t* surface, const shader_t* shader, int staticGeoChunk = 0, int zppFirstIndex = 0, int zppIndexCount = 0, float radiusOverZ = 666.0f );
void R_AddDrawSurf( const surfaceType_t* surface, const shader_t* shader, int uniqueEntityId, float mblurScale, int staticGeoChunk = 0, int zppFirstIndex = 0, int zppIndexCount = 0, float radiusOverZ = 666.0f );
void R_AddLitSurf( const surfaceType_t* surface, const shader_t* shader, int staticGeoChunk );
void R_AddRTSurf( const surfaceType_t* surface, const shader_t* shader );
uint64_t R_ComposeSort( int entityNum, const shader_t* shader, int staticGeoChunk );
@ -1411,7 +1423,7 @@ SCENE GENERATION
void R_ClearFrame();
void RE_ClearScene();
void RE_AddRefEntityToScene( const refEntity_t *ent, qbool intShaderTime );
void RE_AddRefEntityToScene( const refEntity_t *ent, refEntityVersion_t version );
void RE_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num );
void RE_AddLightToScene( const vec3_t org, float radius, float r, float g, float b );
void RE_RenderScene( const refdef_t *fd, int us );

View file

@ -1280,7 +1280,7 @@ static float SurfGreyscaleAmount( const shader_t* shader )
}
void R_AddDrawSurf( const surfaceType_t* surface, const shader_t* shader, int staticGeoChunk, int zppFirstIndex, int zppIndexCount, float radiusOverZ )
void R_AddDrawSurf( const surfaceType_t* surface, const shader_t* shader, int uniqueEntityId, float mblurScale, int staticGeoChunk, int zppFirstIndex, int zppIndexCount, float radiusOverZ )
{
if (tr.refdef.numDrawSurfs >= MAX_DRAWSURFS)
return;
@ -1295,6 +1295,8 @@ void R_AddDrawSurf( const surfaceType_t* surface, const shader_t* shader, int st
drawSurf->zppFirstIndex = zppFirstIndex;
drawSurf->zppIndexCount = zppIndexCount;
drawSurf->radiusOverZ = radiusOverZ;
drawSurf->uniqueEntityId = uniqueEntityId;
drawSurf->motionBlurScale = mblurScale;
}
@ -1644,7 +1646,7 @@ static void R_AddEntitySurfaces()
continue;
}
shader = R_GetShaderByHandle( ent->e.customShader );
R_AddDrawSurf( &entitySurface, shader );
R_AddDrawSurf( &entitySurface, shader, ent->e.uniqueId, ent->e.motionBlurScale );
R_AddRTSurf( &entitySurface, shader ); // @TODO: billboards need to be procedural geometry
break;
@ -1654,7 +1656,7 @@ static void R_AddEntitySurfaces()
tr.currentModel = R_GetModelByHandle( ent->e.hModel );
if (!tr.currentModel) {
R_AddDrawSurf( &entitySurface, tr.defaultShader );
R_AddDrawSurf( &entitySurface, tr.defaultShader, ent->e.uniqueId, ent->e.motionBlurScale );
} else {
switch ( tr.currentModel->type ) {
case MOD_MD3:
@ -1669,7 +1671,7 @@ static void R_AddEntitySurfaces()
case MOD_BAD: // null model axis
if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal)
break;
R_AddDrawSurf( &entitySurface, tr.defaultShader );
R_AddDrawSurf( &entitySurface, tr.defaultShader, ent->e.uniqueId, ent->e.motionBlurScale );
break;
default:
ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" );

View file

@ -287,7 +287,7 @@ void R_AddMD3Surfaces( trRefEntity_t* ent )
// don't add third_person objects if not viewing through a portal
if ( !personalModel ) {
if( !culled )
R_AddDrawSurf( (const surfaceType_t*)surface, shader );
R_AddDrawSurf( (const surfaceType_t*)surface, shader, ent->e.uniqueId, ent->e.motionBlurScale );
R_AddRTSurf( (const surfaceType_t*)surface, shader );
}

View file

@ -128,7 +128,7 @@ typedef struct {
// a scene is built up by calls to R_ClearScene and the various R_Add functions.
// Nothing is drawn until R_RenderScene is called.
void (*ClearScene)();
void (*AddRefEntityToScene)( const refEntity_t *re, qbool intShaderTime );
void (*AddRefEntityToScene)( const refEntity_t *re, refEntityVersion_t version );
void (*AddPolyToScene)( qhandle_t hShader, int numVerts, const polyVert_t *verts, int num );
qbool (*LightForPoint)( const vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir );
void (*AddLightToScene)( const vec3_t org, float radius, float r, float g, float b );

View file

@ -83,7 +83,7 @@ void R_AddPolygonSurfaces()
const srfPoly_t* poly = tr.refdef.polys;
for (int i = 0; i < tr.refdef.numPolys; ++i, ++poly) {
R_AddDrawSurf( (const surfaceType_t*)poly, R_GetShaderByHandle( poly->hShader ) );
R_AddDrawSurf( (const surfaceType_t*)poly, R_GetShaderByHandle( poly->hShader ), 0, 1.0f );
// @TODO: polygons are sometimes used to replace sprites (e.g. CPMA rocket smoke),
// they should be procedural geometry
R_AddRTSurf( (const surfaceType_t*)poly, R_GetShaderByHandle( poly->hShader ) );
@ -154,7 +154,7 @@ void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t* verts
///////////////////////////////////////////////////////////////
void RE_AddRefEntityToScene( const refEntity_t* ent, qbool intShaderTime )
void RE_AddRefEntityToScene( const refEntity_t* ent, refEntityVersion_t version )
{
if ( !tr.registered ) {
return;
@ -169,9 +169,15 @@ void RE_AddRefEntityToScene( const refEntity_t* ent, qbool intShaderTime )
}
trRefEntity_t* const trEnt = &backEndData->entities[r_numentities];
trEnt->e = *ent;
if ( version == REV_LATEST ) {
trEnt->e = *ent;
} else {
memcpy( &trEnt->e, ent, sizeof( refEntityBase_t ) );
trEnt->e.uniqueId = 0;
trEnt->e.motionBlurScale = 1.0f;
}
trEnt->lightingCalculated = qfalse;
trEnt->intShaderTime = intShaderTime;
trEnt->intShaderTime = version >= REV_INTSHADERTIME;
r_numentities++;
}

View file

@ -183,7 +183,7 @@ static qbool R_LightCullSurface( const surfaceType_t* surface, const dlight_t* d
}
static void R_AddWorldSurface( msurface_t* surf )
static void R_AddWorldSurface( msurface_t* surf, int uniqueId, float mblurScale )
{
if ( surf->vcBSP == tr.viewCount )
return; // already checked during this BSP walk
@ -210,7 +210,7 @@ static void R_AddWorldSurface( msurface_t* surf )
radiusOverZ = triangles->radius / max( dist, 0.001f );
}
R_AddDrawSurf( surf->data, surf->shader, surf->staticGeoChunk, surf->zppFirstIndex, surf->zppIndexCount, radiusOverZ );
R_AddDrawSurf( surf->data, surf->shader, uniqueId, mblurScale, surf->staticGeoChunk, surf->zppFirstIndex, surf->zppIndexCount, radiusOverZ );
}
@ -319,7 +319,7 @@ static void R_RecursiveWorldNode( mnode_t *node, int planeBits )
while (c--) {
// the surface may have already been added if it spans multiple leafs
msurface_t* surf = *mark;
R_AddWorldSurface( surf );
R_AddWorldSurface( surf, 0, 1.0f );
mark++;
}
@ -451,7 +451,7 @@ void R_AddBrushModelSurfaces( const trRefEntity_t* re )
return;
for ( int s = 0; s < bmodel->numSurfaces; ++s ) {
R_AddWorldSurface( bmodel->firstSurface + s );
R_AddWorldSurface( bmodel->firstSurface + s, re->e.uniqueId, re->e.motionBlurScale );
}
R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.orient );

View file

@ -413,7 +413,13 @@ void ProcessCRP()
CompilePixelShader("gbufferviz_depth.h", "gbufferviz_depth.hlsl", "gbufferviz_depth");
CompilePixelShader("gbufferviz_normal.h", "gbufferviz_normal.hlsl", "gbufferviz_normal");
CompilePixelShader("gbufferviz_position.h", "gbufferviz_position.hlsl", "gbufferviz_position");
CompilePixelShader("gbufferviz_motion.h", "gbufferviz_motion.hlsl", "gbufferviz_motion");
CompileGraphics("wireframe_normals.h", "wireframe_normals.hlsl", "wireframe_normals");
CompilePixelShader("skybox_motion.h", "skybox_motion.hlsl", "skybox_motion");
CompileCompute("mblur_tile_gen.h", "mblur_tile_gen.hlsl", "tile_gen");
CompileCompute("mblur_tile_max.h", "mblur_tile_max.hlsl", "tile_max");
CompilePixelShader("mblur_blur.h", "mblur_blur.hlsl", "blur");
CompilePixelShader("mblur_pack.h", "mblur_pack.hlsl", "pack");
}
int main(int /*argc*/, const char** argv)

View file

@ -135,6 +135,7 @@
<ClCompile Include="..\..\code\renderer\crp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
@ -231,6 +232,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_depth.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_motion.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_normal.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
@ -243,6 +247,18 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\magnifier.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_blur.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_pack.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_tile_gen.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_tile_max.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mip_1.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
@ -261,6 +277,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\skybox_motion.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>

View file

@ -39,6 +39,7 @@
<ClCompile Include="..\..\code\renderer\crp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
@ -135,6 +136,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_depth.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_motion.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_normal.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
@ -147,6 +151,18 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\magnifier.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_blur.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_pack.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_tile_gen.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_tile_max.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mip_1.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
@ -165,6 +181,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\skybox_motion.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>

View file

@ -137,6 +137,7 @@
<ClCompile Include="..\..\code\renderer\crp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
@ -233,6 +234,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_depth.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_motion.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_normal.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
@ -245,6 +249,18 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\magnifier.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_blur.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_pack.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_tile_gen.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_tile_max.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mip_1.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
@ -263,6 +279,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\skybox_motion.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<ExcludedFromBuild>true</ExcludedFromBuild>
</FxCompile>

View file

@ -39,6 +39,7 @@
<ClCompile Include="..\..\code\renderer\crp_geometry.cpp" />
<ClCompile Include="..\..\code\renderer\crp_magnifier.cpp" />
<ClCompile Include="..\..\code\renderer\crp_main.cpp" />
<ClCompile Include="..\..\code\renderer\crp_motion_blur.cpp" />
<ClCompile Include="..\..\code\renderer\crp_opaque.cpp" />
<ClCompile Include="..\..\code\renderer\crp_prepass.cpp" />
<ClCompile Include="..\..\code\renderer\crp_raytracing.cpp" />
@ -135,6 +136,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_depth.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_motion.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\gbufferviz_normal.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
@ -147,6 +151,18 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\magnifier.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_blur.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_pack.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_tile_gen.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mblur_tile_max.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\mip_1.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
@ -165,6 +181,9 @@
<FxCompile Include="..\..\code\renderer\shaders\crp\prepass.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\skybox_motion.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>
<FxCompile Include="..\..\code\renderer\shaders\crp\tone_map.hlsl">
<Filter>shaders\crp</Filter>
</FxCompile>