Skip to main content
GameDev.net gamedev.net
🔒 Locked

Engine-Shadow Integration Question

Started by Julian Spillane Aug 8, 2006 at 10:09 AM 15 replies 4.6k views
Original Post
Julian Spillane
Julian Spillane
Hey all, My team and I have recently redesigned a large portion of our game's engine to be more flexible for use in future games and for potential licensing. In the engine, shaders can be loaded into a global resource pool and can be then bound as 'global shaders', or they can be loaded in with a material file and bound to a specific geometry. In the render loop, the geometry is sorted by material and rendered with the appropriate shaders unless global shaders have been activated in which case they take priority. Using our system I have been able to implement shadow mapping rather effectively, but I wanted to solicit opinions on integrating shadowing into the engine itself. I don't want to limit users to shadow mapping, or even a specific technique of shadow mapping (VSM, PSM, etc.), but a part of me wants to just have a "EnableShadowMapping()" call that abstracts it for the user. I gave a bit of a background on our material-shader system because its architecture comes into play in implementing the shadow maps. Another problem is that shadow mapping would have to be implemented into the end-user's pixel shader anyways (since we have no fixed function support), so would there be a point in abstracting it? Thanks for taking the time to consider this post. :) Best Regards, Julian
HellRaiZer
HellRaiZer
Hi Julian...

I was thinking of making a similar post, because i am too in the process of altering our shadowing system, in case to be more flexible.

First of all one question. How are you planning to support completely different shadowing techniques, like i.e. PSSM for directional lights (needs multiple passes with regular shadowmaps), point light shadowmaps (needs 6 passes with depth encoded color cubemap), shadow volumes (needs rendering completely different geometry to the stencil buffer), etc. ? Are you going to do it with an extra pass which will be blended with the incoming lighting shader? Or you will put it inside the lighting pass? (This isn't possible with shadow volumes; they need an extra pass to render the volumes).

I'm thinking of an extra pass which will then be blended with the frame buffer. This way the lighting shader doesn't need to know the shadowing technique, and you can change algorithms on the fly (supposed you have the needed data; see sv)

The biggest problem i found so far is with "non-regular" geometry. With it i mean everything else, other than completely opaque and static objects. If a piece of geometry is using alpha testing (common for outdoor scenes, where you can apply many shadowmapping techniques), then the shadow shader needs to know that in case to function as needed. Or if you are doing skinning (or anything else that alters the input vertex data) on the GPU, then again the shadowing algorithm must know about it in case to adapt and render the piece of geometry correctly. Even worse, there may be geometries for which the main shader alters their depth inside the fragment program.

Sorry if this isn't what you were looking for, but because i started to work on a similar system, i thought it may be usefull to present some potential problems. I haven't worked much on the subject, so i'm still searching. If the above don't apply to your situation then simply skip it. I hope you don't take it as hijacking. :)

If someone has any comments i would be really glad to hear them.

HellRaiZer
HellRaiZer
Matt Aufderheide
Matt Aufderheide
I cant comment on specifc implementions of your engine, but in my view a modern engine should have one shadowing method/look that can be applied to all things.. both with direcitonal and point lights. Eitehr choose shadow volumes or shadow buffers, but optimize for one. Shadow maps are the way of the future though.

HellRaiZer
HellRaiZer
Quote:

I cant comment on specifc implementions of your engine, but in my view a modern engine should have one shadowing method/look that can be applied to all things.. both with direcitonal and point lights. Eitehr choose shadow volumes or shadow buffers, but optimize for one. Shadow maps are the way of the future though.

I agree that having a limited set of shadowing algorithms (e.g. shadow volumes and depth-based shadow maps only), can be good because you can optimize them as much as it takes. This is really hard if you are planning to support every different shadowing techniques (from distance based color-encoded maps to hybrid sv/shadowmap), because there are way to many parameters to keep in mind when designing such a system. And if you aim for real-time frame rates, then i guess you must stick with one or maybe two different techniques. I say one or two because i don't count static lighting. I'm talking only for dynamic methods.

Sometimes i try to abstract things too much, so that's why i started thinking of such a system. If someone has done something like this before with success, and want to post some comments, it would be nice.

HellRaiZer
HellRaiZer
Jason Z
Jason Z
It sounds to me like in both cases the engine needs to be more flexible. The application should be able to use the renderer to implement the shadowing algorithm of choice. Whether there is a default version supplied with the engine is just a matter of implementing some form of it yourself before shipping it to the customer.

My engine uses the idea of 'renderviews' to encapsulate one rendering sequence. For instance, shadow map generation, reflection/refraction map generation, post processing, paraboloid map generation, etc... Each render view has a default effect file associated with it.

The renderer collects objects through the scene graph, then starts rendering all of the views that are needed. Each object has the opportunity to override the default effect file with its own - so skinned geometry or a skybox or whatever can have special behaviour if needed. This is done with an array of indices to effect files. All views are allocated a spot in the array, and each object gets its own copy. At load time the object can specify its special purpose effect file by simply putting the index of the special file into the array corresponding to the renderview in question. This is typically done with a script file at load time to make the whole system data oriented.

With this type of a system, I haven't found ANY algorithm that I couldn't implement into a render view, not just shadowing alrgorithms. So an abstraction layer between the engine and the application will likely be a good solution to the problems that you are describing.
Jason Zink :: DirectX MVP   Direct3D 11 engine on CodePlex: Hieroglyph 3 Direct3D Books: 
HellRaiZer
HellRaiZer
Hi Jason...

What follows is my thoughts on the problem. You can skip them directly to '=======' for my new perspective on things after reading your reply.

==========================================================
I've thought of something similar to what you describe, with the difference that i don't have a 'renderview'. Instead of that, the renderer executes a fixed number and type of passes for rendering the scene, and i have other classes (e.g. dynamic textures, shadow methods), which are responsible for creating special textures. E.g. a dynamic texture can be a dynamic cubemap and a shadow method can be a simple uniform shadow map.

What i was thinking, in case to make the shadowing system more flexible, was to separate the shadow generation from the actual material shader (the main shader applied to an object), so the main shader doesn't know anything about the shadowing method. It just has a lighting pass which will be blended with the shadow result. This is usefull if you are going to support both texture based shadows (shadowmaps) and shadow volumes, because shadow volumes render completely different things to completely different places (the stencil buffer).

Now lets take a simple shadowing technique (uniform depth-textured shadows) with a simple shader (dot3specular). The shader (dot3specular) will have a single ambient pass, and a lighting pass (one for each type of light). The lighting passes doesn't know anything about the shadowing method, so they don't have any shadowmap textures in them (as i said i though it this way in case to be able to support shadow volumes).
Now, for the shadowmap i have to somehow create the depth texture for the light pov, and project it on the scene (in the alpha channel of the framebuffer for alpha blending the result with the lighting pass). Lets take texture creation first. For a simple shader like this i can render to the depth buffer only but disabling color writes and use the ambient pass. This is the easy way, because if the shader has somekind of vertex transformations in it, it can be handled without special case code. The same will happen if the shader did alpha testing. We don't care. It should already been handled inside the ambient pass, so no need to worry.
On the other hand, the projection can be tricky. If the shader did skinning on the GPU (or per-vertex displacement, morphing, etc.) i must write a special projection pass supporting the extra per-vertex calculations. This extra pass must come from somewhere.

1) Either the shader itself knows about this pass, and implements it along with the rest, or
2) the shadowing technique knows about the special needs of the shader and whenever encounters an object using this shader, it is going to use the special pass instead of the default (default = no vertex displacement, no depth calculations in fp).

