Forum

[FTE]Directional dynamic light implementation

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

Moderator: InsideQC Admins

[FTE]Directional dynamic light implementation

Postby toneddu2000 » Mon Jan 08, 2018 10:28 am

Hi guys! It's been quite a while that I'm struggling with the same question: would it be too hard to implement directional dynamic lights (aka sun light) in FTE with openGL? I read some papers here and here that explains that it wouldn't be a super hard task but the problem is..FTE code it's too complex. I don't even know where to start. I'd like to "simply" add a static directional light (even with hard coded inclination would be ok, not ideal, but ok) in openGL that works at least in client (read only-csqc games: yes in FTE you can create a only-csqc game!) but I cannot understand where FTE starts to draw lights. I tried gl/gllight.c and took a quick look at R_RenderDlights(), but I didn't find anything useful.

Anyone could please guide me in the right direction?

Thanks in advance guys!!
toneddu2000
 
Posts: 1282
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: [FTE]Directional dynamic light implementation

Postby Spike » Tue Jan 09, 2018 3:08 am

'directional' light is just such a vague term.
there are 3 'basic' types of rtlight.
1) omni-lights that cast light in every direction from a central spot (something like a cubemap).
2) spot-lights that cast light in a single direction from a 'central' spot (pretty much just a single face of the aforementioned cubemap).
3) parallel lights that have no central spot, but instead project from one side of its logical cuboid to the other.

(there's actually many different ways an engine could implement the above. deferred, forward, forward+, etc. then there's different ways to do shadows. stencil, cubemaps, dual-paraboloid, etc... but I'm ignoring that stuff for now, and focusing on just FTE's forward rendering.)

