[FTE]Directional dynamic light implementation
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
[FTE]Directional dynamic light implementation
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!!
Anyone could please guide me in the right direction?
Thanks in advance guys!!
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Re: [FTE]Directional dynamic light implementation
'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.
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.
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
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!
Thanks again
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!
![Big Grin :biggrin:](./images/smilies/icon_biggrin.gif)
Thanks again
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Re: [FTE]Directional dynamic light implementation
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...
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...
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
Well, I had fun!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...
![Big Grin :biggrin:](./images/smilies/icon_biggrin.gif)
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!
![Mr. Green :mrgreen:](./images/smilies/icon_mrgreen.gif)
Meadow Fun!! - my first commercial game, made with FTEQW game engine
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
ok, my first tiny steps in this endless universe!
Thanks to Spike
and especially here
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
and shadows are.. weird..They're like tiny and following objects in a mirrored way(!) so I probably inverted matrix xside with -xside
![Image](https://s14.postimg.org/yloutyqbh/shdw0.png)
then I modified code adding - in the xmax argument
And the image is even weirder than previous one!
![Image](https://s14.postimg.org/ev181w3f1/shdw1.png)
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?![Laughing :lol:](./images/smilies/icon_laughing.gif)
![Big Grin :biggrin:](./images/smilies/icon_biggrin.gif)
Thanks to Spike
I understood that function that project shadow maps in FTE is Sh_GenShadowMap()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.
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);
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);
![Image](https://s14.postimg.org/yloutyqbh/shdw0.png)
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);
![Big Grin :biggrin:](./images/smilies/icon_biggrin.gif)
![Image](https://s14.postimg.org/ev181w3f1/shdw1.png)
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?
![Laughing :lol:](./images/smilies/icon_laughing.gif)
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Re: [FTE]Directional dynamic light implementation
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.
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.
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
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?
PS:![Big Grin :biggrin:](./images/smilies/icon_biggrin.gif)
I quite fully understood what I have to do, just some aspects are not clear, could you please explain them to me?
- 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 - 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:
Could it be ok? Replicating the EXACT values as in c, of course. Or probably should i do
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
?Code: Select all
vtexprojcoord = (l_cubematrix*testmat*vec4(v_position.xyz, 1.0));
- 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?
PS:
Spike writing C-style jokes: priceless!Spike wrote:but it might give you a few pointers. (void*)0xdeadbeef. there, have another pointer.
![Big Grin :biggrin:](./images/smilies/icon_biggrin.gif)
Meadow Fun!! - my first commercial game, made with FTEQW game engine
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
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!
I also tried
and
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?
![Embarassed :oops:](./images/smilies/icon_embarassed.gif)
I also tried
Code: Select all
Matrix4x4_CM_Orthographic(r_refdef.m_projection_std,-1,1,1,-1,0,1);
Code: Select all
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection_std,-1,1,0,false);
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?
Meadow Fun!! - my first commercial game, made with FTEQW game engine
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
I asked help on Freelancer site for implementing dynamic shadows on FTE. Let's see if someone replies. I don't know if offer price it's too low..
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Re: [FTE]Directional dynamic light implementation
Hi toneddu2000,
you might want to look into some of my work on ortho lights (which also include lights from sky brushes) and parallel split shadow maps over here:
https://github.com/Qfusion/qfusion/commits/rtlights2
https://github.com/Qfusion/glsl/commits/rtlights2
The code is 90% done and tested, only needs partial rewrite here and there, especially the part that constructs the skylight volumes. The most obviously missing feature is blending between cascades. That I'm going to implement some time later.
Cheers!
you might want to look into some of my work on ortho lights (which also include lights from sky brushes) and parallel split shadow maps over here:
https://github.com/Qfusion/qfusion/commits/rtlights2
https://github.com/Qfusion/glsl/commits/rtlights2
The code is 90% done and tested, only needs partial rewrite here and there, especially the part that constructs the skylight volumes. The most obviously missing feature is blending between cascades. That I'm going to implement some time later.
Cheers!
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
Thanks a lot Vic, that really means a lot to me. Honestly, I must say, I was thinking to abandon FTE because of the lack of dynamic parallel lights, but I'll definately look at your code.
I'll let you know if I succeed.
Thanks again
I'll let you know if I succeed.
Thanks again
Meadow Fun!! - my first commercial game, made with FTEQW game engine
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
Just some heads up: could you please tell which files should I look into it?
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Re: [FTE]Directional dynamic light implementation
I would recommend going backwards in commit log and studying the diffs rather that going through individual files. But most of the actual stuff happens in (surprise!) in r_light.c and r_shadow.c, with bits and pieces scattered across other ref_gl files.toneddu2000 wrote:Just some heads up: could you please tell which files should I look into it?
-
- Posts: 1395
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: [FTE]Directional dynamic light implementation
can you please tell me how to go back and forth through commits in GitHub...I'm pretty noob about it.. ![Embarassed :oops:](./images/smilies/icon_embarassed.gif)
![Embarassed :oops:](./images/smilies/icon_embarassed.gif)
Meadow Fun!! - my first commercial game, made with FTEQW game engine