The problem with the first approach is that whenever i write a new shadowing technique, i have to alter all shaders to be aware of it. Not exactly all of them, but the "problematic" ones.
The problem with the second approach is that whenever i add a new shader (which can't use the default creation/projection pass) i must update the shadow method code in case to handle the new special case.
============================================================

Let me see if i understand what you are describing.

You have the main renderview which is responsible to render the scene correctly, using the material/shader bound to each object. Something like this:

class RenderView{public:	virtual void Render(scene) = 0;};class MainRenderView : public RenderView{public:	virtual void Render(scene)	{		Find visible objects from camera.		Sort objects by shader.		for each object 'obj' in the list		{			Shader = obj->GetShaderForRenderView("MainRenderView");			// Shader can't be null because every object must have a shader for the "MainRenderView"			for each pass 'pass' in the shader			{				pass->Bind();				DrawObject(obj);				pass->UnBind();			}					}	}};


Now suppose you are going to write a new shadow algorithm. Lets take VSM for example (NOTE : i haven't worked with VSM so forgive any mistakes in the explanation). IIRC VSM needs two floating point values per-pixel. The pixel's depth and depth squared. So you create a new RenderView which in its default shader does just that for static geometry with no special per-fragment operations (alpha testing, per-fragment depth calculations, etc.)

class VSMRenderView : public RenderView{	virtual void Render(scene)	{		Find visible object from light		Sort object by distance (front to back)		for each object 'obj' in the list		{			Shader = obj->GetShaderForRenderView("VSMRenderView");			if(Shader == NULL)				Shader = DefaultVSMShader;						for each pass 'pass' in the shader			{				pass->Bind();				DrawObject(obj)				pass->UnBind();			}		}	}};


And you have your texture ready. Till now everything seems logical and clear (i hope i understood it correctly), with the one exception that i have to ask for the shader by the renderview name (i guess there are better ways to do it; how are you handling this).

Now its time to project the texture on the scene. How are you handling this? Because using the VSM texture needs some extra calculations inside the fragment program (it isn't just a texture fetch as of depth maps), how do you know if a piece of geometry is going to animation, etc. Do you have another renderview for this? Do you handle it inside the main shader?

Forgive me for writing so much, but... i couldn't help it. Anything less than that seemed to be incomplete, so i did my best for a complete view of my understanding. I'd be glad to hear your reply on the above, and i hope i'm not completely off on this.

Thanks again.

HellRaiZer
HellRaiZer
Julian Spillane
Julian Spillane
Hey guys,

Thanks for the responses. Seems that this isn't a trivial problem...

What I'm leaning towards is implementing PSSMs and Dual-Paraboloid Shadow Mapping into the engine as intrinsic shadowing functionality, and then letting the user implement their own shadowing methods if they want to. The engine is flexible enough that the user can implement their own shadowing system. I guess I just wanted to be -too- open ended, if you know what I mean.

I think taking those two methods and optimizing the hell out of them would probably be the best idea.

Thank you all so much for your input.

Cheers.
Julian Spillane
Julian Spillane
I would also like to hear more about this RenderView system you've mentioned. I'm having the same conceptual problem with it that HellRaizer has, so it'd be nice to see how you solved it.

It seems like a rather powerful solution, though, if implemented correctly.
wolf
wolf
I agree with the people that suggested to choose one shadow map method and go with it. I believe you can differ between outdoor and indoor scenarios. Both scenarios need their own solutions. Mixing up shadow volumes with shadow mapping is not a good idea, so you might go with shadow volumes for everything or different shadow mapping techniques for outdoor and indoor solutions. If you have a change between night and day ... you might even have a specific version for night scenes ... maybe a cached shadow map solution.
Depending on your requirements you might choose very different approaches for the outdoor and indoor areas. If you keyframe or script all of this, it is trivial to switch between the night and day version. Outdoor and indoor can live together, to light and shadow interiors where you see both shadows ... I call this "Dancing Shadows in a room" feature. At some point you want to switch off outdoor shadows or indoor shadows .. this might be distance or anything else.

So a max requirement list is:
- indoor shadows
- outdoor shadows to support time of day
- outdoor shadows at night
- Dancing Shadows in a Room

All this can be implemented with shadow map approaches. The most painless indoor shadow maps are cube shadow maps ... there are a couple of short cuts like leaving out a few sites of the cube. Cascaded shadow maps give you a very long shadow distance, so that you can shadow everything in the scene over several hundred meters. Outdoor shadows at night just need to catch the shadow of spotlights or pointlights that are treated as spotlights at night. You might implement a caching system that offers 32 small shadow maps to apply on selected lights.
Dancing shadows in a room is just a trick to apply shadows coming from outside and the inside easily ...
Jason Z
Jason Z
Quote:
Original post by HellRaiZer

Let me see if i understand what you are describing.

You have the main renderview which is responsible to render the scene correctly, using the material/shader bound to each object. Something like this:

*** Source Snippet Removed ***

Now suppose you are going to write a new shadow algorithm. Lets take VSM for example (NOTE : i haven't worked with VSM so forgive any mistakes in the explanation). IIRC VSM needs two floating point values per-pixel. The pixel's depth and depth squared. So you create a new RenderView which in its default shader does just that for static geometry with no special per-fragment operations (alpha testing, per-fragment depth calculations, etc.)

*** Source Snippet Removed ***

And you have your texture ready. Till now everything seems logical and clear (i hope i understood it correctly), with the one exception that i have to ask for the shader by the renderview name (i guess there are better ways to do it; how are you handling this).

Now its time to project the texture on the scene. How are you handling this? Because using the VSM texture needs some extra calculations inside the fragment program (it isn't just a texture fetch as of depth maps), how do you know if a piece of geometry is going to animation, etc. Do you have another renderview for this? Do you handle it inside the main shader?

Forgive me for writing so much, but... i couldn't help it. Anything less than that seemed to be incomplete, so i did my best for a complete view of my understanding. I'd be glad to hear your reply on the above, and i hope i'm not completely off on this.

Thanks again.

HellRaiZer

The big picture on the render view system is that you can pretty much do it however you want. The render views are built around the fact that you can set any input and output render targets for them to use, which enables you to link them together and chain multiple rendering algorithms together.

For your example, if you have a spot light in your scene and you want to render its contribution to the frame buffer, the spot light object in the scene graph creates an instance of a VSM render view and adds it to the renderer's list of views to render. The VSM render view creates a shadow buffer in its constructor, then gets a list of all of the objects that it affects from the scene graph and renders the objects with its 'VSM generation render view' standard effect to create the depth and variance data. The objects can override the standard one if needed.

Next, the VSM render view would set the frame buffer as the current render target, set the depth buffer as a texture, and render the objects again with its 'VSM illumination render view' standard effect. The object can also use their own special versions if needed.

With this method you could render the contributions of 10 different spotlights and each would alpha blend their contribution onto the frame buffer one at a time. However, you could also perform all of the depth map generation at once with VSM generation render views, then render their contributions with a separate VSM illumination render view. You can essentially make the render views as complex or as componentized as you need.

One method saves memory at the expense of rendering context switches, and the other saves context switches at the expense of using more memory. The system is inherently open ended to allow flexible implementations for different hardware, LOD, or whatever other considerations you might have. I really haven't found any situations where I can't create a render view to accommodate the algorithm at hand.

There is more involved in caching the views during a traversal of the scene graph and deciding what order to render them all in, but I'll keep the post a little shorter. If you are still interested we can continue the discussion. Does this clear it up at all?

As a side note - it wouldn't be all that difficult to mix and match shadowing systems AT THE SAME TIME with a render view system. You just have to make sure that the overall rendering methods are capable of working together, then set up the views to play nice together...
Jason Zink :: DirectX MVP   Direct3D 11 engine on CodePlex: Hieroglyph 3 Direct3D Books: 
HellRaiZer
HellRaiZer
Jason thanks for the reply.

Quote:

The render views are built around the fact that you can set any input and output render targets for them to use, which enables you to link them together and chain multiple rendering algorithms together.

I assume that you mention this because you previously said that you are using the same system for post-processing effects (a stack of them?). But i don't think this is needed for renderviews like (i.e.) the VSM, because as you say later, it creates its own shadow buffer inside. Do you mean that you set the final render target where the illumination will be rendered? This makes sense when you have a stack of post-processing effects, so i assume that for now.

Quote:

For your example, if you have a spot light in your scene and you want to render its contribution to the frame buffer, the spot light object in the scene graph creates an instance of a VSM render view and adds it to the renderer's list of views to render. The VSM render view creates a shadow buffer in its constructor, then gets a list of all of the objects that it affects from the scene graph and renders the objects with its 'VSM generation render view' standard effect to create the depth and variance data. The objects can override the standard one if needed.

This is clear enough. I hope the pseudo-code i posted earlier does what you describe. Or i am way off? I have a little problem with the following :

Quote:

Next, the VSM render view would set the frame buffer as the current render target, set the depth buffer as a texture, and render the objects again with its 'VSM illumination render view' standard effect. The object can also use their own special versions if needed.

What confuses me is the 'VSM illumination render view' (the vsm map projection pass i mentioned earlier).
1) Is this a different render view (other than the 'VSM generation render view'), or you handle it as a second step inside the generation one (first generate, then immediately project)? And if it is, how it communicates with the generation rv, in case to get the correct shadow map? How do you schedule a rv like this to be rendered when required (this is when you reach the specific spotlight, in the list of lights)? Or if it isn't, does that mean that the VSM map generation is done just before projecting it to the scene?
2) I know this is implementation depended but how are you handling the actual illumination (if you don't mind me asking so much details)? The way i see it, it can be done in two ways.

a) Either you write special case passes for (almost) all shaders (e.g. dot3specular with VSM, parallax with VSM, glass with VSM, etc.), which handle the VSM projection and the illumination in a single pass, or
b) you project the VSM shadow result on the scene, writing to the alpha channel, and then use blending and the default (no-shadows) illumination pass of each shader. Which way are you going?

Advantages of (a):
1) Single pass illumination (faster rendering, no blending => more lights)

Disadvantages of (a):
1) Special case passes for all shaders involving lights (one pass per light type per shadowing technique)

Advantages of (b):
1) No need for special case pass based on the shadowing technique (the same pass that is used for VSM can be used for no shadows, or for uniform shadowmaps with a 5x5 PCF)

Disadvantager of (b):
1) One extra pass per light in case to render the light's shadow result to the alpha channel.
2) Requires blending (slow when rendering to FP rt)

