0
0
Fork 0
mirror of https://github.com/dhewm/dhewm3.git synced 2025-03-22 10:41:08 +00:00

Integrate (working!) soft particle shader, clean up that code a bit

turned out the whole problem with soft particles what that the vertex
shader used vertex.attrib[8] instead of vertex.texcoord (and with
nvidia drivers those are equivalent)

I integrated the shader source into the c++ code so I don't have to
ship glprogs/soft_particles.vfp
This commit is contained in:
Daniel Gibson 2024-06-17 18:16:36 +02:00
parent a407a6060f
commit 3c887d5af5
5 changed files with 127 additions and 31 deletions

View file

@ -239,6 +239,10 @@ idCVar r_screenshotPngCompression("r_screenshotPngCompression", "3", CVAR_RENDER
idCVar r_windowResizable("r_windowResizable", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Allow resizing (and maximizing) the window (needs SDL2; with 2.0.5 or newer it's applied immediately)" );
idCVar r_vidRestartAlwaysFull( "r_vidRestartAlwaysFull", 0, CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Always do a full vid_restart (ignore 'partial' argument), e.g. when changing window size" );
// DG: for soft particles (ported from TDM)
idCVar r_skipDepthCapture( "r_skipDepthCapture", "0", CVAR_RENDERER | CVAR_BOOL, "skip depth capture" ); // #3877
idCVar r_useSoftParticles( "r_useSoftParticles", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "soften particle transitions when player walks through them or they cross solid geometry" );
idCVar r_glDebugContext( "r_glDebugContext", "0", CVAR_RENDERER | CVAR_BOOL, "Enable OpenGL Debug context - requires vid_restart, needs SDL2" );
// define qgl functions

View file

@ -346,12 +346,96 @@ static progDef_t progs[MAX_GLPROGS] = {
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_GLASSWARP, "arbFP_glasswarp.txt" },
// SteveL #3878: Particle softening applied by the engine
{ GL_VERTEX_PROGRAM_ARB, VPROG_SOFT_PARTICLE, "soft_particle.vfp" }, // TODO: can we specify this in C++?
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_SOFT_PARTICLE, "soft_particle.vfp" },
{ GL_VERTEX_PROGRAM_ARB, VPROG_SOFT_PARTICLE, "soft_particle.vp" },
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_SOFT_PARTICLE, "soft_particle.fp" },
// additional programs can be dynamically specified in materials
};
// DG: the following two shaders are taken from TheDarkMod 2.04 (glprogs/soft_particle.vfp)
// (C) 2005-2016 Broken Glass Studios (The Dark Mod Team) and the individual authors
// released under a revised BSD license and GPLv3
const char* softpartVShader = "!!ARBvp1.0 \n"
"OPTION ARB_position_invariant; \n"
"# NOTE: unlike the TDM shader, the following lines use .texcoord and .color \n"
"# instead of .attrib[8] and .attrib[3], to make it work with non-nvidia drivers \n"
"MOV result.texcoord, vertex.texcoord; \n"
"MOV result.color, vertex.color; \n"
"END \n";
const char* softpartFShader = "!!ARBfp1.0 \n"
"# == Fragment Program == \n"
"# taken from The Dark Mod 2.04, adjusted for dhewm3 \n"
"# (C) 2005-2016 Broken Glass Studios (The Dark Mod Team) \n"
"# \n"
"# Input textures \n"
"# texture[0] particle diffusemap \n"
"# texture[1] _currentDepth \n"
"# \n"
"# Constants set by the engine: \n"
"# program.env[22] is reciprocal of _currentDepth size. Lets us convert a screen position to a texcoord in _currentDepth \n"
"# { 1.0f / depthtex.width, 1.0f / depthtex.height, float(depthtex.width)/int(depthtex.width), \n"
"# float(depthtex.height)/int(depthtex.height) } \n"
"# program.env[23] is the particle radius, given as { radius, 1/(fadeRange), 1/radius } \n"
"# fadeRange is the particle diameter for alpha blends (like smoke), but the particle radius for additive \n"
"# blends (light glares), because additive effects work differently. Fog is half as apparent when a wall \n"
"# is in the middle of it. Light glares lose no visibility when they have something to reflect off. \n"
"# program.env[24] is the color channel mask. Particles with additive blend need their RGB channels modified to blend them out. \n"
"# Particles with an alpha blend need their alpha channel modified. \n"
"# \n"
"# Hard-coded constants \n"
"# depth_consts allows us to recover the original depth in Doom units of anything in the depth \n"
"# buffer. Doom3's and thus TDM's projection matrix differs slightly from the classic projection matrix as \n"
"# it implements a \"nearly-infinite\" zFar. The matrix is hard-coded in the engine, so we use hard-coded \n"
"# constants here for efficiency. depth_consts is derived from the numbers in that matrix. \n"
"# \n"
"# next line: prevent dhewm3 from injecting gamma in shader code into this shader, \n"
"# because that looks bad when rendered with additive blending (gets too bright) \n"
"# nodhewm3gammahack \n"
"\n"
"PARAM depth_consts = { 0.33333333, -0.33316667, 0.0, 0.0 }; \n"
"PARAM particle_radius = program.env[23]; \n"
"TEMP tmp, scene_depth, particle_depth, near_fade, fade; \n"
"\n"
"# Map the fragment to a texcoord on our depth image, and sample to find scene_depth \n"
"MUL tmp.xy, fragment.position, program.env[22]; \n"
"TEX scene_depth, tmp, texture[1], 2D; \n"
"MIN scene_depth, scene_depth, 0.9994; # Required by TDM projection matrix. Equates to max recoverable \n"
" # depth of 30k units, which is enough. 0.9995 is infinite depth. \n"
" # This is needed only if there is caulk sky on show (which writes \n"
" # no depth, so leaves 1 in the depth texture). \n"
"\n"
"# Recover original depth in doom units \n"
"MAD tmp, scene_depth, depth_consts.x, depth_consts.y; \n"
"RCP scene_depth, tmp.x; \n"
"\n"
"# Convert particle depth to doom units too \n"
"MAD tmp, fragment.position.z, depth_consts.x, depth_consts.y; \n"
"RCP particle_depth, tmp.x; \n"
"\n"
"# Scale the depth difference by the particle diameter to calc an alpha \n"
"# value based on how much of the 3d volume represented by the particle \n"
"# is in front of the solid scene \n"
"ADD tmp, -scene_depth, particle_depth; # NB depth is negative. 0 at the eye, -100 at 100 units into the screen. \n"
"ADD tmp, tmp, particle_radius.x; # Add the radius so a depth difference of particle radius now equals 0 \n"
"MUL_SAT fade, tmp, particle_radius.y; # divide by the particle radius or diameter and clamp \n"
"\n"
"# Also fade if the particle is too close to our eye position, so it doesn't 'pop' in and out of view \n"
"# Start a linear fade at particle_radius distance from the particle. \n"
"MUL_SAT near_fade, particle_depth, -particle_radius.z; \n"
"\n"
"# Calculate final fade and apply the channel mask \n"
"MUL fade, near_fade, fade; \n"
"ADD_SAT fade, fade, program.env[24]; # saturate the channels that don't want modifying \n"
"\n"
"# Set the color. Multiply by vertex/fragment color as that's how the particle system fades particles in and out \n"
"TEMP oColor; \n"
"TEX oColor, fragment.texcoord, texture[0], 2D; \n"
"MUL oColor, oColor, fade; \n"
"MUL result.color, oColor, fragment.color; \n"
"\n"
"END \n";
/*
=================
R_LoadARBProgram
@ -395,27 +479,37 @@ static ID_INLINE bool isARBidentifierChar( int c ) {
void R_LoadARBProgram( int progIndex ) {
int ofs;
int err;
idStr fullPath = "glprogs/";
fullPath += progs[progIndex].name;
char *fileBuffer;
char *buffer;
char *start = NULL, *end;
common->Printf( "%s", fullPath.c_str() );
if ( progs[progIndex].ident == VPROG_SOFT_PARTICLE || progs[progIndex].ident == FPROG_SOFT_PARTICLE ) {
// these shaders are loaded directly from a string
common->Printf( "<internal> %s", progs[progIndex].name );
const char* srcstr = (progs[progIndex].ident == VPROG_SOFT_PARTICLE) ? softpartVShader : softpartFShader;
// load the program even if we don't support it, so
// fs_copyfiles can generate cross-platform data dumps
fileSystem->ReadFile( fullPath.c_str(), (void **)&fileBuffer, NULL );
if ( !fileBuffer ) {
common->Printf( ": File not found\n" );
return;
// copy to stack memory
buffer = (char *)_alloca( strlen( srcstr ) + 1 );
strcpy( buffer, srcstr );
} else {
idStr fullPath = "glprogs/";
fullPath += progs[progIndex].name;
char *fileBuffer;
common->Printf( "%s", fullPath.c_str() );
// load the program even if we don't support it, so
// fs_copyfiles can generate cross-platform data dumps
fileSystem->ReadFile( fullPath.c_str(), (void **)&fileBuffer, NULL );
if ( !fileBuffer ) {
common->Printf( ": File not found\n" );
return;
}
// copy to stack memory and free
buffer = (char *)_alloca( strlen( fileBuffer ) + 1 );
strcpy( buffer, fileBuffer );
fileSystem->FreeFile( fileBuffer );
}
// copy to stack memory and free
buffer = (char *)_alloca( strlen( fileBuffer ) + 1 );
strcpy( buffer, fileBuffer );
fileSystem->FreeFile( fileBuffer );
if ( !glConfig.isInitialized ) {
return;
}
@ -465,7 +559,7 @@ void R_LoadARBProgram( int progIndex ) {
// note that strlen("dhewm3tmpres") == strlen("result.color")
const char* tmpres = "TEMP dhewm3tmpres; # injected by dhewm3 for gamma correction\n";
// Note: program.env[4].xyz = r_brightness; program.env[4].w = 1.0/r_gamma
// Note: program.env[21].xyz = r_brightness; program.env[21].w = 1.0/r_gamma
// outColor.rgb = pow(dhewm3tmpres.rgb*r_brightness, vec3(1.0/r_gamma))
// outColor.a = dhewm3tmpres.a;
const char* extraLines =

View file

@ -34,8 +34,6 @@ If you have questions concerning this license or the applicable additional terms
extern idCVar r_useCarmacksReverse;
extern idCVar r_useStencilOpSeparate;
idCVar r_skipDepthCapture( "r_skipDepthCapture", "0", CVAR_RENDERER | CVAR_BOOL, "skip depth capture" ); // #3877
/*
=====================
RB_BakeTextureMatrixIntoTexgen
@ -952,13 +950,14 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
qglEnableClientState( GL_COLOR_ARRAY );
}
//GL_State( pStage->drawStateBits | GLS_DEPTHFUNC_ALWAYS ); // Disable depth clipping. The fragment program will
// handle it to allow overdraw.
#if 0 // debug stuff: render particles opaque so debug colors written in the shader are properly visible
int dsbits = pStage->drawStateBits | GLS_DEPTHFUNC_ALWAYS;
dsbits &= ~(GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS);
//dsbits |= GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO; both values are 0, so this would be a noop
GL_State( dsbits );
#endif
GL_State( pStage->drawStateBits | GLS_DEPTHFUNC_ALWAYS ); // Disable depth clipping. The fragment program will
// handle it to allow overdraw.
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_SOFT_PARTICLE );
qglEnable( GL_VERTEX_PROGRAM_ARB );
@ -972,6 +971,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_SOFT_PARTICLE );
qglEnable( GL_FRAGMENT_PROGRAM_ARB );
#if 0 // debug stuff
// Set up parameters for fragment program
const char* srcblendstr = "???";
if ( src_blend >= 0 && src_blend <= 9 ) {
@ -1006,14 +1006,15 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
#undef MY_CASE
}
//printf("XX mat: %s, src_blend = %s dest_blend = %s radius = %g\n", shader->GetName(), srcblendstr, dstblend, surf->particle_radius);
printf("XX mat: %s, src_blend = %s dest_blend = %s radius = %g\n", shader->GetName(), srcblendstr, dstblend, surf->particle_radius);
#endif
// program.env[5] is the particle radius, given as { radius, 1/(faderange), 1/radius }
// program.env[23] is the particle radius, given as { radius, 1/(faderange), 1/radius }
float fadeRange = 1.0f;
// fadeRange is the particle diameter for alpha blends (like smoke), but the particle radius for additive
// blends (light glares), because additive effects work differently. Fog is half as apparent when a wall
// is in the middle of it. Light glares lose no visibility when they have something to reflect off. See
// issue #3878 for diagram
// The Dark Mod issue #3878 for diagram
if ( src_blend == GLS_SRCBLEND_SRC_ALPHA ) // an alpha blend material
{
fadeRange = surf->particle_radius * 2.0f;
@ -1031,7 +1032,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
};
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_PARTICLE_RADIUS, parm );
// program.env[6] is the color channel mask. It gets added to the fade multiplier, so adding 1
// program.env[24] is the color channel mask. It gets added to the fade multiplier, so adding 1
// to a channel will make sure it doesn't get faded at all. Particles with additive blend
// need their RGB channels modifying to blend them out. Particles with an alpha blend need
// their alpha channel modifying.
@ -1045,8 +1046,6 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
parm[0] = parm[1] = parm[2] = 0.0f; // Fade the rgb channels but
parm[3] = 1.0f; // leave the alpha channel at full strength
}
//parm[0] = parm[1] = parm[2] = 1.0f; // XXX hack
//parm[3] = 1.0f; // XXX hack
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_PARTICLE_COLCHAN_MASK, parm );
// draw it

View file

@ -40,8 +40,6 @@ If you have questions concerning this license or the applicable additional terms
static const float CHECK_BOUNDS_EPSILON = 1.0f;
static idCVar r_useSoftParticles( "r_useSoftParticles", "1", CVAR_RENDERER | CVAR_BOOL, "soften particle transitions when player walks through them or they cross solid geometry" );
/*
===========================================================================================

View file

@ -985,6 +985,7 @@ extern idCVar r_debugRenderToTexture;
extern idCVar r_glDebugContext; // DG: use debug context to call logging callbacks on GL errors
extern idCVar r_skipDepthCapture; // DG: disable capturing depth buffer, used for soft particles
extern idCVar r_useSoftParticles;
/*
====================================================================