]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
Add r_shadow_culllights_trace feature, this culls rtlights by traceline checks simila...
[xonotic/darkplaces.git] / r_shadow.c
index ebb0e469f3700693210e2a11c9f76fbb64de3fcc..b774a5c325cbe02b68643ac989a9f0329edb0e83 100644 (file)
@@ -334,6 +334,13 @@ cvar_t r_shadow_sortsurfaces = {0, "r_shadow_sortsurfaces", "1", "improve perfor
 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect OpenGL 2.0 render path)"};
+cvar_t r_shadow_culllights_pvs = {CVAR_SAVE, "r_shadow_culllights_pvs", "1", "check if light overlaps any visible bsp leafs when determining if the light is visible"};
+cvar_t r_shadow_culllights_trace = {CVAR_SAVE, "r_shadow_culllights_trace", "1", "use raytraces from the eye to random places within light bounds to determine if the light is visible"};
+cvar_t r_shadow_culllights_trace_eyejitter = {CVAR_SAVE, "r_shadow_culllights_trace_eyejitter", "16", "offset eye location randomly by this much"};
+cvar_t r_shadow_culllights_trace_enlarge = {CVAR_SAVE, "r_shadow_culllights_trace_enlarge", "0.1", "make light bounds bigger by *1.0+enlarge"};
+cvar_t r_shadow_culllights_trace_samples = {CVAR_SAVE, "r_shadow_culllights_trace_samples", "16", "use this many traces to random positions (in addition to center trace)"};
+cvar_t r_shadow_culllights_trace_tempsamples = {CVAR_SAVE, "r_shadow_culllights_trace_tempsamples", "16", "use this many traces if the light was created by csqc (no inter-frame caching), -1 disables the check (to avoid flicker entirely)"};
+cvar_t r_shadow_culllights_trace_delay = {CVAR_SAVE, "r_shadow_culllights_trace_delay", "1", "light will be considered visible for this many seconds after any trace connects"};
 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", "0", "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"};
@@ -464,19 +471,19 @@ static void R_Shadow_SetShadowMode(void)
                        {
                                if (!r_fb.usedepthtextures)
                                        r_shadow_shadowmappcf = 1;
-                               else if((strstr(gl_vendor, "NVIDIA") || strstr(gl_renderer, "Radeon HD")) && vid.support.arb_shadow && r_shadow_shadowmapshadowsampler) 
+                               else if((strstr(gl_vendor, "NVIDIA") || strstr(gl_renderer, "Radeon HD")) && vid.support.arb_shadow && r_shadow_shadowmapshadowsampler)
                                {
                                        r_shadow_shadowmapsampler = true;
                                        r_shadow_shadowmappcf = 1;
                                }
                                else if(vid.support.amd_texture_texture4 || vid.support.arb_texture_gather)
                                        r_shadow_shadowmappcf = 1;
-                               else if((strstr(gl_vendor, "ATI") || strstr(gl_vendor, "Advanced Micro Devices")) && !strstr(gl_renderer, "Mesa") && !strstr(gl_version, "Mesa")) 
+                               else if((strstr(gl_vendor, "ATI") || strstr(gl_vendor, "Advanced Micro Devices")) && !strstr(gl_renderer, "Mesa") && !strstr(gl_version, "Mesa"))
                                        r_shadow_shadowmappcf = 1;
-                               else 
+                               else
                                        r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler;
                        }
-                       else 
+                       else
                        {
                 r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler;
                                switch (r_shadow_shadowmapfilterquality)
@@ -804,6 +811,13 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_polygonfactor);
        Cvar_RegisterVariable(&r_shadow_polygonoffset);
        Cvar_RegisterVariable(&r_shadow_texture3d);
+       Cvar_RegisterVariable(&r_shadow_culllights_pvs);
+       Cvar_RegisterVariable(&r_shadow_culllights_trace);
+       Cvar_RegisterVariable(&r_shadow_culllights_trace_eyejitter);
+       Cvar_RegisterVariable(&r_shadow_culllights_trace_enlarge);
+       Cvar_RegisterVariable(&r_shadow_culllights_trace_samples);
+       Cvar_RegisterVariable(&r_shadow_culllights_trace_tempsamples);
+       Cvar_RegisterVariable(&r_shadow_culllights_trace_delay);
        Cvar_RegisterVariable(&r_shadow_bouncegrid);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_blur);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_bounceanglediffuse);