Quote:

With this method you could render the contributions of 10 different spotlights and each would alpha blend their contribution onto the frame buffer one at a time. However, you could also perform all of the depth map generation at once with VSM generation render views, then render their contributions with a separate VSM illumination render view. You can essentially make the render views as complex or as componentized as you need.

One method saves memory at the expense of rendering context switches, and the other saves context switches at the expense of using more memory. The system is inherently open ended to allow flexible implementations for different hardware, LOD, or whatever other considerations you might have. I really haven't found any situations where I can't create a render view to accommodate the algorithm at hand.

I think this answers the 2nd question above, but i'm curious how you do it (again, if you don't mind telling such info)


There is more involved in caching the views during a traversal of the scene graph and deciding what order to render them all in, but I'll keep the post a little shorter. If you are still interested we can continue the discussion.

Above i have a question on how to schedule the illumination pass, which might be tricky, depended on the way you do it. One more thing that comes to my mind now involves more local things (shadows are too global, so the way i understand it, it is possible to code something like that). What happens when a shader needs a dynamic cubemap? IIRC you mentioned that you use this system for things like that too. What i see as a problem here, is because of the local nature of cubemaps, you must create different RVs based on the object's location. Some questions:

1) Assuming that the cubemap rt is created inside the RVs (just like the VSM rt), how can the shader get it in case to use it? Are you handling this inside the RV (something like the 'VSM illumination pass')? I don't think this is the way to go, because it involves checking all the time which objects needs to rendered with a dynamic cubemap, and which of them are affected by this particular cm. Also, there are many ways you can use a color cubemap (reflection, refraction, using mask textures for them, diffuse reflections, etc.)
2) Are you creating a different cubemap per object? Do you have some way of telling that two (e.g.) reflective objects are close together in case to use the same cm on both of them?
3) (Implementation depended) Are your CMs "double buffered" (meaning that they hold the previous frame) in case to be able to see reflections inside reflections? Is this the Rv's responsibility to do it, or it is coded somewhere else (i assume at the same place where the communication between the shader and the RV is done, but... i had to ask)
4) About scheduling. One way i can think is by using priorities. E.g. shadowmap generation RVs (whatever technique they use) must be rendered before everything else, firstly because they don't use are color info from the scene, and secondly because you may want to render shadows inside the reflection CMs. Have you assigned fixed priorites based on something like that, or it is a more dynamic system?
5) Suppose a dynamic CM need to render shadows in it, does it use the same RV system to render the six faces with shadows, or it uses its own way to render the scene? In other words, do i need to have a special RV which will render the scene in a predefined order (i.e. ambient => illumination => translucent objects => fog), or this can be broke into different RVs with predefined priorities, so the result is correct?

