Managing Shader Effects and Texture Units

Started by
2 comments, last by Ingenu 17 years, 6 months ago
So.... I'm one part confused, and one part looking for suggestions. I have a basic rendering engine that can do all kinds of wonderful multipass- material/effect-shader-offscreen-rendering type things, but it's not very robust yet, and I'm not sure what needs I should expect to meet with it. So, first question, if a graphics card has x texture units, does that become an actual limit to the number of textures you can read from in the shader, or can a card swap textures around so that you can read from say 8 textures on a card that only has 4 units. Second question, in todays typical setup were a renderer may have to render a model multiple times (say once for each light/shadow map, plus a number of fancy extra effects), what is the best way to set things up so that you don't end up writing special case shaders for everything, and so that you can take advantage of multiple texture units so that you can, say, do all of your lighting in one pass, and all of your effects in another pass. I hope the question isn't too general, but I'm not completly sure how much my system has to be prepared to support. I've just read posts were people mention rendering systems processing 20 lights on a model, or doing x number of shadowed lights, so I'm wondering what's a realistic target to shoot for. Let's take kind of a general case situation, though. There is a model, with three lights that are done with normal mapping, and shadow mapping, maybe some other type of effect like paralax mapping, and, just for fun, we'll throw in one more pass for a special effect, and possibly a simple vertex lighting pass for 5 or 6 less important lights. Does anyone have a good way to manage all these passes so that you take full advantage of the hardware's capabilities. Without having to write 100 different shaders? I've seen it mentioned before techniques that generate shaders on the fly, has anyone tried out something like that?
Write more poetry.http://www.Me-Zine.org
Advertisement
Quote:Original post by INVERSED
So, first question, if a graphics card has x texture units, does that become an actual limit to the number of textures you can read from in the shader, or can a card swap textures around so that you can read from say 8 textures on a card that only has 4 units.

No, but the number of texture units in shader mode is different than in fixed function mode, there are at least as many available, and often many more (like twice).

Quote:Original post by INVERSED
Second question, in todays typical setup were a renderer may have to render a model multiple times (say once for each light/shadow map, plus a number of fancy extra effects), what is the best way to set things up so that you don't end up writing special case shaders for everything, and so that you can take advantage of multiple texture units so that you can, say, do all of your lighting in one pass, and all of your effects in another pass.