@@ -1607,7 +1621,7 @@ static int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float borde
        int sides = 0x3F, masks[6] = { 3<<4, 3<<4, 3<<0, 3<<0, 3<<2, 3<<2 };
        float scale = (size - 2*border)/size, len;
        float bias = border / (float)(size - border), dp, dn, ap, an;
-       // check if cone enclosing side would cross frustum plane 
+       // check if cone enclosing side would cross frustum plane
        scale = 2 / (scale*scale + 2);
        Matrix4x4_OriginFromMatrix(&rtlight->matrix_lighttoworld, o);
        for (i = 0;i < 5;i++)
@@ -2565,7 +2579,7 @@ static void R_Shadow_BounceGrid_GenerateSettings(r_shadow_bouncegrid_settings_t
        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)); 
+       memset(settings, 0, sizeof(*settings));
 
        // build up a complete collection of the desired settings, so that memcmp can be used to compare parameters
        settings->staticmode                    = s;
@@ -2846,12 +2860,26 @@ static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *se
                w = r_shadow_lightintensityscale.value * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
                if (!settings->staticmode)
                {
-                       if (R_CullBox(cullmins, cullmaxs))
-                               continue;
+                       // skip if the expanded light box does not touch any visible leafs
                        if (r_refdef.scene.worldmodel
-                        && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs
-                        && !r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, cullmins, cullmaxs))
+                               && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs
+                               && !r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, cullmins, cullmaxs))
                                continue;
+                       // skip if the expanded light box is not visible to traceline
+                       // note that PrepareLight already did this check but for a smaller box, so we
+                       // end up casting more traces per frame per light when using bouncegrid, which
+                       // is probably fine (and they use the same timer)
+                       if (r_shadow_culllights_trace.integer)
+                       {
+                               if (rtlight->trace_timer != realtime && R_CanSeeBox(rtlight->trace_timer == 0 ? r_shadow_culllights_trace_tempsamples.integer : r_shadow_culllights_trace_samples.integer, r_shadow_culllights_trace_eyejitter.value, r_shadow_culllights_trace_enlarge.value, r_refdef.view.origin, rtlight->cullmins, rtlight->cullmaxs))
+                                       rtlight->trace_timer = realtime;
+                               if (realtime - rtlight->trace_timer > r_shadow_culllights_trace_delay.value)
+                                       return;
+                       }
+                       // skip if expanded light box is offscreen
+                       if (R_CullBox(cullmins, cullmaxs))
+                               continue;
+                       // skip if overall light intensity is zero
                        if (w * VectorLength2(rtlight->color) == 0.0f)
                                continue;
                }
@@ -4138,7 +4166,7 @@ void R_RTLight_Compile(rtlight_t *rtlight)
                // this variable must be set for the CompileShadowVolume/CompileShadowMap code
                r_shadow_compilingrtlight = rtlight;
                R_FrameData_SetMark();
-               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs, r_shadow_buffer_visitingleafpvs, 0, NULL);
+               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs, r_shadow_buffer_visitingleafpvs, 0, NULL, rtlight->shadow == 0);
                R_FrameData_ReturnToMark();
                numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
                numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
@@ -4656,7 +4684,7 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight)
        }
        */
 
-       // if lightstyle is currently off, don't draw the light
+       // skip if lightstyle is currently off
        if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
                return;
 
@@ -4664,10 +4692,27 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight)
        if (nolight)
                return;
 
-       // if the light box is offscreen, skip it
+       // skip if the light box is not touching any visible leafs
+       if (r_shadow_culllights_pvs.integer
+               && r_refdef.scene.worldmodel
+               && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs
+               && !r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, rtlight->cullmins, rtlight->cullmaxs))
+               return;
+
+       // skip if the light box is not visible to traceline
+       if (r_shadow_culllights_trace.integer)
+       {
+               if (rtlight->trace_timer != realtime && R_CanSeeBox(rtlight->trace_timer == 0 ? r_shadow_culllights_trace_tempsamples.integer : r_shadow_culllights_trace_samples.integer, r_shadow_culllights_trace_eyejitter.value, r_shadow_culllights_trace_enlarge.value, r_refdef.view.origin, rtlight->cullmins, rtlight->cullmaxs))
+                       rtlight->trace_timer = realtime;
+               if (realtime - rtlight->trace_timer > r_shadow_culllights_trace_delay.value)
+                       return;
+       }
+
+       // skip if the light box is off screen
        if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
                return;
 