What you're asking for is the third type, but FTE only supports the first two right now.
parallel lights are interesting in that they're just weird. Logically, they're just a really (infinitely) far away omni light. except that this means that you might as well treat them like a spot light without the spot filter. and their infinite-distance nature means that they should use an orthographic projection matrix instead of the frustum-based projection matrix that a spot light or omniface would use.
orthographic matricies have no single point of origin, which makes pvs checks really messy (you'd have to scan for all leafs that contain an entire bounded plane), or just disable pvs checks... *shrug*.
so yeah, if you take the spot-light code, change the shadow generation to use an ortho matrix (the Matrix4x4_CM_Projection_Far line in Sh_GenShadowMap), change the projection glsl to use an ortho matrix (this is messy, the matrix is passed via a simple vec4 and transformed weirdly in the glsl for the benefit of omni lights), somehow define a non-axial cuboid for your light's volume that encompasses all visible geometry for your light to project over (or just use the worldmodel's bounds, if you're lazy), and disable all pvs checks when generating the shadows (beware of lights inside walls!), then you should have something close to what you're after.
getting it to work without breaking spotlights in the process is a bit more of a challenge... bonus points if you can
note that many implementations of such lights actually define multiple cuboids, with multiple shadowmaps, as a way to avoid depth precision issues on large maps. terrain systems might need it, but if your map is small or you're willing to fade the effects of the sun's shadows with distance then its probably overkill. Note how this stuff is all kinda special-casey, which is why I never sought to address this stuff - the idea of lights emitting from an entire plane that is actually inside a wall kinda put me off.
Spike
 
Posts: 2874
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: [FTE]Directional dynamic light implementation

Postby toneddu2000 » Tue Jan 09, 2018 7:50 am

Thanks Spike for the detailed reply. Instead of modifying spot light code, would it be possible to:
1. Removing light decay from omni light(I know it would be always an omni light, but at least there wouldn't be any attenuation from center of light)?
2. Using spot light with cubemaps with just one cube face as "hole" where to project light(again, lame solution, but..)?
3. There's the possibility to "lengthen" shadow computation with very far omni lights? Because, when I use a distant omni light (say [0,0, 1024]) with a big radius (like 8000), shadows didn't reach ground perfectly, they're like very low detailed. Once, I touched engine code to increase shadowmap to 8192 px but it was idiotic, I admit.

If these 3 options are (probably) lame or not feasible, could you tell me where's the portion of code that manages spot light?

On a side note, in CSQC, which is the method to use spotlight? Is it perhaps the 5th argument of dynamiclight_add() func? Where it says "optional string cubemapname"? So, filling that with cubemap, will it transform omni light in spot light? And, which would be the "orientation" of light cubemaps?

Just to know, are you planning to add parallel light support in future versions of FTE or it's not one of your prerogatives? Because if not, I'll try to tinker with spotlights code on my own but I don't guarantee any results! :biggrin:

Thanks again
toneddu2000
 
Posts: 1282
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: [FTE]Directional dynamic light implementation

Postby Spike » Tue Jan 09, 2018 9:46 am

float light = dynamiclight_add(...);
dynamiclight_set(light, LFIELD_ANGLES, self.v_angle);
dynamiclight_set(light, LFIELD_FOV, 90.0);
will add a spotlight with a 90-degree cone. larger values will be distorted, 180+ will bug out completely, much like the fov cvar.

that's the theory anyway. they don't get tested enough - they might be completely bugged right now... have fun...
Spike
 
Posts: 2874
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: [FTE]Directional dynamic light implementation

Postby toneddu2000 » Tue Jan 09, 2018 12:56 pm

Spike wrote:float light = dynamiclight_add(...);
dynamiclight_set(light, LFIELD_ANGLES, self.v_angle);
dynamiclight_set(light, LFIELD_FOV, 90.0);
will add a spotlight with a 90-degree cone. larger values will be distorted, 180+ will bug out completely, much like the fov cvar.

that's the theory anyway. they don't get tested enough - they might be completely bugged right now... have fun...

Well, I had fun! :biggrin: It works pretty well, I might say. It's not buggy at all, plus I didn't know dynamiclight_add could be tied to a float to get retrieved later for editing..cool..

But still I'm wondering.. there's a solution for very distant omni lights shadows resolution? Because, in that case, I could use a super huge omni light (which, to be finicky, it's what sun is! :mrgreen: ) and set its origin WAYYYYY more up and its enormous radius should cover a decent terrain portion (something like 4096 x 4096), which could be ok
toneddu2000
 
Posts: 1282
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: [FTE]Directional dynamic light implementation

Postby toneddu2000 » Sat Apr 07, 2018 2:44 pm

ok, my first tiny steps in this endless universe! :biggrin:

Thanks to Spike
Spike wrote:so yeah, if you take the spot-light code, change the shadow generation to use an ortho matrix (the Matrix4x4_CM_Projection_Far line in Sh_GenShadowMap), change the projection glsl to use an ortho matrix (this is messy, the matrix is passed via a simple vec4 and transformed weirdly in the glsl for the benefit of omni lights), somehow define a non-axial cuboid for your light's volume that encompasses all visible geometry for your light to project over (or just use the worldmodel's bounds, if you're lazy), and disable all pvs checks when generating the shadows (beware of lights inside walls!), then you should have something close to what you're after.

I understood that function that project shadow maps in FTE is Sh_GenShadowMap()
and especially here
Code: Select all
if (lighttype & LSHADER_SPOT)
      Matrix4x4_CM_Projection_Far(r_refdef.m_projection_std, l->fov, l->fov, r_shadow_shadowmapping_nearclip.value, l->radius, false);
   else if (lighttype & LSHADER_ORTHO)
   {
      float xmin = -l->radius;
      float ymin = -l->radius;
      float znear = -l->radius;
      float xmax = l->radius;
      float ymax = l->radius;
      float zfar = l->radius;
      Matrix4x4_CM_Orthographic(r_refdef.m_projection_std, xmin, xmax, ymax, ymin, znear, zfar);
   }
   else
      Matrix4x4_CM_Projection_Far(r_refdef.m_projection_std, 90, 90, r_shadow_shadowmapping_nearclip.value, l->radius, false);

engine assigns specific matrix projection depending on which light type the user chose.

I also noticed there's an "ortho" type of light that I didn't know.. oh well

So I studied a little on internet and I took a look the Matrix4x4_CM_OrthographicD3D in mathlib.c, so I thought, if I replace the Matrix4x4_CM_Projection_Far with Matrix4x4_CM_Orthographic it could be ok, but..which kind of arguments should I put inside?

I first tried using arbitrary values, just to test. And I replaced the code above so every light type will use same matrix
Code: Select all
Matrix4x4_CM_OrthographicD3D(r_refdef.m_projection_std, 0.0,8000.0, 0.0, 8000.0, 10.0,10000.0);

and shadows are.. weird..They're like tiny and following objects in a mirrored way(!) so I probably inverted matrix xside with -xside
Image
then I modified code adding - in the xmax argument
Code: Select all
Matrix4x4_CM_OrthographicD3D(r_refdef.m_projection_std, 0.0,-8000.0, 0.0, 8000.0, 10.0,10000.0);

And the image is even weirder than previous one! :biggrin:
Image
I also tried other "combinations" but the first one is the better one(yeah no joking)!:)

Could someone at least tell me if I'm in the right direction atleast? :lol:
toneddu2000
 
Posts: 1282
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: [FTE]Directional dynamic light implementation

Postby Spike » Sat Apr 07, 2018 8:01 pm

LSHADER_ORTHO was me having a go at ortho lights, but I never actually got it working properly.

Matrix4x4_CM_OrthographicD3D is NOT what you want. gl uses matricies that span from -1 to 1 for depth values. d3d uses 0 to 1. mixing them badly will result in some screwed shadow maps.
I don't remember if the y coord is negated or not - remember that 0 in gl is the bottom of the screen rather than the top, and thus t=0 is the BOTTOM of the texture too, so if your code assumes that t=0 is the top of the texture then you'll need to use t=1-t to fix that.

the point about ortho matricies is that the near plane can trivially be negative. you might want to put the near plane at 0 though, thereby ensuring the light itself is positioned on the near plane itself. however, note that this then means that your light's origin will probably be outside the map, which brings me to the next issue.
pvs - if you want to calculate the pvs properly, you'll need to find all the leafs that the near clip plane passes through and merge the pvs of those leafs. however, most of those will be inside a solid wall which would stop shadows from being generated in that entire area, so you might want to just be lazy and use the entire ortho frustum, or just not bother with the pvs at all.
one fancy option might be to come up with a way to flatten all the leafs to find the nearest ones, but that stuff sounds much more complicated and expensive, I'm not entirely sure on the maths needed, should be okay for a static light though, I guess.

note that not only do the shadows have to be generated with a suitable ortho matrix, but also your glsl needs to sample from the correct part of the cubemap - which means incorporating the same ortho values into your glsl.
essentially, your shadowmap needs to contain depth values generated with an ortho matrix, and your shadowmap needs to be sampled as though it were projected across the same ortho frustum's front plane. so you calculate the world coords in your vertex shader and then project those through the inverse of your ortho matrix combined with the light's modelview matrix, or something. calc the distance between that light's near plane and the vertex, shove that into a varying, and compare that to the sampled values inside your fragment shader to see if its in shadow or not. you'll want to grab a few samples (eg: textureGather) in order to smooth things out a bit.

that's all really badly explained, but it might give you a few pointers. (void*)0xdeadbeef. there, have another pointer.
Spike
 
Posts: 2874
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: [FTE]Directional dynamic light implementation

Postby toneddu2000 » Sun Apr 08, 2018 8:01 am

First of all, thanks A LOT for the exhaustive explanation! Not badly exlained at all!

I quite fully understood what I have to do, just some aspects are not clear, could you please explain them to me?

  1. I should create an ortho matrix and pass r_refdef.m_projection_std to that, right? But, which is bottom, which is top, which is left or right?
    Because I found this site, which it's very well explained but I cannot understand which argument pass to which. In your math.c, in matricies func, you use arguments like "xmax","xmin","ymax"..could you please tell me which is left, right..? NOTE:I use ONLY openGL, so, for this example, it would be fine just to know how to setup openGL matricies
  2. So, once I created the matrix in C, I replicate the same matrix in GLSL, right? Yesterday, before asking for help, I edited this glsl file like this:
    Code: Select all
    varying vec2 tc;
    varying vec4 vtexprojcoord;
    varying vec4 vc,tf;
    varying vec3 lightvector;
    varying vec3 eyevector;
    uniform float cvar_render_sun_x,cvar_render_sun_y,cvar_render_sun_z,cvar_render_sun_ground;
    #ifdef VERTEX_SHADER
    void main (void)
    {
    ...
       const float right = -1.0;
        const float bottom = -1.0;
        const float left = 1.0;
        const float top = 1.0;
        const float far = 1.0;
        const float near = 0.0;
        mat4 testmat = mat4(
            vec4(2.0 / (right - left), 0, 0, -(right + left) / (right - left)),
                        vec4(0, 2.0 / (top - bottom), 0, -(top + bottom) / (top - bottom)),
                        vec4(0, 0, -2.0 / (far - near), -(far + near) / (far - near)),
                        vec4(0, 0, 0, 1)
        );
       #ifdef PARALLELLIGHT
       vtexprojcoord = (testmat*vec4(v_position.xyz, 1.0));
       #else
       vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));
       #endif
    }
    #endif
    #ifdef FRAGMENT_SHADER
    void main (void)
    {
    ...
       //IMPORTANT adding realtime shadows
       colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);
       //adding lights to diffuse part
       composite.rgb *= colorscale * l_lightcolour;
    ...
    }
    #endif

    Could it be ok? Replicating the EXACT values as in c, of course. Or probably should i do
    Code: Select all
    vtexprojcoord = (l_cubematrix*testmat*vec4(v_position.xyz, 1.0));
    ?
  3. pvs- since I don't use NetRadiant anymore and I have just a gigantic bsp hull and only static meshes put them via code at startup, is it really a problem?

Thanks again for your help!

PS:
Spike wrote:but it might give you a few pointers. (void*)0xdeadbeef. there, have another pointer.

Spike writing C-style jokes: priceless! :biggrin:
toneddu2000
 
Posts: 1282
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: [FTE]Directional dynamic light implementation

Postby toneddu2000 » Mon Apr 09, 2018 8:53 am

ok, my bad.. I read wrong the name of function Matrix4x4_CM_OrthographicD3D. I thought it was Matrix4x4_CM_Orthographic3D (without the "D"). No I get it, it's for D3d! That's why Spike told not to mix d3d and opengl! Sorry Spike, it thought it was a common function for both d3d and openGL! :oops:

I also tried
Code: Select all
Matrix4x4_CM_Orthographic(r_refdef.m_projection_std,-1,1,1,-1,0,1);

and
Code: Select all
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection_std,-1,1,0,false);

But no shadow at all.
Have anyone a link to a good parallel matrix explanation/code?
Another thing: does anyone if I do correct the C-part but I make some mistakes on the matrix GLSL-part, could this led to no shadow at all or it should render at least a wrong shadow map?
toneddu2000
 
Posts: 1282
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest