From 718b966414467d0262ee85d58b647b7f9b5b6e23 Mon Sep 17 00:00:00 2001 From: myT Date: Sun, 26 Apr 2020 16:32:51 +0200 Subject: [PATCH] depth fade fixes and improvements - renamed r_softSprites to r_depthFade the term's more descriptive and it helps that UE4 uses it - fixed the GL3 fragment shader halving the depth bias - fixed the D3D11 pixel shader only fetching depth sample 0 not fixed for GL3 yet, see the code comments for that - added support for more blend states - added the q3map_cnq3_depthFade general shader directive --- changelog.txt | 13 ++- code/renderer/hlsl/sprite.hlsl | 34 ++++--- code/renderer/tr_backend.cpp | 10 +- code/renderer/tr_backend_d3d11.cpp | 34 +++---- code/renderer/tr_backend_gl2.cpp | 2 +- code/renderer/tr_backend_gl3.cpp | 76 +++++++++----- code/renderer/tr_help.h | 6 +- code/renderer/tr_init.cpp | 6 +- code/renderer/tr_local.h | 28 ++++-- code/renderer/tr_shade.cpp | 6 +- code/renderer/tr_shader.cpp | 154 ++++++++++++++++++++++++----- 11 files changed, 264 insertions(+), 105 deletions(-) diff --git a/changelog.txt b/changelog.txt index 5f416db..542ee80 100644 --- a/changelog.txt +++ b/changelog.txt @@ -13,12 +13,12 @@ add: r_backend (default: D3D11 on Windows, GL3 otherwise) select -----------------------|--------|--------|--------------------| MSAA | GL 3.0 | always | always | MSAA centroid sampling | never | always | always | - depth sprites | never | always | always | + depth fade | never | always | always | alpha to coverage AA | never | always | always | dithering | never | always | always | GPU mip-map generation | never | GL 4.3 | feature level 11.0 | -add: r_softSprite <0|1> (default: 1) enables depth sprites (soft particles) +add: r_depthFade <0|1> (default: 1) enables depth fade rendering this helps explosions, smoke, blood, etc. not having "sharp edges" when intersecting geometry add: r_alphaToCoverage <0|1> (default: 1) enables alpha to coverage anti-aliasing @@ -100,6 +100,15 @@ add: r_transpSort <0|1> (default: 0) to select the transparency sorting mode quite inconsistent based on the view angles and surface dimensions example: dropped items in the cpm18r acid with cg_simpleItems 1 +add: global shader directive q3map_cnq3_depthFade enables depth fade rendering + scale is the depth range over wich to fade the currently rendered surface + bias is a value added to the currently rendered surface's depth, positive goes towards the camera + whether this is enabled is decided by r_depthFade (must be 1) and r_backend (can't be GL2) + depth writes must be disabled in all stages (no shader stage can use the depthWrite directive) + the blend mode (blendFunc) must be the same for all stages and must be one of: + (one, one) (src_alpha, one) (one_minus_src_alpha, one) (dst_color, zero) (zero, src_color) + (src_alpha, one_minus_src_alpha) (one, one_minus_src_alpha) + add: r_mapBrightness now works on q3map2's external lightmap atlas images add: the renderer can now batch surfaces with different (but sufficiently similar) shaders diff --git a/code/renderer/hlsl/sprite.hlsl b/code/renderer/hlsl/sprite.hlsl index 053e95f..22fb5e9 100644 --- a/code/renderer/hlsl/sprite.hlsl +++ b/code/renderer/hlsl/sprite.hlsl @@ -39,6 +39,7 @@ struct VOut float4 position : SV_Position; float4 color : COLOR0; float2 texCoords : TEXCOORD0; + float2 proj2232 : TEXCOORD1; float depthVS : DEPTHVS; float clipDist : SV_ClipDistance0; }; @@ -51,6 +52,7 @@ VOut vs_main(VIn input) output.position = mul(projectionMatrix, positionVS); output.color = input.color; output.texCoords = input.texCoords; + output.proj2232 = float2(-projectionMatrix[2][2], projectionMatrix[2][3]); output.depthVS = -positionVS.z; output.clipDist = dot(positionVS, clipPlane); @@ -60,13 +62,11 @@ VOut vs_main(VIn input) cbuffer PixelShaderBuffer { uint alphaTest; - float proj22; - float proj32; - float additive; float distance; float offset; - uint dummy0; - uint dummy1; + float dummy; + float4 colorScale; + float4 colorBias; }; Texture2D texture0 : register(t0); @@ -74,7 +74,20 @@ SamplerState sampler0 : register(s0); Texture2DMS textureDepth; -float LinearDepth(float zwDepth) +/* +f = far clip plane distance +n = near clip plane distance +exp = exponential depth value (as stored in the Z-buffer) + + 2 * f * n B +linear(exp) = ----------------------- = ------- + (f + n) - exp * (f - n) exp - A + + f + n -2 * f * n +with A = ----- and B = ---------- + f - n f - n +*/ +float LinearDepth(float zwDepth, float proj22, float proj32) { return proj32 / (zwDepth - proj22); } @@ -88,7 +101,7 @@ float Contrast(float d, float power) return aboveHalf ? (1.0 - r) : r; } -float4 ps_main(VOut input) : SV_TARGET +float4 ps_main(VOut input, uint sampleIndex : SV_SampleIndex) : SV_TARGET { float4 r = input.color * texture0.Sample(sampler0, input.texCoords); if((alphaTest == 1 && r.a == 0.0) || @@ -96,12 +109,11 @@ float4 ps_main(VOut input) : SV_TARGET (alphaTest == 3 && r.a < 0.5)) discard; - float depthS = LinearDepth(textureDepth.Load(input.position.xy, 0).x); + float zwDepth = textureDepth.Load(input.position.xy, sampleIndex).x; + float depthS = LinearDepth(zwDepth, input.proj2232.x, input.proj2232.y); float depthP = input.depthVS - offset; float scale = Contrast((depthS - depthP) * distance, 2.0); - float scaleColor = max(scale, 1.0 - additive); - float scaleAlpha = max(scale, additive); - float4 r2 = float4(r.rgb * scaleColor, r.a * scaleAlpha); + float4 r2 = lerp(r * colorScale + colorBias, r, scale); return r2; } diff --git a/code/renderer/tr_backend.cpp b/code/renderer/tr_backend.cpp index d03c5d6..e5b0b69 100644 --- a/code/renderer/tr_backend.cpp +++ b/code/renderer/tr_backend.cpp @@ -187,7 +187,7 @@ static qbool AreShadersStillBatchable( const shader_t* a, const shader_t* b ) a->polygonOffset != b->polygonOffset || a->imgflags != b->imgflags || a->numStages != b->numStages || - a->softSprite != b->softSprite ) + a->dfType != b->dfType ) return qfalse; for ( int i = 0; i < a->numStages; ++i ) { @@ -258,10 +258,10 @@ static void RB_RenderDrawSurfList( const drawSurf_t* drawSurfs, int numDrawSurfs const shader_t* shaderPrev = shader; int entityNum; R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum ); - const qbool softSpriteChange = shader->softSprite != tess.softSprite; + const qbool depthFadeChange = shader->dfType != tess.depthFade; // detect and batch surfaces across different (but sufficiently similar) shaders - if ( !softSpriteChange && + if ( !depthFadeChange && oldEntityNum == ENTITYNUM_WORLD && entityNum == ENTITYNUM_WORLD && AreShadersStillBatchable( shaderPrev, shader ) ) { @@ -298,11 +298,11 @@ static void RB_RenderDrawSurfList( const drawSurf_t* drawSurfs, int numDrawSurfs // modern code just billboards in cgame and submits raw polys, all of which are // ENTITYNUM_WORLD and thus automatically take the "same sort" fast path - if ( !shader->entityMergable || ((sort ^ drawSurf->sort) & ~QSORT_ENTITYNUM_MASK) || softSpriteChange ) { + if ( !shader->entityMergable || ((sort ^ drawSurf->sort) & ~QSORT_ENTITYNUM_MASK) || depthFadeChange ) { if (shaderPrev) RB_EndSurface(); RB_BeginSurface( shader, fogNum ); - tess.softSprite = shader->softSprite; + tess.depthFade = shader->dfType; } sort = drawSurf->sort; diff --git a/code/renderer/tr_backend_d3d11.cpp b/code/renderer/tr_backend_d3d11.cpp index a188ea3..f9cf797 100644 --- a/code/renderer/tr_backend_d3d11.cpp +++ b/code/renderer/tr_backend_d3d11.cpp @@ -176,22 +176,21 @@ struct GenericPSData float dummy; }; -struct SoftSpriteVSData +struct DepthFadeVSData { float modelViewMatrix[16]; float projectionMatrix[16]; float clipPlane[4]; }; -struct SoftSpritePSData +struct DepthFadePSData { uint32_t alphaTest; // AlphaTest enum - float proj22; - float proj32; - float additive; float distance; float offset; - uint32_t dummy[2]; + float dummy; + float scale[4]; + float bias[4]; }; struct DynamicLightVSData @@ -691,17 +690,16 @@ static void UploadPendingShaderData() } else if(pid == PID_SOFT_SPRITE) { - SoftSpriteVSData vsData; - SoftSpritePSData psData; + DepthFadeVSData vsData; + DepthFadePSData psData; memcpy(vsData.modelViewMatrix, d3d.modelViewMatrix, sizeof(vsData.modelViewMatrix)); memcpy(vsData.projectionMatrix, d3d.projectionMatrix, sizeof(vsData.projectionMatrix)); memcpy(vsData.clipPlane, d3d.clipPlane, sizeof(vsData.clipPlane)); psData.alphaTest = d3d.alphaTest; - psData.proj22 = -vsData.projectionMatrix[2 * 4 + 2]; - psData.proj32 = vsData.projectionMatrix[3 * 4 + 2]; - psData.additive = tess.shader->softSprite == SST_ADDITIVE ? 1.0f : 0.0f; - psData.distance = tess.shader->softSpriteDistance; - psData.offset = tess.shader->softSpriteOffset; + memcpy(psData.scale, r_depthFadeScale[tess.shader->dfType], sizeof(psData.scale)); + memcpy(psData.bias, r_depthFadeBias[tess.shader->dfType], sizeof(psData.bias)); + psData.distance = tess.shader->dfInvDist; + psData.offset = tess.shader->dfBias; ResetShaderData(pipeline->vertexBuffer, &vsData, sizeof(vsData)); ResetShaderData(pipeline->pixelBuffer, &psData, sizeof(psData)); } @@ -1585,14 +1583,14 @@ static qbool GAL_Init() ZeroMemory(&vertexShaderBufferDesc, sizeof(vertexShaderBufferDesc)); vertexShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC; - vertexShaderBufferDesc.ByteWidth = sizeof(SoftSpriteVSData); + vertexShaderBufferDesc.ByteWidth = sizeof(DepthFadeVSData); vertexShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; vertexShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; D3D11_CreateBuffer(&vertexShaderBufferDesc, NULL, &d3d.pipelines[PID_SOFT_SPRITE].vertexBuffer, "soft sprite vertex shader buffer"); ZeroMemory(&pixelShaderBufferDesc, sizeof(pixelShaderBufferDesc)); pixelShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC; - pixelShaderBufferDesc.ByteWidth = sizeof(SoftSpritePSData); + pixelShaderBufferDesc.ByteWidth = sizeof(DepthFadePSData); pixelShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; pixelShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; D3D11_CreateBuffer(&pixelShaderBufferDesc, NULL, &d3d.pipelines[PID_SOFT_SPRITE].pixelBuffer, "soft sprite pixel shader buffer"); @@ -1693,7 +1691,7 @@ static qbool GAL_Init() glInfo.displayFrequency = 0; glInfo.maxAnisotropy = D3D11_REQ_MAXANISOTROPY; // @NOTE: D3D10_REQ_MAXANISOTROPY == D3D11_REQ_MAXANISOTROPY glInfo.maxTextureSize = MAX_GPU_TEXTURE_SIZE; - glInfo.softSpriteSupport = r_softSprites->integer == 1; + glInfo.depthFadeSupport = r_depthFade->integer == 1; glInfo.mipGenSupport = mipGenOK; glInfo.alphaToCoverageSupport = alphaToCoverageOK; @@ -2368,7 +2366,7 @@ static void DrawDynamicLight() DrawIndexed(tess.dlNumIndexes); } -static void DrawSoftSprite() +static void DrawDepthFade() { AppendVertexData(&d3d.indexBuffer, tess.indexes, tess.numIndexes); if(d3d.splitBufferOffsets) @@ -2421,7 +2419,7 @@ static void GAL_Draw(drawType_t type) else if(type == DT_SOFT_SPRITE) { ApplyPipeline(PID_SOFT_SPRITE); - DrawSoftSprite(); + DrawDepthFade(); } } diff --git a/code/renderer/tr_backend_gl2.cpp b/code/renderer/tr_backend_gl2.cpp index df4ec1a..9b22534 100644 --- a/code/renderer/tr_backend_gl2.cpp +++ b/code/renderer/tr_backend_gl2.cpp @@ -1422,7 +1422,7 @@ static void InitGLInfo() else glInfo.maxAnisotropy = 0; - glInfo.softSpriteSupport = qfalse; + glInfo.depthFadeSupport = qfalse; glInfo.mipGenSupport = qfalse; glInfo.alphaToCoverageSupport = qfalse; } diff --git a/code/renderer/tr_backend_gl3.cpp b/code/renderer/tr_backend_gl3.cpp index 9ddcbcd..152230e 100644 --- a/code/renderer/tr_backend_gl3.cpp +++ b/code/renderer/tr_backend_gl3.cpp @@ -172,8 +172,9 @@ enum SoftSpriteUniform SU_PROJECTION, SU_CLIP_PLANE, SU_ALPHA_TEST, - SU_PROJ22_32, - SU_ADD_DIST_OFFSET, + SU_DIST_OFFSET, + SU_COLOR_SCALE, + SU_COLOR_BIAS, SU_COUNT }; @@ -236,6 +237,10 @@ struct OpenGL3 AlphaTest alphaTest; qbool dlOpaque; float dlIntensity; + float depthFadeScale[4]; + float depthFadeBias[4]; + float depthFadeDist; + float depthFadeOffset; ArrayBuffer arrayBuffers[VB_COUNT]; ArrayBuffer indexBuffer; @@ -476,6 +481,7 @@ static const char* sprite_vs = "out vec2 texCoords1FS;\n" "out vec4 colorFS;\n" "out float depthVS;\n" +"out vec2 proj22_32;\n" "\n" "void main()\n" "{\n" @@ -485,6 +491,7 @@ static const char* sprite_vs = " texCoords1FS = texCoords1;\n" " colorFS = color;\n" " depthVS = -positionVS.z;\n" +" proj22_32 = vec2(-projection[2][2], projection[3][2]);\n" "}\n"; static const char* sprite_fs = @@ -492,17 +499,18 @@ static const char* sprite_fs = "uniform sampler2D texture2; // depth texture\n" "\n" "uniform uint alphaTest;\n" -"uniform vec2 proj22_32;\n" -"uniform vec3 addDistOffset;\n" -"#define proj22 proj22_32.x\n" -"#define proj32 proj22_32.y\n" -"#define additive addDistOffset.x\n" -"#define distance addDistOffset.y\n" -"#define offset addDistOffset.z\n" +"uniform vec2 distOffset;\n" +"uniform vec4 colorScale;\n" +"uniform vec4 colorBias;\n" +"#define distance distOffset.x\n" +"#define offset distOffset.y\n" "\n" "in vec2 texCoords1FS;\n" "in vec4 colorFS;\n" "in float depthVS;\n" +"in vec2 proj22_32;\n" +"#define proj22 proj22_32.x\n" +"#define proj32 proj22_32.y\n" "\n" "out vec4 fragColor;\n" "\n" @@ -530,11 +538,9 @@ static const char* sprite_fs = "\n" " float depthSRaw = texelFetch(texture2, ivec2(gl_FragCoord.xy), 0).r;\n" " float depthS = LinearDepth(depthSRaw * 2.0 - 1.0);\n" -" float depthP = depthVS - offset * 0.5;\n" +" float depthP = depthVS - offset;\n" " float scale = Contrast((depthS - depthP) * distance, 2.0);\n" -" float scaleColor = max(scale, 1.0 - additive);\n" -" float scaleAlpha = max(scale, additive);\n" -" vec4 r2 = vec4(r.rgb * scaleColor, r.a * scaleAlpha);\n" +" vec4 r2 = mix(r * colorScale + colorBias, r, scale);\n" " fragColor = r2;\n" "}\n"; @@ -1134,6 +1140,12 @@ static void ApplyPipeline(PipelineId pipelineId) if(pipelineId == PID_SOFT_SPRITE && gl.fbMSEnabled) { + // This is not how it should be done and will counter the benefits of MSAA. + // To do this right, we need to bind the FBO's depth attachment to the shader and for that, + // we need multi-sampled textures as FBO attachments instead of multi-sampled render buffers. + // We also need the shader to use gl_SampleID, which changes our minimum requirements. + // Because of all these changes and lack of testing time, + // I'll do the necessary changes after the 1.52 release to avoid problems. FBO_ResolveDepth(); FBO_Bind(); } @@ -1918,8 +1930,9 @@ static void Init() gl.pipelines[PID_SOFT_SPRITE].uniformNames[SU_PROJECTION] = "projection"; gl.pipelines[PID_SOFT_SPRITE].uniformNames[SU_CLIP_PLANE] = "clipPlane"; gl.pipelines[PID_SOFT_SPRITE].uniformNames[SU_ALPHA_TEST] = "alphaTest"; - gl.pipelines[PID_SOFT_SPRITE].uniformNames[SU_PROJ22_32] = "proj22_32"; - gl.pipelines[PID_SOFT_SPRITE].uniformNames[SU_ADD_DIST_OFFSET] = "addDistOffset"; + gl.pipelines[PID_SOFT_SPRITE].uniformNames[SU_DIST_OFFSET] = "distOffset"; + gl.pipelines[PID_SOFT_SPRITE].uniformNames[SU_COLOR_SCALE] = "colorScale"; + gl.pipelines[PID_SOFT_SPRITE].uniformNames[SU_COLOR_BIAS] = "colorBias"; gl.pipelines[PID_POST_PROCESS].uniformNames[PU_BRIGHT_GAMMA_GREY] = "brightGammaGrey"; @@ -1967,7 +1980,7 @@ static void Init() gl.errorMode = EM_FATAL; } - glInfo.softSpriteSupport = r_softSprites->integer == 1; + glInfo.depthFadeSupport = r_depthFade->integer == 1; gl.pipelineId = PID_COUNT; ApplyPipeline(PID_GENERIC); @@ -2008,7 +2021,7 @@ static void InitGLInfo() glInfo.maxAnisotropy = 0; } - glInfo.softSpriteSupport = qfalse; + glInfo.depthFadeSupport = qfalse; glInfo.mipGenSupport = qfalse; glInfo.alphaToCoverageSupport = qfalse; } @@ -2269,14 +2282,13 @@ static void DrawDynamicLight() DrawElements(tess.dlNumIndexes); } -static void DrawSoftSprite() +static void DrawDepthFade() { Pipeline* const pipeline = &gl.pipelines[PID_SOFT_SPRITE]; if(pipeline->uniformsDirty[SU_PROJECTION]) { glUniformMatrix4fv(pipeline->uniformLocations[SU_PROJECTION], 1, GL_FALSE, gl.projectionMatrix); - glUniform2f(pipeline->uniformLocations[SU_PROJ22_32], -gl.projectionMatrix[2 * 4 + 2], gl.projectionMatrix[3 * 4 + 2]); } if(pipeline->uniformsDirty[SU_MODELVIEW]) { @@ -2286,10 +2298,26 @@ static void DrawSoftSprite() { glUniform4fv(pipeline->uniformLocations[SU_CLIP_PLANE], 1, gl.clipPlane); } - glUniform3f(pipeline->uniformLocations[SU_ADD_DIST_OFFSET], - tess.shader->softSprite == SST_ADDITIVE ? 1.0f : 0.0f, - tess.shader->softSpriteDistance, - tess.shader->softSpriteOffset); + if(pipeline->uniformsDirty[SU_COLOR_SCALE] || + memcmp(gl.depthFadeScale, r_depthFadeScale[tess.shader->dfType], sizeof(gl.depthFadeScale)) != 0) + { + glUniform4fv(pipeline->uniformLocations[SU_COLOR_SCALE], 1, r_depthFadeScale[tess.shader->dfType]); + memcpy(gl.depthFadeScale, r_depthFadeScale[tess.shader->dfType], sizeof(gl.depthFadeScale)); + } + if(pipeline->uniformsDirty[SU_COLOR_BIAS] || + memcmp(gl.depthFadeBias, r_depthFadeBias[tess.shader->dfType], sizeof(gl.depthFadeBias)) != 0) + { + glUniform4fv(pipeline->uniformLocations[SU_COLOR_BIAS], 1, r_depthFadeBias[tess.shader->dfType]); + memcpy(gl.depthFadeBias, r_depthFadeBias[tess.shader->dfType], sizeof(gl.depthFadeBias)); + } + if(pipeline->uniformsDirty[SU_DIST_OFFSET] || + tess.shader->dfInvDist != gl.depthFadeDist || + tess.shader->dfBias != gl.depthFadeOffset) + { + glUniform2f(pipeline->uniformLocations[SU_DIST_OFFSET], tess.shader->dfInvDist, tess.shader->dfBias); + gl.depthFadeDist = tess.shader->dfInvDist; + gl.depthFadeOffset = tess.shader->dfBias; + } UploadVertexArray(VB_POSITION, tess.xyz); @@ -2334,7 +2362,7 @@ static void GAL_Draw(drawType_t type) else if(type == DT_SOFT_SPRITE) { ApplyPipeline(PID_SOFT_SPRITE); - DrawSoftSprite(); + DrawDepthFade(); } } diff --git a/code/renderer/tr_help.h b/code/renderer/tr_help.h index 1d9125d..63e8522 100644 --- a/code/renderer/tr_help.h +++ b/code/renderer/tr_help.h @@ -140,9 +140,9 @@ S_COLOR_VAL " T2 " S_COLOR_HELP "= Tent 2 (1/3 2/3, same as the CPU version) "While " S_COLOR_VAL "1.0 " S_COLOR_HELP "will match the game's original look,\n" \ "higher values will better preserve contrast." -#define help_r_softSprites \ -"enables depth sprites\n" \ -"With it, sprites like blood, smoke, etc don't 'cut' through geometry sharply." +#define help_r_depthFade \ +"enables depth fade rendering\n" \ +"With it, surfaces like blood, smoke, etc don't 'cut' through geometry sharply." #define help_r_gpuMipGen \ "enables GPU mip-map generation\n" \ diff --git a/code/renderer/tr_init.cpp b/code/renderer/tr_init.cpp index 417cad1..d0c3574 100644 --- a/code/renderer/tr_init.cpp +++ b/code/renderer/tr_init.cpp @@ -64,7 +64,7 @@ cvar_t *r_lightmapGreyscale; cvar_t *r_novis; cvar_t *r_nocull; cvar_t *r_nocurves; -cvar_t *r_softSprites; +cvar_t *r_depthFade; cvar_t *r_gpuMipGen; cvar_t *r_alphaToCoverage; cvar_t *r_dither; @@ -319,7 +319,7 @@ void GfxInfo_f( void ) ri.Printf( PRINT_ALL, "Renderer: %s\n", glConfig.renderer_string ); if ( glConfig.version_string[0] != '\0' ) ri.Printf( PRINT_ALL, "OpenGL version: %s\n", glConfig.version_string ); - ri.Printf( PRINT_ALL, "Soft sprites : %s\n", glInfo.softSpriteSupport ? "ON" : "OFF" ); + ri.Printf( PRINT_ALL, "Depth fade : %s\n", glInfo.depthFadeSupport ? "ON" : "OFF" ); ri.Printf( PRINT_ALL, "Alpha to coverage : %s\n", glInfo.alphaToCoverageSupport ? "ON" : "OFF" ); ri.Printf( PRINT_ALL, "GPU mip-map generation: %s\n", glInfo.mipGenSupport ? "ON" : "OFF" ); gal.PrintInfo(); @@ -382,7 +382,7 @@ static const cvarTableItem_t r_cvars[] = { &r_fullbright, "r_fullbright", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_fullbright }, { &r_lightmap, "r_lightmap", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_lightmap }, { &r_lightmapGreyscale, "r_lightmapGreyscale", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_FLOAT, "0", "1", "how monochrome the lightmap looks" }, - { &r_softSprites, "r_softSprites", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_softSprites }, + { &r_depthFade, "r_depthFade", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_depthFade }, { &r_gpuMipGen, "r_gpuMipGen", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_gpuMipGen }, { &r_alphaToCoverage, "r_alphaToCoverage", "1", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_alphaToCoverage }, { &r_dither, "r_dither", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, help_r_dither }, diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index dfa6de8..37242c7 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -357,10 +357,17 @@ typedef struct { } fogParms_t; typedef enum { - SST_NONE, // disabled - SST_BLEND, // blend pass -> modulate alpha - SST_ADDITIVE // additive pass -> modulate color -} softSpriteType_t; + DFT_NONE, // disabled + DFT_BLEND, // std alpha blend -> fade color = (R G B 0) + DFT_ADD, // additive -> fade color = (0 0 0 A) + DFT_MULT, // multiplicative -> fade color = (1 1 1 A) + DFT_PMA, // pre-mult alpha -> fade color = (0 0 0 0) + DFT_TBD, // to be determined, i.e. fix up later + DFT_COUNT +} depthFadeType_t; + +extern const float r_depthFadeScale[DFT_COUNT][4]; +extern const float r_depthFadeBias [DFT_COUNT][4]; struct shader_t { @@ -411,9 +418,10 @@ struct shader_t { vec2_t lmScale; vec2_t lmBias; - softSpriteType_t softSprite; - float softSpriteDistance; - float softSpriteOffset; + // depth fade rendering settings + depthFadeType_t dfType; + float dfInvDist; + float dfBias; shader_t* next; }; @@ -1006,7 +1014,7 @@ extern cvar_t *r_transpSort; // transparency sorting mode extern cvar_t *r_lightmap; // render lightmaps only extern cvar_t *r_lightmapGreyscale; // how monochrome the lightmap looks extern cvar_t *r_fullbright; // avoid lightmap pass -extern cvar_t *r_softSprites; // draws certain surfaces as depth particles +extern cvar_t *r_depthFade; // fades marked shaders based on depth extern cvar_t *r_gpuMipGen; // uses GPU-side mip-map generation extern cvar_t *r_alphaToCoverage; // enables A2C on alpha-tested surfaces extern cvar_t *r_dither; // enables dithering @@ -1256,7 +1264,7 @@ struct shaderCommands_t const shaderStage_t** xstages; // when > 0, only soft sprites are allowed in this batch - softSpriteType_t softSprite; + depthFadeType_t depthFade; // when qtrue, RB_EndSurface doesn't need to compute deforms, colors, texture coordinates qbool deformsPreApplied; @@ -1629,7 +1637,7 @@ struct glinfo_t { // used by renderer int maxTextureSize; int maxAnisotropy; - qbool softSpriteSupport; + qbool depthFadeSupport; qbool mipGenSupport; qbool alphaToCoverageSupport; }; diff --git a/code/renderer/tr_shade.cpp b/code/renderer/tr_shade.cpp index 7f3a0ae..9b41067 100644 --- a/code/renderer/tr_shade.cpp +++ b/code/renderer/tr_shade.cpp @@ -45,7 +45,7 @@ void RB_BeginSurface( const shader_t* shader, int fogNum ) tess.shader = shader; tess.fogNum = fogNum; tess.xstages = (const shaderStage_t**)shader->stages; - tess.softSprite = SST_NONE; + tess.depthFade = DFT_NONE; tess.deformsPreApplied = qfalse; tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; @@ -121,7 +121,7 @@ static void RB_DrawDynamicLight() static void RB_DrawGeneric() { - if (tess.softSprite == SST_NONE && tess.fogNum && tess.shader->fogPass) { + if (tess.depthFade == DFT_NONE && tess.fogNum && tess.shader->fogPass) { tess.drawFog = qtrue; unsigned int fogStateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; @@ -142,7 +142,7 @@ static void RB_DrawGeneric() backEnd.pc[RB_BATCHES]++; backEnd.pc[RB_VERTICES] += tess.numVertexes; backEnd.pc[RB_INDICES] += tess.numIndexes; - gal.Draw(tess.softSprite != SST_NONE ? DT_SOFT_SPRITE : DT_GENERIC); + gal.Draw(tess.depthFade != DFT_NONE ? DT_SOFT_SPRITE : DT_GENERIC); } diff --git a/code/renderer/tr_shader.cpp b/code/renderer/tr_shader.cpp index 7a1b396..c8f8a10 100644 --- a/code/renderer/tr_shader.cpp +++ b/code/renderer/tr_shader.cpp @@ -23,6 +23,26 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // tr_shader.c -- this file deals with the parsing and definition of shaders +const float r_depthFadeScale[DFT_COUNT][4] = +{ + { 0.0f, 0.0f, 0.0f, 0.0f }, // DFT_NONE + { 1.0f, 1.0f, 1.0f, 0.0f }, // DFT_BLEND + { 0.0f, 0.0f, 0.0f, 1.0f }, // DFT_ADD + { 0.0f, 0.0f, 0.0f, 1.0f }, // DFT_MULT + { 0.0f, 0.0f, 0.0f, 0.0f }, // DFT_PMA + { 0.0f, 0.0f, 0.0f, 0.0f } // DFT_TBD +}; + +const float r_depthFadeBias[DFT_COUNT][4] = +{ + { 0.0f, 0.0f, 0.0f, 0.0f }, // DFT_NONE + { 0.0f, 0.0f, 0.0f, 0.0f }, // DFT_BLEND + { 0.0f, 0.0f, 0.0f, 0.0f }, // DFT_ADD + { 1.0f, 1.0f, 1.0f, 0.0f }, // DFT_MULT + { 0.0f, 0.0f, 0.0f, 0.0f }, // DFT_PMA + { 0.0f, 0.0f, 0.0f, 0.0f } // DFT_TBD +}; + static char* s_shaderText = 0; // the shader is parsed into these global variables, then copied into @@ -1265,6 +1285,36 @@ static void ParseSurfaceParm( const char** text ) } +// q3map_cnq3_depthFade + +static void ParseDepthFade( const char** text ) +{ + const char* token = COM_ParseExt( text, qfalse ); + float scale; + if ( token[0] == '\0' || sscanf( token, "%f", &scale ) != 1 || scale <= 0.0f ) { + ri.Printf( PRINT_WARNING, + "WARNING: invalid/missing depth fade scale argument '%s' in shader '%s'! " + "Ignoring the directive completely.\n", + token, shader.name ); + return; + } + + token = COM_ParseExt( text, qfalse ); + float bias; + if ( token[0] == '\0' || sscanf( token, "%f", &bias ) != 1 ) { + ri.Printf( PRINT_WARNING, + "WARNING: invalid/missing depth fade bias argument '%s' in shader '%s'! " + "Ignoring the directive completely.\n", + token, shader.name ); + return; + } + + shader.dfType = DFT_TBD; + shader.dfInvDist = 1.0f / scale; + shader.dfBias = bias; +} + + // the current text pointer is at the explicit text definition of the shader. // parse it into the global shader variable. later functions will optimize it. @@ -1330,6 +1380,10 @@ static qbool ParseShader( const char** text ) shader.clampTime = atof(token); } } + else if ( !Q_stricmp( token, "q3map_cnq3_depthFade" ) ) { + ParseDepthFade( text ); + continue; + } // skip stuff that only the q3map needs else if ( !Q_stricmpn( token, "q3map", 5 ) ) { SkipRestOfLine( text ); @@ -1866,15 +1920,36 @@ static void VertexLightingCollapse( void ) { } -static qbool IsAdditiveBlend() +static qbool IsAdditiveBlendDepthFade() { for (int i = 0; i < shader.numStages; ++i) { if (!stages[i].active) continue; - const int bits = stages[i].stateBits; - if ((bits & GLS_SRCBLEND_BITS) != GLS_SRCBLEND_ONE || - (bits & GLS_DSTBLEND_BITS) != GLS_DSTBLEND_ONE || + const unsigned int bits = stages[i].stateBits; + const unsigned int src = bits & GLS_SRCBLEND_BITS; + const unsigned int dst = bits & GLS_DSTBLEND_BITS; + if ((src != GLS_SRCBLEND_ONE && src != GLS_SRCBLEND_SRC_ALPHA && src != GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA) || + dst != GLS_DSTBLEND_ONE || + (bits & GLS_DEPTHMASK_TRUE) != 0) + return qfalse; + } + + return qtrue; +} + + +static qbool IsNormalBlendDepthFade() +{ + for (int i = 0; i < shader.numStages; ++i) { + if (!stages[i].active) + continue; + + const unsigned int bits = stages[i].stateBits; + const unsigned int src = bits & GLS_SRCBLEND_BITS; + const unsigned int dst = bits & GLS_DSTBLEND_BITS; + if (src != GLS_SRCBLEND_SRC_ALPHA || + dst != GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA || (bits & GLS_DEPTHMASK_TRUE) != 0) return qfalse; } @@ -1883,15 +1958,37 @@ static qbool IsAdditiveBlend() } -static qbool IsNormalBlend() +static qbool IsMultiplicativeBlendDepthFade() { for (int i = 0; i < shader.numStages; ++i) { if (!stages[i].active) continue; - const int bits = stages[i].stateBits; - if ((bits & GLS_SRCBLEND_BITS) != GLS_SRCBLEND_SRC_ALPHA || - (bits & GLS_DSTBLEND_BITS) != GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA || + const unsigned int bits = stages[i].stateBits; + const unsigned int src = bits & GLS_SRCBLEND_BITS; + const unsigned int dst = bits & GLS_DSTBLEND_BITS; + const qbool multA = src == GLS_SRCBLEND_DST_COLOR && dst == GLS_DSTBLEND_ZERO; + const qbool multB = src == GLS_SRCBLEND_ZERO && dst == GLS_DSTBLEND_SRC_COLOR; + if ((!multA && !multB) || + (bits & GLS_DEPTHMASK_TRUE) != 0) + return qfalse; + } + + return qtrue; +} + + +static qbool IsPreMultAlphaBlendDepthFade() +{ + for (int i = 0; i < shader.numStages; ++i) { + if (!stages[i].active) + continue; + + const unsigned int bits = stages[i].stateBits; + const unsigned int src = bits & GLS_SRCBLEND_BITS; + const unsigned int dst = bits & GLS_DSTBLEND_BITS; + if (src != GLS_SRCBLEND_ONE || + dst != GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA || (bits & GLS_DEPTHMASK_TRUE) != 0) return qfalse; } @@ -1900,7 +1997,7 @@ static qbool IsNormalBlend() } -static void ProcessSoftSprite() +static void ProcessDepthFade() { struct ssShader { const char* name; @@ -1935,9 +2032,10 @@ static void ProcessSoftSprite() { "shotgunSmokePuffNPM", 8.0f } }; - shader.softSprite = SST_NONE; + const qbool shaderEnabled = shader.dfType == DFT_TBD; + shader.dfType = DFT_NONE; - if (!glInfo.softSpriteSupport) + if (!glInfo.depthFadeSupport) return; if (shader.sort <= SS_OPAQUE) @@ -1952,22 +2050,28 @@ static void ProcessSoftSprite() if (activeStages <= 0) return; - qbool found = qfalse; - for (int i = 0; i < ARRAY_LEN(ssShaders); ++i) { - if (!Q_stricmp(shader.name, ssShaders[i].name)) { - shader.softSpriteDistance = 1.0f / ssShaders[i].distance; - shader.softSpriteOffset = ssShaders[i].offset; - found = qtrue; - break; + if (!shaderEnabled) { + qbool found = qfalse; + for (int i = 0; i < ARRAY_LEN(ssShaders); ++i) { + if (!Q_stricmp(shader.name, ssShaders[i].name)) { + shader.dfInvDist = 1.0f / ssShaders[i].distance; + shader.dfBias = ssShaders[i].offset; + found = qtrue; + break; + } } + if (!found) + return; } - if (!found) - return; - if (IsAdditiveBlend()) - shader.softSprite = SST_ADDITIVE; - else if (IsNormalBlend()) - shader.softSprite = SST_BLEND; + if (IsAdditiveBlendDepthFade()) + shader.dfType = DFT_ADD; + else if (IsNormalBlendDepthFade()) + shader.dfType = DFT_BLEND; + else if (IsMultiplicativeBlendDepthFade()) + shader.dfType = DFT_MULT; + else if (IsPreMultAlphaBlendDepthFade()) + shader.dfType = DFT_PMA; } @@ -2192,7 +2296,7 @@ static shader_t* FinishShader() shader.sort = SS_FOG; } - ProcessSoftSprite(); + ProcessDepthFade(); return GeneratePermanentShader(); }