Forum

Shadows on brush models

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Postby mh » Sun Apr 10, 2011 1:53 pm

Shadow volumes shouldn't assume PPL unless the author has built artificial dependencies in.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby revelator » Sun Apr 10, 2011 5:10 pm

tenebrae uses something a bit like an early version of dp's rtlights
so it would seem it does :S

pretty much nothing of the old ligth code remains.

Code: Select all
// r_light.c - PENTA: almost obsolete now

#include "quakedef.h"

int   r_dlightframecount;

/*
==================
R_AnimateLight
==================
*/
void R_AnimateLight (void)
{
   int         i,j,k;
   
//
// light animations
// 'm' is normal light, 'a' is no light, 'z' is double bright
   i = (int)(cl.time*10);
   for (j=0 ; j<MAX_LIGHTSTYLES ; j++)
   {
      if (!cl_lightstyle[j].length)
      {
         d_lightstylevalue[j] = 256;
         continue;
      }
      k = i % cl_lightstyle[j].length;
      k = cl_lightstyle[j].map[k] - 'a';
      k = k*22;
      d_lightstylevalue[j] = k;
   }   
}

/*
=============================================================================

LIGHT SAMPLING

=============================================================================
*/

mplane_t      *lightplane;
vec3_t         lightspot;

int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end)
{
   int         r;
   float      front, back, frac;
   int         side;
   mplane_t   *plane;
   vec3_t      mid;
   msurface_t   *surf;
   int         s, t, ds, dt;
   int         i;
   mtexinfo_t   *tex;
   byte      *lightmap;
   unsigned   scale;
   int         maps;

   if (node->contents < 0)
   {
      return -1;      // didn't hit anything
   }
// calculate mid point
   plane = node->plane;
   front = DotProduct (start, plane->normal) - plane->dist;
   back = DotProduct (end, plane->normal) - plane->dist;
   side = front < 0;
   
   if ( (back < 0) == side)
   {
      return RecursiveLightPoint (node->children[side], start, end);
   }
   frac = front / (front-back);
   mid[0] = start[0] + (end[0] - start[0])*frac;
   mid[1] = start[1] + (end[1] - start[1])*frac;
   mid[2] = start[2] + (end[2] - start[2])*frac;
   
// go down front side   
   r = RecursiveLightPoint (node->children[side], start, mid);

   if (r >= 0)
   {
      return r;      // hit something
   }

   if ( (back < 0) == side )
   {
      return -1;      // didn't hit anuthing
   }

// check for impact on this node
   VectorCopy (mid, lightspot);
   lightplane = plane;

   surf = cl.worldmodel->surfaces + node->firstsurface;

   for (i=0 ; i<node->numsurfaces ; i++, surf++)
   {
      if (surf->flags & SURF_DRAWTILED) continue;   // no lightmaps

      tex = surf->texinfo;
      
      s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3];
      t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];;

      if (s < surf->texturemins[0] || t < surf->texturemins[1]) continue;
      
      ds = s - surf->texturemins[0];
      dt = t - surf->texturemins[1];
      
      if ( ds > surf->extents[0] || dt > surf->extents[1] ) continue;

      if (!surf->samples)
      {
         return 0;
      }
      ds >>= 4;
      dt >>= 4;

      lightmap = surf->samples;
      r = 0;

      if (lightmap)
      {

         lightmap += dt * ((surf->extents[0]>>4)+1) + ds;

         for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++)
         {
            scale = d_lightstylevalue[surf->styles[maps]];
            r += *lightmap * scale;
            lightmap += ((surf->extents[0]>>4)+1) *   ((surf->extents[1]>>4)+1);
         }         
         r >>= 8;
      }      
      return r;
   }

// go down back side
   return RecursiveLightPoint (node->children[!side], mid, end);
}

int R_LightPoint (vec3_t p)
{
   vec3_t      end;
   int         r;
   
   if (!cl.worldmodel->lightdata)
   {
      return 255;
   }
   end[0] = p[0];
   end[1] = p[1];
   end[2] = p[2] - 2048;
   
   r = RecursiveLightPoint (cl.worldmodel->nodes, p, end);
   
   if (r == -1)
   {
      r = 0;
   }
   return r;
}


this is what remains.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Postby LordHavoc » Tue Apr 12, 2011 12:54 pm

reckless wrote:looking at tenebraes i once thought hey it cant be that hard but im still to have a breakthrough with that one (does tenenbraes shadow volumes assume per pixel lighting ?).


It does.

Technically without per pixel lighting to mask off with the stencil buffer, all you're doing is some crude hack.

r_shadows in darkplaces applies a 50% gray multiply blend on the whole screen where the stencil buffer is set.

Shadow volumes work on this principle:
From a set of geometry facing the light source (or facing away from it) you can extrude a volume - each triangle produces a front triangle (identical to the original) and a back triangle (stretched far away from the lightsource), it also produces a quad (2 triangles) for each non-casting edge (silhouette quads).

Clear stencil buffer - as a practical matter I clear it to 128.

Render all objects using a stencil increment where zfail for backfaces (facing away from the viewer) and a stencil decrement where zfail for frontfaces (facing toward the viewer).

This results in pixels inside the shadow volume being marked as 129 (or more in the case of overlapping shadows), because the decrement did not occur on the frontfaces, because the frontfaces of the volume were zpass (which was set to do nothing), not zfail on those pixels.

So you end up with literally a count of how many shadows are on each pixel.

DarkPlaces computes the triangleneighbors array at load for each model/map, containing 3 triangle indexes per triangle, which are -1 if there is no triangle sharing that edge (or if more than one other triangle is sharing that edge! be careful about this, the dog model and some others have some redundantly shared edges, be sure they are stored as -1), the algorithm is simply to look up every triangle that uses the same two vertices as this edge, and there are 3 edges per triangle.

Lights simply mark which triangles should cast shadows, and call the casting code with that list of triangles, it generates a new mesh from those triangles (the shadow volume).

After all this, you can render lighting with stencil test on (and compare to 128), any pixel that is 128 receives light, any pixel that is not 128 does not.

r_shadows works similarly but inverted - render a fullscreen "shadow" quad (alpha blending some black works fine for this) where stencil is not 128 - and obviously this uses fake light sources for each entity for the shadow casting itself.

Side effect of any fake shadowing is shadow bleed (seeing a shadow on the floor below a ledge a monster is standing on), this is the main drawback.

Shadow bleed does not occur with real light sources being masked, because the ledge itself is casting shadows in that case, one can not tell that multiple shadows are stacked on the floor below.

Note that shadowmapping works differently - a depth texture is bound as a framebuffer object's depth attachment, no color buffers are bound, and all the geometry is rendered from the light's viewpoint, as 6 separate views.

Then this texture is bound as a shader input when rendering the lighting and each pixel it has to make a decision as to which of the 6 views contains the data it wants, read that pixel and neighboring pixels and compare them to the depth value it expects for this pixel (from the light's view!) to get a white or black result, then blur between those shadow comparison results a bit (soft shadowing).

You can find the shadowmapping and all other shader code used in DP here: http://ghdigital.com/~havoc/default.glsl (Note: usually somewhat out of date, I post updated shader code to this occasionally, not every time it is changed in darkplaces svn)
LordHavoc
 
Posts: 322
Joined: Fri Nov 05, 2004 3:12 am
Location: western Oregon, USA

Postby revelator » Tue Apr 12, 2011 2:01 pm

ah :) ok that explains a lot.

so to implement this in say realm id have to do a pretty major rewrite.

ill have a look at shadowmaps :) thanks.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Previous

Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest