Game Development Community

Multi-Pass Shaders

by PrvtHudson · in Torque Game Engine Advanced · 04/09/2007 (1:35 pm) · 20 replies

Has anyone seen a resource/example of a multi-pass ("render to texture") around here. I am just beginning shaders and can't find anything recogniseable. I want to impliment shaders, from a book, as I learn.

thanx R

#1
08/16/2007 (6:20 pm)
Amazingly, I have found no resources anywhere on GG's site regarding this issue (apart from a barely related cursory mention about the TorqueScript pass[] array of CustomMaterial objects).

Did you ever figure this out, Randy? I need to get render targeting working, don't even know where to start. Is it all C++ coding, or is there support for render targets in TorqueScript? If my CustomMaterial has passes defined in it, is the texture bitmap associated with subsequent passes presumed to be a render from the previous pass, or do I need to somehow explicitly point to the render?

Is there support for alternate vertex buffers (i.e. besides the mesh to which the material applies) for a pass? If so, how is it done?

Inquiring minds yadda yadda yadda... surprising to find nothing on this subject anywhere.


-- M. Cooper
#2
08/16/2007 (7:51 pm)
Render-to-Texture would require some source changes. I believe dynamic cubemaps use this, as well as Legacy terrain.

Milo, there is no render target support in Torque Script, so all of your render to texture and render target code needs to be in C++.

I don't think pass[i] actually uses previous material information.
#3
08/16/2007 (8:50 pm)
Quote:Milo, there is no render target support in Torque Script, so all of your render to texture and render target code needs to be in C++.

*Sigh* Here we go again...

Is there any documentation on TGEA's rendering architecture? If I'm going to have to wing it on yet another implementation of basic functionality, I'd rather not have to spend another 3+ days grepping, Google hunting, and pulling teeth in these forums. Thus far, all I've found to get me going is this.


-- M. Cooper
#4
08/16/2007 (10:50 pm)
Unfortunately the GFX documentation is a bit lacking. I haven't seen any resource around using render targets, but the glow buffer would be the place to start. (It uses them extensively)
#5
08/16/2007 (10:59 pm)
Yeah, I noticed the glowBuffer.cpp render targeting shortly after my last post. Seems like there's some helpful stuff in there.

I'm looking over the various CustomMaterial methods, now... looks like it's going to be another long couple of days. @!#$


-- M. Cooper
#6
08/17/2007 (9:11 am)
There's actually two separate concepts here:

Multi-pass rendering, which is what the CustomMaterial pass[n] array allows you to do. This renders any geometry with that material n times and allows you to do blending between the passes. See the "blendOp" member of CustomMaterial for more info there. So, this is the standard way to compute pieces of a function piecemeal and combine the results via blending (this concept back to FF days).

Matt is totally right about using the GlowBuffer as a Render to target example. RTT normally needs more setup than our Material system provides to script, so C++ coding is in order.

Hope that helps!
#7
08/17/2007 (9:41 am)
Hey there is a Render to Texture sample in RenderMonkey:), I have been trying look through a lot of their code, trying to make a better form of a decal projector;)
#8
08/17/2007 (1:23 pm)
Say Brian, I'm coding this now and I'm trying to figure out where it's safe to pop render targets off the stack.

Where in the code does a render operation end (i.e. immediately after drawing to the frame buffer)?

EDIT: Looks like it's just after the while( mat && mat->setupPass( sgData ) ) loop in renderMeshMgr.cpp, is that right?


-- M. Cooper
#9
08/17/2007 (2:32 pm)
Yup, for a batch of meshes, that's the end of rendering.

Each RenderManager is in charge of a batch of geometry. So if you only need to worry about DTS based objects, that's the spot where rendering "ends".
#10
08/17/2007 (2:57 pm)
Yeah, for now, I'm just going to focus on DTS objects.

Specifically, I'm interested in the scope of code that constitutes all of the passes for a single material. Is this represented by that while loop? Without a lengthy explanation, how is it determined which geometry a RenderManager will oversee?

I'm assuming that each "batch" of meshes comprises one and only one material. I wouldn't want to push a single render target stack for multiple materials...


-- M. Cooper
#11
08/17/2007 (5:03 pm)
The batch can contain multiple materials. But the while loop you pointed out is processing one material. So you can push and pop around it to get the behavior you want.

I quickly threw together this article on TDN to give an overview of the batching system. Hope this helps!
#12
08/17/2007 (5:48 pm)
Brian, thanks for the article, I'll be sure to check it out.

Quick question, do the indices of the texture[] array in TorqueScript correspond to the S registers in HLSL shaders?

e.g. Can I reference texture[5] in my CustomMaterial with uniform sampler samplerTarget : register(S5) in my HLSL shader code?


-- M. Cooper
#13
08/17/2007 (5:56 pm)
Yes, they do.
#14
08/19/2007 (7:41 pm)
OK, it's a couple of days later and I appear to have it working. I'm having the engine spit out PNG files of my render target buffers, and I consistently get good, predictable results.