Quote:

Does this clear it up at all?

Yep. It clears many things, and if you answer (some/all of) the above i think the whole procedure will be crystal clear :)

Thanks again, and sorry for the lengthy posts. I'm really interesting in your way of handling things, so i try to find every possible fault in my thoughts. And please forgive my English. I hope you can find some time answering.

HellRaiZer

============================================================================
EDIT :
Immediatelly after posting more questions came to my mind. The main problem i have is how am i going to inform existing shaders for new RVs. But i think i found a way that doesn't need to alter existing code. Just write the new special passes (e.g. VSM projection on skinned alpha tested geometry) as new shaders and attach them to the appropriate effects.
I think you mentioned something like this, but now it clicked inside my mind :) This can be done with a simple text file specifying the shader to use for every new render view.

Effects.txt
Effect("Dot3Specular"){	MainRenderView = "Dot3Specular_Shader";	// Includes an ambient pass, and one lighting pass for each type of light. Each one will be executed when and if needed.	VSMRenderView_Update = "VSMUpdateDefault_Shader";	// You can skip this because it is the default. If we don't find it we are going to use the default.	VSMRenderView_Project = "VSMProjectDefault_Shader";	// You can skip this because it is the default.	ReflectCubemapRenderView = "Dot3Specular_Shader"	// This way we can fallback to something faster for reflection CMs.}Effect("Dot3Specular_Skinning_AlphaTest"){	MainRenderView = "Dot3Specular_Skinning_AlphaTest_Shader";	// Includes an ambient pass, and one lighting pass for each type of light with. Does skinning on the GPU with alpha testing enabled.	VSMRenderView_Update = "VSMUpdate_Skinning_AlphaTest_Shader";	// Calculate VSM related values, for a skinned, alpha tested piece of geometry.	VSMRenderView_Project = "VSMProject_Skinning_AlphaTest_Shader";	// Project a VSM texture on a skinned, alpha tested piece of geometry.	ReflectCubemapRenderView = "Dot3Specular_Skinning_AlphaTest_Shader"	// The same as the MainRenderView}


This way there is no need to alter (insert passes to) the main shader (e.g. Dot3Specular_Shader), because we assign a whole new shader for a specific renderview.
Any potential problems with that?

Two more questions:
1) In case to update a texture (e.g. VSM map, dynamic CM) you have to find all visible objects from a different pov. How do you know that the extra render views aren't already pushed in the list of RVs to update in this frame? Let me explain.
You traverse the scenegraph once in case to collect all RVs which need to be updated in this frame (probably inside the renderer). Now, when executing (e.g.) a VSM map update, you have to traverse the scenegraph in case to find all visible object from the light's pov. Doesn't this add again all the RVs in the renderer's list? Or you have different entry points for that? One for collecting RVs and one for finding visible objects?
2) If you have different entry points (which seems logical), how do you handle new visible lights introduced through reflections? Will you update the shadow textures for them? How do you know where and if they are pushed inside the renderer's RV list, in case to execute them before the actual texture update? Actually, if they are already in the list there is no problem, because we can assume they are already updated. But if they aren't, what happens?
3) You haven't answered this one yet, but assuming that you have a main render view where all objects are rendered with their default shader (by using the results of other RVs; VSMs, CMs, etc.), do you keep it separately inside the renderer? Because this one must remain all the time inside the list (along with every post-process shader), in contrast with other RVs which must be pushed whenever needed (e.g. add a VSM RV when a VSM spot light becomes visible).

