Horde3D

Next-Generation Graphics Engine
It is currently 18.10.2018, 20:59

All times are UTC + 1 hour




Post new topic Reply to topic  [ 8 posts ] 
Author Message
PostPosted: 18.01.2010, 11:05 
Offline

Joined: 08.11.2006, 03:10
Posts: 384
Location: Australia
I'm trying to implement properly lit alpha into the forward and deferred pipelines (for comparison with inferred lit alpha), but I'm not sure of the best way to achieve this.

As far as I can tell, the current forward lighting technique (additive multi-pass) doesn't work correctly with alpha-blending - each successive light removes more of the background colour. To make it work for alpha-blending, you'd have to accumulate the lit colour of each object to a temporary texture, and then alpha-blend that texture into the frame-buffer.

Alternatively, you could limit alpha objects to a single lighting pass, which still supports alpha blending. The downside is you have to put a restriction on the number of lights.

To implement this option you could make a new pipeline command "DoForwardLightPass" (to accompany "DoForwardLightLoop"), which would only render each object at most once, by only using the single most important light affecting each object.
Code:
      <DoForwardLightPass context="TRANSLUCENT" class="Translucent" order="BACK_TO_FRONT"/>

This could also be implemented by extending "DoForwardLightLoop" to take a few more parameters
* The number of lights to pack into each pass
* The maximum number of lighting passes (limit this to 1 for the translucency pass)
Code:
      <DoForwardLightLoop context="TRANSLUCENT" class="Translucent" order="BACK_TO_FRONT" lightsPerPass="4" maxPasses="1"/>
This implementation could also be used to optimise the forward-lighting of opaque objects, as you could greatly reduce the number of batches required with many lights.

The current shaders only support a single light, but they could support more lights by having an array of lighting uniforms.
To choose which lights to compute in the shader, you could either
* add a shader permutation for every value of "lightsPerPass"
* have the shader specify a maximumvalue of "lightsPerPass", and
--* add a "lightCount" uniform and use dynamic branching to skip unused lights.
--* fill unused lights with black/no-radius/etc and always compute all lights.

Another complication here is that if multiple shadow-casting lights are used in the one pass, we'll need to allocate more shadow-map buffers.


I've only had a quick peek at the Horde code to see how this kind of extension could be implemented, but it seems like a bit of work :(
Currently the renderer seems to do:
Code:
for each light
  for each model
    draw( light, model )
However, in cases where lightsPerPass is not one or maxPasses is not zero, this would require something like:
Code:
lightGroup = vector of lights
for each model
  if( maxPasses )
    sort lights by importance to this model
  passCount = 0
  for each light
    lightGroup.push( light );
    if( last light in list || lightGroup.size == lightsPerPass )
      draw( lightGroup, model )
      lightGroup.clear()
      if( maxPasses && ++passCount >= maxPasses )
        break


Top
 Profile  
Reply with quote  
PostPosted: 19.01.2010, 01:01 
Offline
Engine Developer

Joined: 10.09.2006, 15:52
Posts: 1217
First, thanks a lot DarkAngel for all your input! Always appreciated :)

DarkAngel wrote:
As far as I can tell, the current forward lighting technique (additive multi-pass) doesn't work correctly with alpha-blending - each successive light removes more of the background colour. To make it work for alpha-blending, you'd have to accumulate the lit colour of each object to a temporary texture, and then alpha-blend that texture into the frame-buffer.

I haven't tried that but I think you could first render the transparent objects in some sort of ambient pass using the mode 'Blend' (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) and after that accumulate the lighting using 'AddBlended' (GL_SRC_ALPHA, GL_ONE) in multiple passes.

Anyway, for forward shading performance, it would be good to support multiple lights per pass as you propose. The main reason why we don't have that yet is that besides the complications you listed, it breaks generality. Currently, light sources could for example have any number of projective textures (within the hw limits of course). This would not be possible or much harder when combining several lights in a pass. The other problem are shadows as you mention. We would have to use several shadow maps which increases the risk of running out of samplers. This can be overcome by decoupling the shadowing from lighting and generating some shadow occlusion texture but it complicates things more and more. Going deferred is really a relief here.

We could introduce the concept of simple lights which don't have projective textures or shadows. Those would be easy to handle in a single pass and could be vectorized nicely. Adding support for that to horde will take a bit of time (although it shouldn't be difficult). The nice thing is that if we have light groups affecting individual object and some prioritization scheme, it would be easy to add some more advanced ambient solution (like light probes).