It seems to work well, but I added about fifty extra lines of code spanning seven files. I'm still hoping that someone else will pitch in on this; maybe they'll show me something that I missed, or some nifty optimization that I overlooked.

I want to run more tests before I post my code. I'll do so later this week, if anyone is still interested then.


-- M. Cooper
#15
08/19/2007 (11:04 pm)
Milo, please keep us updated, as this seems like extremely useful information. I wish I could help more, but my knowledge of the GFX layer and general C++ is very minimal. Still, I'd like to see what you've done and I'm willing to offer any help I can.
#16
08/22/2007 (5:19 pm)
Brian, I am having a lot of trouble trying to get my render targeting working with multi-pass shaders.

I read your article on the rendering pipeline, but I just want to be clear on what exactly is going on. Please tell me if any of this is incorrect:


(1) A list of render instances is generated, sorted by material. Each instance represents a mesh.

(2) Iteration through the instances begins.

(3) The renderer notes the material of the current instance.

(4) The renderer draws pass 1 for every mesh using the material, then pass 2 (if it exists) for every mesh using the material, then pass 3 (if it exists), then pass 4, etc.

(5) The renderer proceeds to the next instance (if any), whose material is necessarily different from the one that it noted in (3).

(6) Go to (3).


-- M. Cooper
#17
08/23/2007 (8:40 am)
Milo,

That looks good, what kind of problems are you running into?
#18
08/23/2007 (2:56 pm)
Quote:That looks good, what kind of problems are you running into?

Well, I've got two major issues, really:

(1) If the shader is rendering to a target, then I tell GFX to pushActiveRenderSurfaces(), setActiveRenderSurface(), and clear(), if it hasn't previously done these things. If the shader isn't rendering to a target, and I previously called those three functions, then I make sure that GFX has done a popActiveRenderSurfaces() before it draws any more meshes. Given this, I expect the final result of a pass that renders to a target to show all visible meshes of that material in the render target buffer. What I'm seeing, though (when I tell the engine to output a PNG file of the render target buffer), is a single mesh in the render target.

For example, if I've got two meshes that use a material that renders to a target on the first pass, then the first time the PNG file is generated, it will show only mesh 1. The next time it's generated, I will see only mesh 2. After that, it shows only mesh 1 again, then only mesh 2, then only mesh 1, then only mesh 2, ad infinitum.

Again, since the engine renders all meshes per pass, I would expect to see both meshes in the render target buffer. Oddly, if I comment out the GFX->clear() call, then the render target never erases, and the resulting PNG image additively shows every frame that's rendered (picture a double-buffered screen update that never erases the offscreen buffer).


(2) As one might expect, I've got to render the second pass using a screen-aligned quad, and I really don't know enough about the idiosyncrasies of the engine to do this with conviction. There appears to be code along these lines in glowBuffer.cpp, but it's too esoteric and ad hoc for me to apply to my situation.

Currently, I'm just creating a general purpose vertex buffer representing this quad, and preparing it for my pass #2 shader using GFX->setVertexBuffer(). Having done this, I instruct the RenderMeshMgr::render() function to GFX->drawPrimitive(GFXTriangleFan, 0, 2) (instead of GFX->drawPrimitive(passRI->primBuffIndex)), expecting the quad vertex buffer to get passed to the shader, but so far, I have gotten very poor results (i.e. either nothing or a screen full of garbage).



As always, thanks for your input/advice. I've taken my code about as far as my knowledge of the engine and D3D will allow, and I've been stuck for a couple of days. I think it comes down to not knowing enough about the engine, at this point.


-- M. Cooper
#19
08/24/2007 (8:54 am)
1. It almost just sounds like it's calling clear too much. I'd get a simple scene going and just step through it with the debugger to see what is going on exactly.

2. Re: Screen full of garbage almost sounds like something just isn't init'ed? Textures, shaders, etc?

Sorry I'm not more helpful, at this level, it's hard to debug without looking at the source itself. Don't give up!
#20
08/24/2007 (11:45 am)
Just an FYI for anyone who might be interested:

I made some progress after discovering that some information on this page is incorrect (or at least, incomplete). To wit:

Quote:Preload will force the Material to be fully loaded upon creation of itself (original behavior). CustomMaterials are already set to do this, since most of the default ones are important for Water, Atlas, etc.
I discovered that sub-pass custom materials are not coded to auto-preload by default. A comment at the beginning of the definition of CustomMaterial::setStageData() alludes to this fact:

// This is necessary to insure that fallbacks are loaded when 
// MaterialList::mapMaterials() is called - since it only calls
// setStageData() on top level material

I had to tell my sub-pass custom materials to preload, otherwise their setStateData() function wouldn't be called, in which crucial variables are initialized. Once I did that, I obviously started to get much better results, and was able to continue debugging my code.

I'll let everyone know whether I get everything worked out.


-- M. Cooper