I'll try not to post anything else, because this is already huge :)

HellRaiZer

[Edited by - HellRaiZer on August 10, 2006 4:26:16 AM]
HellRaiZer
Jason Z
Jason Z
I will have to post later on after work (that's a bunch of questions! :) ) but try to think of the renderview system as a tool to let you implement algorithms. The decision to use a single cube map per object or to use one CM for all objects is made at the algorithm level, so you could do it either way. My engine doesn't use cube maps very much - I typically prefer paraboloid or just planar type environment information. But again, its up to the algorithm to decide.

Also, to communicate the render view information to other render views you can do it several ways. You can define an enum and ID system and let the object that created the two render views link them together. For instance, the generation pass and the illumination pass of the VSM can be linked by the spot light object when they are created. Then the illumination render view knows to use X render target for its shadow map.

Another method is as you said, you can statically define an order to do them in. Like cubemaps first, reflection buffers second, main, alpha, silhouette rendering, post processing and so on. Basically the scene graph pushes the render views into a list and then they are sorted by their 'type' value.

I'll try to post again tonight - hopefully this answered more of your questions.
Jason Zink :: DirectX MVP   Direct3D 11 engine on CodePlex: Hieroglyph 3 Direct3D Books: 
HellRaiZer
HellRaiZer
Sorry for the delayed posting.

Quote:

The decision to use a single cube map per object or to use one CM for all objects is made at the algorithm level, so you could do it either way. My engine doesn't use cube maps very much - I typically prefer paraboloid or just planar type environment information. But again, its up to the algorithm to decide.

The question is, who defines the algorithm? Is it the render view itself (not likely)? Is it the object that requires a cm in case to be rendered correctly? Or it is some other system? I haven't worked with dual paraboloid maps, but i can assume that as far as they capture the environment around an object, they can be cached in case to be used by 2 adjacent objects.

Quote:

Also, to communicate the render view information to other render views you can do it several ways. You can define an enum and ID system and let the object that created the two render views link them together. For instance, the generation pass and the illumination pass of the VSM can be linked by the spot light object when they are created. Then the illumination render view knows to use X render target for its shadow map.

With shadowmaps this is easy. No need for any special searching (as you said). You can do it by having another class that holds both a generation RV and an illumination RV. You can create the shadowmap inside that (new) class and give it to the two RVs. When it's time to send them to the renderer, you simply push the two RVs separately, and they will read/write to the same rt, because they are configured that way.
But with other things like envmaps (either CMs or DPMs) this doesn't work, if you are going to share a single envmap between 'n' objects. This is because the RV for the envmap is one (the one which updates it), but the users can be many and different (e.g. a simple reflective shader, or a more complex fresnel effect with specular highlights; something like glass).
My problem is that when you reach the main pass (render all objects with their material defined shader; in other words render the main RV), the shader must ask for an envmap suitable for the object's position. From where should it get this info?
This may be shader system depended, and because i have my shader system in mind, forgive me if this sound too obvious to you.

Quote:

Another method is as you said, you can statically define an order to do them in. Like cubemaps first, reflection buffers second, main, alpha, silhouette rendering, post processing and so on. Basically the scene graph pushes the render views into a list and then they are sorted by their 'type' value.

Sorry for the confusion, but when i mentioned the statically defined order (priorities) i didn't have communication in mind (you say "another method", so i assume you are refering to "how can two things communicate"). What i was thinking was on how to schedule RVs correctly, so when an RV is needed it is up-to-date for the current frame.

Quote:

I'll try to post again tonight - hopefully this answered more of your questions.

If you have something more to comment on the questions in the previous post, i'd be glad to hear it. No need to hurry. When and if you can :)

Thanks again for your time.

HellRaiZer
HellRaiZer
Jason Z
Jason Z
Sorry it took so long to respond, but here goes on the questions:

Quote:
The question is, who defines the algorithm? Is it the render view itself (not likely)? Is it the object that requires a cm in case to be rendered correctly? Or it is some other system? I haven't worked with dual paraboloid maps, but i can assume that as far as they capture the environment around an object, they can be cached in case to be used by 2 adjacent objects.
The algorithm is decided on by the application or the render view - whichever makes more sense for the situation. If you have n objects in your scene that require cube maps, the application would decide if it should use a cached cube map and reuse it, or if it should use a single cube map and render each reflective object after re-rendering the cube map for that objects position, or if it should use a single cube map for each object.

In my engine, I would just use a single cube map (paraboloid map actually) per object unless it became a memory issue. If I needed to share the cube map, I would write a custom render view that would determine if the reflective objects are close enough to share, and if so it would create a separate render view to render that cube map from the center of the close reflective objects. If there were two major groups, it could spawn two cube maps and so on. In this case, the render view would be responsible for communicating the proper cube map information to the objects, since it decides which cube map is supporting which objects. Keep in mind however that this is just one solution - the beauty of the render view system is that it is completely open ended.

Quote:
But with other things like envmaps (either CMs or DPMs) this doesn't work, if you are going to share a single envmap between 'n' objects. This is because the RV for the envmap is one (the one which updates it), but the users can be many and different (e.g. a simple reflective shader, or a more complex fresnel effect with specular highlights; something like glass).
My problem is that when you reach the main pass (render all objects with their material defined shader; in other words render the main RV), the shader must ask for an envmap suitable for the object's position. From where should it get this info?
This may be shader system depended, and because i have my shader system in mind, forgive me if this sound too obvious to you.
I store a generic set of render parameters in the objects themselves. For example, each object has an array of texture IDs, an array of object parameters (float4's for shader parms), and an array of effect IDs (for the render view effect overriding discussed earlier). In the previous example, this is where the render view would communicate the cube map ID to the object.

Quote:
Sorry for the confusion, but when i mentioned the statically defined order (priorities) i didn't have communication in mind (you say "another method", so i assume you are refering to "how can two things communicate"). What i was thinking was on how to schedule RVs correctly, so when an RV is needed it is up-to-date for the current frame.
This would again be algorithm dependant, so it could be controlled by the app or the render views. Since I usually don't cache the maps, I just statically perform all of the render views like cube maps, shadow maps and so on before any of the final illumination passes. You could implement something more sophisticated if you needed to though.

I think that is everything from your last post. Hopefully that helps out. You'll have to post how you design your version and let us know how it turns out!

Jason Zink :: DirectX MVP   Direct3D 11 engine on CodePlex: Hieroglyph 3 Direct3D Books: 
HellRaiZer
HellRaiZer
Quote:

I store a generic set of render parameters in the objects themselves. For example, each object has an array of texture IDs, an array of object parameters (float4's for shader parms), and an array of effect IDs (for the render view effect overriding discussed earlier). In the previous example, this is where the render view would communicate the cube map ID to the object.

But, doesn't this imply that the main RV knows about the existence of the CubemapUpdateRV? I mean you have to get the CM texture somehow out of the RV when you need it, and the way you describe it seems like they know about each other. Am i wrong on that? Doesn't this break the flexibility of such a system? What happens if you are going to implement a RV which will render all 6 cm faces in one pass (DX10 style)? Are you going to alter the existing RV, or introduce a new one? And if you implement a new RV, doesn't the main RV have to get familiar with this RV (in case to be able to get the CM out of it)?

Quote:

I think that is everything from your last post. Hopefully that helps out. You'll have to post how you design your version and let us know how it turns out!

Thanks for the answers. I'll have work on something like this to see how and why some things are done in certain ways, and what are the benefits of having something so generic. For the moment i think i'll stick to my current design. Instead of making everything based on the same interface, i like to separate things based on what they do. E.g. a dynamic cubemap is nothing more that a texture with an update function. This holds for planar refractions and reflections (but i haven't implemented them yet). So when an material need a dynamic cm, the shader asks for one from the texture manager (and not from an arbitrary RV). Because the requested texture can be depended on the object's position, the query includes some more info (e.g. object's transformation, or the object itself). If a suitable texture is already present (caching) then it is marked for update and returned for use. If there is no texture, one is created, marked for update and returned. All dynamic textures are updated once per-frame (per-viewpoint if they need to; see planar reflections), before the main render passes (ambient -> lighting + shadows -> translucency -> fog).

As for shadows, i'm considering a similar approach, but because shadows can be non-texture-based (e.g. stencil shadow volumes), they don't inheret from the same interface. Instead, i have a shadow method class, which all shadow algorithms must implement. Every light keeps a shadow method, and when it is time to render the illumination from a light, i first lay down the shadow result (shadow method is responsible for that;e.g. render shadow volumes into stencil buffer) and then blend the actual illumination on top of that.

I know that the above have its problems, that's why i was seeking for something more flexible. But for the moment i will stick with it. Maybe in revision 2 of the engine, i'll consider altering it to something more general :) It depends on what extra problems will come up while using the current system.

Thanks again.

HellRaiZer

EDIT:
What i will certainly implement in the current version of my system, from the things discussed here, is the abilility for an effect to hold multiple shaders. This way when i'm trying to update a VSM texture i'll use the shader associated with this pass/shadow method/RV (call it whatever you like). So, i won't have the problem of non-regular geometry (e.g. skinned, alpha-tested geometry).
HellRaiZer
Jason Z
Jason Z
Quote:
Quote:
I store a generic set of render parameters in the objects themselves. For example, each object has an array of texture IDs, an array of object parameters (float4's for shader parms), and an array of effect IDs (for the render view effect overriding discussed earlier). In the previous example, this is where the render view would communicate the cube map ID to the object.

But, doesn't this imply that the main RV knows about the existence of the CubemapUpdateRV? I mean you have to get the CM texture somehow out of the RV when you need it, and the way you describe it seems like they know about each other. Am i wrong on that? Doesn't this break the flexibility of such a system? What happens if you are going to implement a RV which will render all 6 cm faces in one pass (DX10 style)? Are you going to alter the existing RV, or introduce a new one? And if you implement a new RV, doesn't the main RV have to get familiar with this RV (in case to be able to get the CM out of it)?

Not really. The cube map render view would communicate the ID to the scene objects so that they can render themselves with the cube map as a texture in the Main Render View. The Main RV doesn't know anything about the other render views at all - it just queries the scene graph for the visible objects and tells them to render themselves. The method of rendering each object is stored in its effect file that is designated for the main render view.

The thing to keep in mind is that the overall system is not designed to be able to add a new effect, drop it anywhere and the system will handle it for you. Instead, it is generalized so that any algorithm can be implemented - but the programmer still has to make sure everything will work together.

Even so, it's good to discuss these type of design issues - it is always good to hear another side of the issue. Good luck on your new design!
Jason Zink :: DirectX MVP   Direct3D 11 engine on CodePlex: Hieroglyph 3 Direct3D Books: 
t0Xic
t0Xic
I pretty much like the idea of having as many shader techniques to achieve a specific goal as you want ( e.g. shadowing, but also applies for lightning and other stuff ).

To achieve this you may build a set of shaders which not only store the actual shader code but also their requirements ( e.g. a specific vendor, support for a specific shadermodel, ... ) and some states like performance ( maybe for specific hardware ), optical satisfaction and so on and a cathegory, which they belong to.

Now a material do not say 'oh boy, oh boy, I want shader X from file Y, ...' anymore, but 'hey, gimme shadows and bumpmapping and lightning'.

Your renderer now says 'uh, we're in an indoor environement with this sort of lightsources, lets use that paticular shadowmapping technigue because it performs good on the clients hardware cauz its SM3 and do that type of lightning and because we still have plenty of performance, take the z-correct bumpmapping with you...'.


Well, that definitely includes some extra work for your developers, but now the engine can automatically balance the appearnce and the performance of your scene.

To cover the issue of global shaders you could define a set of shaders that do not need to be required by the material but rather turned off if you don't want them ( You don't want to tell every object, that it shall require beeing lit ).


t0}{!c

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.