+       // in the typical case this will be quickly replaced by GetLightInfo
        VectorCopy(rtlight->cullmins, rtlight->cached_cullmins);
        VectorCopy(rtlight->cullmaxs, rtlight->cached_cullmaxs);
 
@@ -4694,7 +4739,7 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight)
        {
                // dynamic light, world available and can receive realtime lighting
                // calculate lit surfaces and leafs
-               r_refdef.scene.worldmodel->GetLightInfo(r_refdef.scene.worldentity, rtlight->shadoworigin, rtlight->radius, rtlight->cached_cullmins, rtlight->cached_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs, r_shadow_buffer_visitingleafpvs, rtlight->cached_numfrustumplanes, rtlight->cached_frustumplanes);
+               r_refdef.scene.worldmodel->GetLightInfo(r_refdef.scene.worldentity, rtlight->shadoworigin, rtlight->radius, rtlight->cached_cullmins, rtlight->cached_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs, r_shadow_buffer_visitingleafpvs, rtlight->cached_numfrustumplanes, rtlight->cached_frustumplanes, rtlight->shadow == 0);
                R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
                leaflist = r_shadow_buffer_leaflist;
                leafpvs = r_shadow_buffer_leafpvs;
@@ -5646,7 +5691,7 @@ static void R_Shadow_DrawModelShadowMaps(void)
        VectorAdd(shadoworigin, r_refdef.view.origin, shadoworigin);
        dot1 = DotProduct(r_refdef.view.forward, shadowdir);
        dot2 = DotProduct(r_refdef.view.up, shadowdir);
-       if (fabs(dot1) <= fabs(dot2)) 
+       if (fabs(dot1) <= fabs(dot2))
                VectorMA(r_refdef.view.forward, -dot1, shadowdir, shadowforward);
        else
                VectorMA(r_refdef.view.up, -dot2, shadowdir, shadowforward);
@@ -5706,7 +5751,7 @@ static void R_Shadow_DrawModelShadowMaps(void)
 
        Matrix4x4_Concat(&mvpmatrix, &r_refdef.view.viewport.projectmatrix, &r_refdef.view.viewport.viewmatrix);
        Matrix4x4_Invert_Full(&invmvpmatrix, &mvpmatrix);
-       Matrix4x4_CreateScale3(&scalematrix, size, -size, 1); 
+       Matrix4x4_CreateScale3(&scalematrix, size, -size, 1);
        Matrix4x4_AdjustOrigin(&scalematrix, 0, size, -0.5f * bias);
        Matrix4x4_Concat(&texmatrix, &scalematrix, &shadowmatrix);
        Matrix4x4_Concat(&r_shadow_shadowmapmatrix, &texmatrix, &invmvpmatrix);
@@ -5935,13 +5980,10 @@ static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
        {
                switch(vid.renderpath)
                {
-               case RENDERPATH_GL11:
-               case RENDERPATH_GL13:
                case RENDERPATH_GL20:
                case RENDERPATH_GLES1:
                case RENDERPATH_GLES2:
 #if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2)
-                       CHECKGLERROR
                        // See if we can use the GPU-side method to prevent implicit sync
                        if (vid.support.arb_query_buffer_object) {
 #define BUFFER_OFFSET(i)    ((GLint *)((unsigned char*)NULL + (i)))
@@ -5956,13 +5998,23 @@ static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                                qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, BUFFER_OFFSET(4));
                                qglBindBufferBase(GL_UNIFORM_BUFFER, 0, r_shadow_occlusion_buf);
                                occlude = MATERIALFLAG_OCCLUDE;
-                       } else {
-                               qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels);
-                               qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels); 
-                               if (visiblepixels < 1 || allpixels < 1)
-                                       return;
-                               rtlight->corona_visibility *= bound(0, (float)visiblepixels / (float)allpixels, 1);
+                               cscale *= rtlight->corona_visibility;
+                               CHECKGLERROR
+                               break;
                        }
+                       // fallthrough
+#else
+                       return;
+#endif
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2)
+                       CHECKGLERROR
+                       qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels);
+                       qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels);
+                       if (visiblepixels < 1 || allpixels < 1)
+                               return;
+                       rtlight->corona_visibility *= bound(0, (float)visiblepixels / (float)allpixels, 1);
                        cscale *= rtlight->corona_visibility;
                        CHECKGLERROR
                        break;