cvar_t r_shadow_bouncegrid = {CVAR_SAVE, "r_shadow_bouncegrid", "0", "perform particle tracing for indirect lighting (Global Illumination / radiosity) using a 3D texture covering the scene, only active on levels with realtime lights active (r_shadow_realtime_world is usually required for these)"};
cvar_t r_shadow_bouncegrid_blur = {CVAR_SAVE, "r_shadow_bouncegrid_blur", "1", "apply a 1-radius blur on bouncegrid to denoise it and deal with boundary issues with surfaces"};
cvar_t r_shadow_bouncegrid_bounceanglediffuse = {CVAR_SAVE, "r_shadow_bouncegrid_bounceanglediffuse", "0", "use random bounce direction rather than true reflection, makes some corner areas dark"};
+cvar_t r_shadow_bouncegrid_dynamic_bounceminimumintensity = { CVAR_SAVE, "r_shadow_bouncegrid_dynamic_bounceminimumintensity", "0.01", "stop bouncing once intensity drops below this fraction of the original particle color" };
cvar_t r_shadow_bouncegrid_dynamic_culllightpaths = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_culllightpaths", "1", "skip accumulating light in the bouncegrid texture where the light paths are out of view (dynamic mode only)"};
cvar_t r_shadow_bouncegrid_dynamic_dlightparticlemultiplier = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_dlightparticlemultiplier", "1", "if set to a high value like 16 this can make dlights look great, but 0 is recommended for performance reasons"};
cvar_t r_shadow_bouncegrid_dynamic_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_directionalshading", "0", "use diffuse shading rather than ambient, 3D texture becomes 8x as many pixels to hold the additional data"};
cvar_t r_shadow_bouncegrid_sortlightpaths = {CVAR_SAVE, "r_shadow_bouncegrid_sortlightpaths", "1", "sort light paths before accumulating them into the bouncegrid texture, this reduces cpu cache misses"};
cvar_t r_shadow_bouncegrid_lightpathsize = {CVAR_SAVE, "r_shadow_bouncegrid_lightpathsize", "1", "width of the light path for accumulation of light in the bouncegrid texture"};
cvar_t r_shadow_bouncegrid_static = {CVAR_SAVE, "r_shadow_bouncegrid_static", "1", "use static radiosity solution (high quality) rather than dynamic (splotchy)"};
+cvar_t r_shadow_bouncegrid_static_bounceminimumintensity = { CVAR_SAVE, "r_shadow_bouncegrid_static_bounceminimumintensity", "0.01", "stop bouncing once intensity drops below this fraction of the original particle color" };
cvar_t r_shadow_bouncegrid_static_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_static_directionalshading", "1", "whether to use directionalshading when in static mode"};
cvar_t r_shadow_bouncegrid_static_energyperphoton = {CVAR_SAVE, "r_shadow_bouncegrid_static_energyperphoton", "10000", "amount of light that one photon should represent in static mode"};
cvar_t r_shadow_bouncegrid_static_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_static_lightradiusscale", "10", "particles stop at this fraction of light radius (can be more than 1) when in static mode"};
Cvar_RegisterVariable(&r_shadow_bouncegrid);
Cvar_RegisterVariable(&r_shadow_bouncegrid_blur);
Cvar_RegisterVariable(&r_shadow_bouncegrid_bounceanglediffuse);
+ Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_bounceminimumintensity);
Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_culllightpaths);
Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_directionalshading);
Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_dlightparticlemultiplier);
Cvar_RegisterVariable(&r_shadow_bouncegrid_particleintensity);
Cvar_RegisterVariable(&r_shadow_bouncegrid_sortlightpaths);
Cvar_RegisterVariable(&r_shadow_bouncegrid_static);
- Cvar_RegisterVariable(&r_shadow_bouncegrid_static_spacing);
+ Cvar_RegisterVariable(&r_shadow_bouncegrid_static_bounceminimumintensity);
Cvar_RegisterVariable(&r_shadow_bouncegrid_static_directionalshading);
+ Cvar_RegisterVariable(&r_shadow_bouncegrid_static_energyperphoton);
Cvar_RegisterVariable(&r_shadow_bouncegrid_static_lightradiusscale);
Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxbounce);
Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxphotons);
- Cvar_RegisterVariable(&r_shadow_bouncegrid_static_energyperphoton);
+ Cvar_RegisterVariable(&r_shadow_bouncegrid_static_spacing);
Cvar_RegisterVariable(&r_coronas);
Cvar_RegisterVariable(&r_coronas_occlusionsizescale);
Cvar_RegisterVariable(&r_coronas_occlusionquery);
{
qboolean s = r_shadow_bouncegrid_static.integer != 0;
float spacing = s ? r_shadow_bouncegrid_static_spacing.value : r_shadow_bouncegrid_dynamic_spacing.value;
+ float bounceminimumintensity = s ? r_shadow_bouncegrid_static_bounceminimumintensity.value : r_shadow_bouncegrid_dynamic_bounceminimumintensity.value;
// prevent any garbage in alignment padded areas as we'll be using memcmp
memset(settings, 0, sizeof(*settings));
settings->lightradiusscale = (s ? r_shadow_bouncegrid_static_lightradiusscale.value : r_shadow_bouncegrid_dynamic_lightradiusscale.value);
settings->maxbounce = (s ? r_shadow_bouncegrid_static_maxbounce.integer : r_shadow_bouncegrid_dynamic_maxbounce.integer);
settings->particlebounceintensity = r_shadow_bouncegrid_particlebounceintensity.value;
- settings->particleintensity = r_shadow_bouncegrid_particleintensity.value * 16384.0f * (settings->directionalshading ? 4.0f : 1.0f) / (spacing * spacing);
+ settings->particleintensity = r_shadow_bouncegrid_particleintensity.value * (settings->directionalshading ? 4.0f : 1.0f) * 16384 / (spacing * spacing) / 262144.0f;
settings->maxphotons = s ? r_shadow_bouncegrid_static_maxphotons.integer : r_shadow_bouncegrid_dynamic_maxphotons.integer;
- settings->energyperphoton = s ? r_shadow_bouncegrid_static_energyperphoton.integer : r_shadow_bouncegrid_dynamic_energyperphoton.integer;
+ settings->energyperphoton = s ? r_shadow_bouncegrid_static_energyperphoton.value : r_shadow_bouncegrid_dynamic_energyperphoton.value;
settings->spacing[0] = spacing;
settings->spacing[1] = spacing;
settings->spacing[2] = spacing;
settings->stablerandom = s ? 1 : r_shadow_bouncegrid_dynamic_stablerandom.integer;
+ settings->bounceminimumintensity2 = bounceminimumintensity * bounceminimumintensity;
// bound the values for sanity
settings->maxphotons = bound(1, settings->maxphotons, 25000000);
Matrix4x4_FromArrayFloatD3D(&r_shadow_bouncegrid_state.matrix, m);
}
-#define MAXBOUNCEGRIDPARTICLESPERLIGHT 1048576
-
// enumerate world rtlights and sum the overall amount of light in the world,
// from that we can calculate a scaling factor to fairly distribute photons
// to all the lights
//
// this modifies rtlight->photoncolor and rtlight->photons
-static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *settings, unsigned int range, unsigned int range1, unsigned int range2, int flag, float *photonscaling)
+static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *settings, unsigned int range, unsigned int range1, unsigned int range2, int flag)
{
float normalphotonscaling;
- float maxphotonscaling;
+ float photonscaling;
+ float photonintensity;
float photoncount = 0.0f;
float lightintensity;
float radius;
unsigned int lightindex;
dlight_t *light;
rtlight_t *rtlight;
+ normalphotonscaling = 1.0f / max(0.0001f, settings->energyperphoton);
for (lightindex = 0;lightindex < range2;lightindex++)
{
if (lightindex < range)
{
- light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+ light = (dlight_t *)Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
if (!light)
continue;
rtlight = &light->rtlight;
- VectorClear(rtlight->photoncolor);
- rtlight->photons = 0;
+ VectorClear(rtlight->bouncegrid_photoncolor);
+ rtlight->bouncegrid_photons = 0;
+ rtlight->bouncegrid_hits = 0;
+ rtlight->bouncegrid_traces = 0;
+ rtlight->bouncegrid_effectiveradius = 0;
if (!(light->flags & flag))
continue;
if (settings->staticmode)
if (rtlight->style > 0 && r_shadow_bouncegrid.integer != 2)
continue;
}
+ else if (r_shadow_debuglight.integer >= 0 && (int)lightindex != r_shadow_debuglight.integer)
+ continue;
}
else
{
rtlight = r_refdef.scene.lights[lightindex - range];
- VectorClear(rtlight->photoncolor);
- rtlight->photons = 0;
+ VectorClear(rtlight->bouncegrid_photoncolor);
+ rtlight->bouncegrid_photons = 0;
+ rtlight->bouncegrid_hits = 0;
+ rtlight->bouncegrid_traces = 0;
+ rtlight->bouncegrid_effectiveradius = 0;
}
// draw only visible lights (major speedup)
radius = rtlight->radius * settings->lightradiusscale;
if (rtlight->radius == 0.0f || VectorLength2(rtlight->color) == 0.0f)
continue;
w *= ((rtlight->style >= 0 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1);
- VectorScale(rtlight->color, w, rtlight->photoncolor);
+ VectorScale(rtlight->color, w, rtlight->bouncegrid_photoncolor);
// skip lights that will emit no photons
- if (!VectorLength2(rtlight->photoncolor))
+ if (!VectorLength2(rtlight->bouncegrid_photoncolor))
continue;
// shoot particles from this light
// use a calculation for the number of particles that will not
lightintensity = VectorLength(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
if (lightindex >= range)
lightintensity *= settings->dlightparticlemultiplier;
- rtlight->photons = bound(0.0f, lightintensity * s * s, MAXBOUNCEGRIDPARTICLESPERLIGHT);
- photoncount += rtlight->photons;
+ rtlight->bouncegrid_photons = lightintensity * s * s * normalphotonscaling;
+ photoncount += rtlight->bouncegrid_photons;
+ VectorScale(rtlight->bouncegrid_photoncolor, settings->particleintensity * settings->energyperphoton, rtlight->bouncegrid_photoncolor);
// if the lightstyle happens to be off right now, we can skip actually
// firing the photons, but we did have to count them in the total.
//if (VectorLength2(rtlight->photoncolor) == 0.0f)
- // rtlight->photons = 0;
+ // rtlight->bouncegrid_photons = 0;
}
// the user provided an energyperphoton value which we try to use
// if that results in too many photons to shoot this frame, then we cap it
// which causes photons to appear/disappear from frame to frame, so we don't
// like doing that in the typical case
- normalphotonscaling = 1.0f / max(0.0001f, settings->energyperphoton);
- maxphotonscaling = (float)settings->maxphotons / max(1, photoncount);
- *photonscaling = min(normalphotonscaling, maxphotonscaling);
+ photonscaling = 1.0f;
+ photonintensity = 1.0f;
+ if (photoncount > settings->maxphotons)
+ {
+ photonscaling = settings->maxphotons / photoncount;
+ photonintensity = 1.0f / photonscaling;
+ }
+
+ // modify the lights to reflect our computed scaling
+ for (lightindex = 0; lightindex < range2; lightindex++)
+ {
+ if (lightindex < range)
+ {
+ light = (dlight_t *)Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+ if (!light)
+ continue;
+ rtlight = &light->rtlight;
+ }
+ else
+ rtlight = r_refdef.scene.lights[lightindex - range];
+ rtlight->bouncegrid_photons *= photonscaling;
+ VectorScale(rtlight->bouncegrid_photoncolor, photonintensity, rtlight->bouncegrid_photoncolor);
+ }
}
static int R_Shadow_BounceGrid_SplatPathCompare(const void *pa, const void *pb)
r_shadow_bouncegrid_state.lastupdatetime = realtime;
}
-static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t settings, unsigned int range, unsigned int range1, unsigned int range2, float photonscaling, int flag)
+static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t settings, unsigned int range, unsigned int range1, unsigned int range2, int flag)
{
vec3_t bouncerandom[10];
dlight_t *light;
int maxbounce;
int shootparticles;
int shotparticles;
+ float bounceminimumintensity2;
trace_t cliptrace;
//trace_t cliptrace2;
//trace_t cliptrace3;
// note that this code used to keep track of residual photons and
// distribute them evenly to achieve exactly a desired photon count,
// but that caused unwanted flickering in dynamic mode
- shootparticles = (int)floor(rtlight->photons * photonscaling);
+ shootparticles = (int)floor(rtlight->bouncegrid_photons);
// skip if we won't be shooting any photons
if (!shootparticles)
continue;
radius = rtlight->radius * settings.lightradiusscale;
- s = settings.particleintensity / shootparticles;
- VectorScale(rtlight->photoncolor, s, baseshotcolor);
+ //s = settings.particleintensity / shootparticles;
+ //VectorScale(rtlight->bouncegrid_photoncolor, s, baseshotcolor);
+ VectorCopy(rtlight->bouncegrid_photoncolor, baseshotcolor);
+ if (VectorLength2(baseshotcolor) <= 0.0f)
+ continue;
r_refdef.stats[r_stat_bouncegrid_lights]++;
r_refdef.stats[r_stat_bouncegrid_particles] += shootparticles;
+ // we stop caring about bounces once the brightness goes below this fraction of the original intensity
+ bounceminimumintensity2 = VectorLength(baseshotcolor) * settings.bounceminimumintensity2;
switch (settings.stablerandom)
{
default:
for (bouncecount = 0;;bouncecount++)
{
r_refdef.stats[r_stat_bouncegrid_traces]++;
+ rtlight->bouncegrid_traces++;
//r_refdef.scene.worldmodel->TraceLineAgainstSurfaces(r_refdef.scene.worldmodel, NULL, NULL, &cliptrace, clipstart, clipend, hitsupercontentsmask);
//r_refdef.scene.worldmodel->TraceLine(r_refdef.scene.worldmodel, NULL, NULL, &cliptrace2, clipstart, clipend, hitsupercontentsmask);
if (settings.staticmode || settings.stablerandom <= 0)
VectorCopy(cliptrace.endpos, hitpos);
R_Shadow_BounceGrid_AddSplatPath(clipstart, hitpos, shotcolor);
}
+ s = VectorDistance(rtlight->shadoworigin, cliptrace.endpos);
+ if (rtlight->bouncegrid_effectiveradius < s)
+ rtlight->bouncegrid_effectiveradius = s;
if (cliptrace.fraction >= 1.0f)
break;
r_refdef.stats[r_stat_bouncegrid_hits]++;
+ rtlight->bouncegrid_hits++;
if (bouncecount >= maxbounce)
break;
// scale down shot color by bounce intensity and texture color (or 50% if no texture reported)
surfcolor[1] = min(surfcolor[1], 1.0f);
surfcolor[2] = min(surfcolor[2], 1.0f);
VectorMultiply(shotcolor, surfcolor, shotcolor);
- if (VectorLength2(baseshotcolor) == 0.0f)
+ if (VectorLength2(shotcolor) <= bounceminimumintensity2)
break;
r_refdef.stats[r_stat_bouncegrid_bounces]++;
if (settings.bounceanglediffuse)
unsigned int range; // number of world lights
unsigned int range1; // number of dynamic lights (or zero if disabled)
unsigned int range2; // range+range1
- float photonscaling;
enable = R_Shadow_BounceGrid_CheckEnable(flag);
range2 = range + range1;
// calculate weighting factors for distributing photons among the lights
- R_Shadow_BounceGrid_AssignPhotons(&settings, range, range1, range2, flag, &photonscaling);
+ R_Shadow_BounceGrid_AssignPhotons(&settings, range, range1, range2, flag);
// trace the photons from lights and accumulate illumination
- R_Shadow_BounceGrid_TracePhotons(settings, range, range1, range2, photonscaling, flag);
+ R_Shadow_BounceGrid_TracePhotons(settings, range, range1, range2, flag);
// clear the texture
R_Shadow_BounceGrid_ClearPixels();
// draw properties on screen
if (!r_editlights_drawproperties.integer)
return;
- x = vid_conwidth.value - 240;
+ x = vid_conwidth.value - 320;
y = 5;
- DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0);
+ DrawQ_Pic(x-5, y-5, NULL, 250, 243, 0, 0, 0, 0.75, 0);
lightnumber = -1;
lightcount = 0;
range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
dpsnprintf(temp, sizeof(temp), "Specular : %.2f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);y += 8;
dpsnprintf(temp, sizeof(temp), "NormalMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);y += 8;
dpsnprintf(temp, sizeof(temp), "RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);y += 8;
+ y += 8;
+ dpsnprintf(temp, sizeof(temp), "Render stats\n"); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "Current color: %.3f %.3f %.3f\n", r_shadow_selectedlight->rtlight.currentcolor[0], r_shadow_selectedlight->rtlight.currentcolor[1], r_shadow_selectedlight->rtlight.currentcolor[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "Shadow size : %ix%ix6\n", r_shadow_selectedlight->rtlight.shadowmapatlassidesize, r_shadow_selectedlight->rtlight.shadowmapatlassidesize); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "World surfs : %i\n", r_shadow_selectedlight->rtlight.cached_numsurfaces); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "Shadow ents : %i + %i noself\n", r_shadow_selectedlight->rtlight.cached_numshadowentities, r_shadow_selectedlight->rtlight.cached_numshadowentities_noselfshadow); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "Lit ents : %i + %i noself\n", r_shadow_selectedlight->rtlight.cached_numlightentities, r_shadow_selectedlight->rtlight.cached_numlightentities_noselfshadow); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "BG photons : %.3f\n", r_shadow_selectedlight->rtlight.bouncegrid_photons); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "BG radius : %.0f\n", r_shadow_selectedlight->rtlight.bouncegrid_effectiveradius); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "BG color : %.3f %.3f %.3f\n", r_shadow_selectedlight->rtlight.bouncegrid_photoncolor[0], r_shadow_selectedlight->rtlight.bouncegrid_photoncolor[1], r_shadow_selectedlight->rtlight.bouncegrid_photoncolor[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+ dpsnprintf(temp, sizeof(temp), "BG stats : %i traces %i hits\n", r_shadow_selectedlight->rtlight.bouncegrid_traces, r_shadow_selectedlight->rtlight.bouncegrid_hits); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
}
static void R_Shadow_EditLights_ToggleShadow_f(void)