Top
 Profile  
Reply with quote  
PostPosted: 19.01.2010, 06:10 
Offline

Joined: 08.11.2006, 03:10
Posts: 384
Location: Australia
marciano wrote:
I haven't tried that but I think you could first render the transparent objects in some sort of ambient pass using the mode 'Blend' (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) and after that accumulate the lighting using 'AddBlended' (GL_SRC_ALPHA, GL_ONE) in multiple passes.
Hmm, you might be right, I'll give this technique a go -- it should only take a minute to set up in the pipeline!

[edit] I haven't tried it yet, but I think the math works out perfectly with this technique. Without any ambient lighting, you could just use zero/black in the initial alpha-blending pass.

Also, even if you render multiple lights per pass, you may still want to perform multiple passes (e.g. 16 lights with 8 per pass), in which case this technique is important for alpha-blending to always be available without placing limits on the number of lights per object.

A = Alpha
D = Diffuse texture
M = aMbient lighting
L# = Lighting
FB = existing Frame Buffer value

Single-pass algorithm:
FB = (M+L1+L2)*D*A + (1-A)*FB

Your multi-pass algorithm:
FB = M*D*A + (1-A)*FB
FB = L1*D*A + FB
FB = L2*D*A + FB
or on one line:
FB = L2*D*A + L1*D*A + M*D*A + (1-A)*FB
Which simplifies down to the original single-pass result of:
FB = (M+L1+L2)*D*A + (1-A)*FB

The only problem is I don't think this can be implemented with the current pipeline commands. I need to execute:
Code:
for each model
  draw ambient pass
  for each light
    draw light pass
But the current pipeline commands will only allow me to execute:
Code:
for each model
  draw ambient pass
for each light
  for each model
    draw light pass
which means the draw-ordering will be incorrect.[/edit]

Quote:
it breaks generality. Currently, light sources could for example have any number of projective textures (within the hw limits of course).
...
We could introduce the concept of simple lights which don't have projective textures or shadows. Those would be easy to handle in a single pass and could be vectorized nicely.
Yeah I didn't think about how flexible horde currently is with light shaders/materials... It is possible to create complex lights right now that can't easily be batched together.
e.g. Putting a projected-texture-spotlight and a simple point-light into a single pass would make for very complex shaders!

I guess these "simple lights" could be similar in structure to GL's fixed-function lights? Or something slim like the existing lighting uniforms:
Code:
vec4 lightPos // radius in w
vec3 lightDir
vec3 lightColor
float lightCosCutoff
^If this is going to be turned into an array, you could probably move 'lightCosCutoff' into lightDir.w, and while we're at it, an extra parameter like a brightness-multiplier could be stored in lightColor.w (seeing as vec3 uniforms will be placed in vec4 registers anyway).

Also, it would probably make sense to only batch lights that share the same shader and context.
Initially, you could also restrict batching for lights that have shadows. Later on this restriction could be lifted if the shadow mapping code is made more flexible.

