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
This commit is contained in:
myT 2020-04-26 16:32:51 +02:00
parent a59875200c
commit 718b966414
11 changed files with 264 additions and 105 deletions

View File

@ -13,12 +13,12 @@ add: r_backend <GL2|GL3|D3D11> (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 <scale> <bias> 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

View File

@ -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<float2> 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;
}

View File

@ -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;

View File

@ -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();
}
}

View File

@ -1422,7 +1422,7 @@ static void InitGLInfo()
else
glInfo.maxAnisotropy = 0;
glInfo.softSpriteSupport = qfalse;
glInfo.depthFadeSupport = qfalse;
glInfo.mipGenSupport = qfalse;
glInfo.alphaToCoverageSupport = qfalse;
}

View File

@ -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();
}
}

View File

@ -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" \

View File

@ -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 },

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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 <scale> <bias>
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();
}