If you're using DirectX, the huge DrawCall cost (due to a lame design) will prevent you from doing a lot of multipass effects (far too slow), so don't go for an approach which would require 1 pass per light ! (Even if you go OpenGL it won't hurt to go with an approach combining many lights in a pass)

Not sure what to recommend, I have RENDER_PASS which basically order Effects, that is an effect involving alpha bleding would be in the RENDER_PASS_TRANSPARENT, which is after the RENDER_PASS_OPAQUE, and is depth sorted back to front (for correct transparency)
Also you might want an additive shader approach, that is adding code in your shader depending on its effect states, such as the number and type of lights illuminating it...


Quote:Original post by INVERSED
I hope the question isn't too general, but I'm not completly sure how much my system has to be prepared to support. I've just read posts were people mention rendering systems processing 20 lights on a model, or doing x number of shadowed lights, so I'm wondering what's a realistic target to shoot for. Let's take kind of a general case situation, though. There is a model, with three lights that are done with normal mapping, and shadow mapping, maybe some other type of effect like paralax mapping, and, just for fun, we'll throw in one more pass for a special effect, and possibly a simple vertex lighting pass for 5 or 6 less important lights. Does anyone have a good way to manage all these passes so that you take full advantage of the hardware's capabilities. Without having to write 100 different shaders? I've seen it mentioned before techniques that generate shaders on the fly, has anyone tried out something like that?


Realistic target would be fully bump mapped world, with additionnal parallax mapping, and a couple (or 3) lights at once, check the number of textures required, you'll see that it goes up quickly.
(Diffuse Map, Normal Map, Specular Map (optional), Height Map (Parallax case), plus 1 Shadow Buffer per light (either 2D or CubeMap for an Omni/Point)...)

You have basically two 'broad' schools for writing shaders:
-subtractive shader in which you use preprocessor commands to enable/disable code snippet, I don't like it, it's messy IMO, require too much work, is hard to maintain and extend (We've a project with a subtractive Uber Shader at work and it's a nightmare)

-additive shader, in which you add code snippets relevant to the parameters of the geometry (quantity and types of lights...), I favore it, for it's way easier to maintain and extend but require strong design to ensure that everything goes smooth.
-* So many things to do, so little time to spend. *-
Quote:Original post by Ingenu
Quote:Original post by INVERSED
So, first question, if a graphics card has x texture units, does that become an actual limit to the number of textures you can read from in the shader, or can a card swap textures around so that you can read from say 8 textures on a card that only has 4 units.

No, but the number of texture units in shader mode is different than in fixed function mode, there are at least as many available, and often many more (like twice).


Ok, that's good to know at least. I wasn't seeing how one would efficiently pull off some of these effects with just four texture units available on some cards.


Quote:Original post by Ingenu
-additive shader, in which you add code snippets relevant to the parameters of the geometry (quantity and types of lights...), I favore it, for it's way easier to maintain and extend but require strong design to ensure that everything goes smooth.


Ok so this is the part I was curious about. I was wondering how to put into code efficiently. The first question is, when compiling a shader for any given object, do you just compile in the maximum number of supported lights and somehow turn off the lights not in use (say if you're allowed to do 4 lights on an object, but in the current scene there are only two affecting the object. Further, what's the best way to compile all these code fragments. At the moment I'm using OpenGL and GLSL. Say I have a situation where I have a code fragment that can render lighting, and a code fragment that can texture a model. The lighting code fragment will need a unique texture for the shadowmap, and the normal map from the model. Say the texturing fragment just needs the model's diffuse texture. Say you wanted to render a scene with three lights and the diffuse texture. Is there any system out there that will allow me to link up these fragments correctly and shuffle around the texture units as needed and spit out the correct GLSL code? Do I have to write this from scratch? Am I going about this the wrong way? Does this scenario make sense?

Write more poetry.http://www.Me-Zine.org
Quote:Original post by INVERSED
Ok, that's good to know at least. I wasn't seeing how one would efficiently pull off some of these effects with just four texture units available on some cards.

Well to be correct, you'll see that not all Texture Units are 'complete' like in fixed function mode, some don't have texture matrices associated and stuff like that, that's why they aren't available on Fixed Function mode.
GF5+ and RadeOn 9500+ should have as many or more Tex Units in Shader Mode than in Fixed Function AFAIR.


Quote:Original post by INVERSED
The first question is, when compiling a shader for any given object, do you just compile in the maximum number of supported lights and somehow turn off the lights not in use (say if you're allowed to do 4 lights on an object, but in the current scene there are only two affecting the object.

Turning off code in a shader would be the subtractive approach, in the additive one, you add code for required operations.
(Say add the code for each light, or add it once and change a uniform to loop through it as many times as required.)


Quote:Original post by INVERSED
Further, what's the best way to compile all these code fragments. At the moment I'm using OpenGL and GLSL. Say I have a situation where I have a code fragment that can render lighting, and a code fragment that can texture a model. The lighting code fragment will need a unique texture for the shadowmap, and the normal map from the model. Say the texturing fragment just needs the model's diffuse texture. Say you wanted to render a scene with three lights and the diffuse texture.

You can compile code snippets, provided they are complete, into shaders in GLSL, and a Program Object is made of a lot of Shaders.

You would have basic "Effects", which would be like 'Diffuse Only', 'Bump Mapping' and so on, which would be combined with other code snippets for lighting...
n Shaders in OpenGL make a Program Object, each Shader can add samplers... so it's not an issue to need more Tex Units, you just need fall under the maximum number of Tex Units available.


Quote:Original post by INVERSED
Is there any system out there that will allow me to link up these fragments correctly and shuffle around the texture units as needed and spit out the correct GLSL code? Do I have to write this from scratch? Am I going about this the wrong way? Does this scenario make sense?


In GLSL you tell the shader where to find each texture (in which texture unit to sample)
You would need a structure with your shader code to know what you're adding, a kind of descriptor to know how many tex units and other resources this shader (as in OpenGL meaning, not a whole program, but a code snippet) requires, and the name of the samplers to later bind the sampler to the correct texture unit.

One problem you'll face is the risk of uniform names colliding if you gather a lot of code snippets, so you'll have to be careful about that.

-* So many things to do, so little time to spend. *-

This topic is closed to new replies.

Advertisement