Actually, in cases where lights can have extra parameters than what horde provides (i.e. not a "simple light"), you would probably have to restrict batching to lights of the same material instead of same shader, so, for example, the projected texture for both lights is the same.
This might actually alleviate the need for a definition of what a "simple light" is -- any non-shadow-casting light using light.material.xml will act like a "simple light" (in that they'll all get batched together, but not with custom lights or shadow-casters) -- and also allows custom lights to be batched together in the same manner.


Top
 Profile  
Reply with quote  
PostPosted: 21.01.2010, 01:25 
Offline
Engine Developer

Joined: 10.09.2006, 15:52
Posts: 1217
DarkAngel wrote:
...which means the draw-ordering will be incorrect.

Hmm, currently I don't see what you mean.

DarkAngel wrote:
^If this is going to be turned into an array, you could probably move 'lightCosCutoff' into lightDir.w, and while we're at it, an extra parameter like a brightness-multiplier could be stored in lightColor.w (seeing as vec3 uniforms will be placed in vec4 registers anyway).

Right, that makes sense!

DarkAngel wrote:
Also, it would probably make sense to only batch lights that share the same shader and context.
Initially, you could also restrict batching for lights that have shadows. Later on this restriction could be lifted if the shadow mapping code is made more flexible.

Actually, in cases where lights can have extra parameters than what horde provides (i.e. not a "simple light"), you would probably have to restrict batching to lights of the same material instead of same shader, so, for example, the projected texture for both lights is the same.
This might actually alleviate the need for a definition of what a "simple light" is -- any non-shadow-casting light using light.material.xml will act like a "simple light" (in that they'll all get batched together, but not with custom lights or shadow-casters) -- and also allows custom lights to be batched together in the same manner.

Thanks, that's all great input for the implementation. I agree that light batching makes sense and that it can be realized. Let's put this to the backlog, although I don't know yet when we will have a chance to implement it. There is still some other important things that should be finished for the next version... ;)


Top
 Profile  
Reply with quote  
PostPosted: 21.01.2010, 04:12 
Offline

Joined: 08.11.2006, 03:10
Posts: 384
Location: Australia
marciano wrote:
DarkAngel wrote:
...which means the draw-ordering will be incorrect.
Hmm, currently I don't see what you mean.
For this to be correct, all alpha objects have to be drawn back-to-front. There can be multiple passes per object, but a distant object has to complete all of it's passes before you start doing any passes on a close object.

For example, consider two objects with 50% with ambient lighting and one light:
Background: Blue
Object 1 (far): Red, 75% alpha
Object 2 (near): Green, 75% alpha
Ambient light: Black
Directional light: White

To draw a pixel covered by both of these objects and lit by this light, the correct order is "for each object (sorted by depth), for each light":
Code:
FB = Blue //clear background
//object 1
FB = Black*Red*0.75 + FB*0.25 // ambient light
FB = White*Red*0.75 + FB //directional light
//object 2
FB = Black*Green*0.75 + FB*0.25 //ambient light
FB = White*Green*0.75 + FB //directional light


However, the only order that Horde supports is "for each light, for each object (sorted by depth)":
Code:
FB = Blue //clear background
//ambient light
FB = Black*Red*0.75 + FB*0.25 //object 1
FB = Black*Green*0.75 + FB*0.25 //object 2
//directional light
FB = White*Red*0.75 + FB //object 1
FB = White*Green*0.75 + FB //object 2

This will produce a different result, with distant objects appearing to be in front of near objects.

So, from what I can tell, there's currently no way to do lit alpha in Horde (Except with the inferred pipeline! :D )


Top
 Profile  
Reply with quote  
PostPosted: 22.01.2010, 21:02 
Offline
Engine Developer

Joined: 10.09.2006, 15:52
Posts: 1217
DarkAngel wrote:
For this to be correct, all alpha objects have to be drawn back-to-front. There can be multiple passes per object, but a distant object has to complete all of it's passes before you start doing any passes on a close object.

Oh right, although the light accumulation is commutative even for transparent objects, the "ambient" pass which does alpha blending is not.


Top
 Profile  
Reply with quote  
PostPosted: 09.08.2010, 11:31 
Offline

Joined: 15.02.2009, 02:13
Posts: 161
Location: Sydney Australia
Hey, has there been any more discussions or implementations of having simple lights which could be batched in a single pass with the ambient model shader?

While currently testing on the ES branch for my N900, the epic framerate lag is caused by the multi-pass light on top (zooming into the knight is like 1-5fps) while disabling lights and only having the ambient cube map term applied the framerate visually never drops from 30fps.

Currently I'm thinking of just making a new custom model.shader which combines as much as possible into the one pass, but the simple lights idea would be really handy, or could I just use that unique uniforms per node approach to set simple lights that way? I don't mind breaking generalisation for my "game" I just want frame-rate. :D

Actually the game "concept" I have doesn't really need too many dynamic lights, I'm thinking of just baking everything to cube maps, and doing radiosity normal maps for backgrounds like valve do or unreal's lightmass (1st texture for XYZ normal direction of light, 2nd texture for the colour average). Unfortunately deferred shading techniques would be too costly on mobiles currently (no MRTs so multi-pass buffers + 8-bit per channel RGBA's are considered "costly", let alone 16-bit per channel RGBA's).

_________________
-Alex
Website


Top
 Profile  
Reply with quote  
PostPosted: 26.08.2010, 19:41 
Offline
Engine Developer

Joined: 10.09.2006, 15:52
Posts: 1217
Hi MistaED, sorry I missed your post.

If you are fine with a "hardcoded" solution, I would suggest to create a new ubershader which can handle ambient, the sun and maybe a dynamic light (depending on what you need) in a single pass. It should actually be quite straightforward, you just need to add a few new lighting uniforms (analogous to the light uniforms we already have) and you can just pick the nearest light nodes to fill the uniform data. In the long term I would like to have a clean system for registering different uniform types (global, per-node, material) but this is really for the future.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group