[help]Multi-Input Dynamic Shaders - how?

Started by
8 comments, last by Bombshell93 12 years, 6 months ago
Many modelling / sculpting / rendering software has this though I assume its because it goes through several stages.
In terms of real-time rendering how would I get a material system allowing for multiple inputs / choices of input (E.G. Replacing the diffuse map with a colour or changing its visibility based on the Fresnel, etc.)
I want it to work in a way a material can be made using a slider / palette interface so real-time editing of how the game looks is possible.
I'm assuming I should create the GBuffer in a typical manor and then have the material effect how the surface is treated while filling the light map, casting shadows, calculating radiosity, etc.

Any and all help is appreciated,
Thanks in advanced,
Bombshell
Advertisement
The common approach is to author a shader that supports every feature you need, and compile variants of that shader that only use the features required by a particular material. It's usually called an ubershader. You can enable and disable features either by using preprocessor macros, or by compiling the shader with hardcoded values like 1 or 0 that will cause the compiler to optimize out that portion of the code. You can also experiment with enabling or disabling features by branching on a shader constant, or by replacing textures with 1x1 textures with a single color. These result in worse runtime shader performance, but can cut down on the number of shader permutations that you need to compile.You can also use them to aid in real-time editing, and then once the material is done being edited you can generate a more optimized version.
would it create too much overhead to have a series of small shaders and a number of techniques each technique taking in its own type of input.
It would result in a large amount of small shaders being used as opposed to small amount of large shaders, but I'm not sure how this would hit performance.
Well the problem is combining two different techniques. Some things can combined by rendering a mesh multiple times and blending the results. For instance you can handle multiple lights this way, or you could handle rim lighting this way by performing the rim lighting in a second pass that that sums the results with the previous pass. But most things are not that simple. For instance, say you want normal mapping and rim lighting. For them to work together the rim lighting needs the perturbed normal produced by normal mapping, and so you need to have both techniques working in the same shader program. Doing everything in one shader is also much more efficient than blending results.
just a thought on the subject of materials, I'm constantly looking at deferred rendering techniques as if its essential I use all deferred techniques.
Would it be better if I pass arrays of lights to the shader as constants. (obviously not passing lights that do not touch the mesh)
Then pass a material containing information on how the model reacts to lights, whether it uses certain effects, if so how it uses them, etc. etc.
run through a basic Vertex shader passing through tangents, UV's, Normals, etc.
Then have a pixel shader that does everything in one pass, a loop though lights
Then a normal and depth GBuffer for the sake of post processing effects.

With a good object culling system it could be pretty fast while holding up diversity with materials right?
Would it also make transparency simpler?

EDIT: by the way thanks for helping so much :)
researching Graphics programming is my obsession, anything I can learn about it I'm chuffed to the moon and back XD
I've decided to use a variation of the method last mentioned.
And I have an Idea for the culling. I need a second opinion on its performance hit / gain.
each object having its own ID is rendered in low poly as the colour of its ID.
Once done a simple foreach loop is run for each pixel, noting ID's and skipping pixels with already listed ID's. then when rendering normally if an objects ID is not int he low poly image the object is not drawn. Simple enough though I assume there are some optimization to be run, such as low poly render being lower resolution than final render, or something.
In the time of this occlusion culling I can do particle calculation / collision detection not relating to movement (bullets, etc.) in another thread, meaning the time would not be wasted only doing the occlusion culling.

any criticisms or anything for me to consider?

Thanks in advance,
Bombshell
You can certainly use a forward rendering approach like the one you described and get very far with it, many many games have shipped with it. But you will run into all of the usual issues, mainly that it becomes more difficult to batch, having lots of lights becomes inefficient, your granularity for rejecting lights is limited to the granularity of your draw calls, etc.

As for your visibility approach, similar things have been done in Splinter Cell Conviction, Killzone 3, and Battlefield 3. However all of those used a depth buffer rather for visibility determination than an ID buffer. The latter 2 also rasterized on the CPU rather than the GPU, which helps avoid latency and synchronization issues. You can find presentations for all 3 if you search for them.
MASSIVE EDIT: sorry I ranted about non-sense and kept second guessing myself.<div>So the way I think I'll be doing it is, using low poly models (within the bounds of the higher poly / smaller volume wise than the higher poly) I will draw the large static objects, like the map or a large vehicle that's not moving, etc. onto a depth map using a software rasterizer at a lower resolution. At the same time items deemed big enough to go on said map will be rendered by the UberShader.</div><div>while doing so I will do very basic culling of objects obviously not in the view, objects well out of the view frustrum, etc.</div><div>I will then multi-threading (why the heck not) test possible objects / particle vertices, eliminating triangles / particle areas that don't have depth values higher than the &amp;nbsp;cullling depth map while doing this each objects highest depth is noted for later sorting. Triangles deemed possible at this point will be rendered onto the culling depth map and in the final image if a piece of an object can be seen it will be rendered.</div><div>NOW for why the sorting, the objects are sorted from front to back based on their highest visible depth value (from when culling vertices) and likewise are drawn, from front to back.</div><div>This allows most hidden pixels to be culled during rasterization (assuming it avoids rasterizing areas that are obviously hidden, if not I could have a simple check in the pixel buffer) so areas not in need of rendering, are not rendered.</div><div><br></div><div>To me this is sounding fairly good but I'd need to know if my assumption about how the hardware rasterizer handles hidden pixels. if it skips them in rasterization its a massive performance saver, if not I've got to waste time with a check before the pixel shader runs.</div>
I'm writting the software rasterizer and at the moment I'm setting up the loop for drawing the triangle,
Let me know if you know any better ways, but I'm using XNA and this is what I have so far:

Vector2 VertexAB = Vertex A - Vertex B;
float LengthAB = VertexAB.X + VertexAB.Y;
Vector2 IncrimentAB = VertexAB * LengthAB;

The Idea is I use the incriment of each line to find the Xstart and Xend for each horrizontal line and then fill everything in between.
I'm sure there must be a better way but the only thing I've come across is a method called "Half-space" where it takes the minX, minY, maxX, maxY and a checks chunk of the "space" if their occupied by the triangle, then fills in the triangle.
I figured it would be faster to find the boundaries of the triangle and fill it in.

EDIT: I found my method is a basic version of Horrizontal spans
still writing the rasterizer and I've got a problem with identifying which edges are start spans and which edges are end spans.



int[] triX = new float[3] { (int)a.X, (int)b.X, (int)c.X }; //Putting the vector positions in an array for the min max functions
int[] triY = new float[3] { (int)a.Y, (int)b.Y, (int)c.Y }; //Also changing the Vector3's into int's because int is faster processing

int minY = (int)MathEx.min(triY); //min and max functions to give top and bottom of spanning
int maxY = (int)MathEx.max(triY);

Point A = new Point((int)a.X, (int)a.Y); //Putting the variables into point structures.
Point B = new Point((int)b.X, (int)b.Y);
Point C = new Point((int)c.X, (int)c.Y);

Point AB = new Point(A.X - B.X, A.Y - B.Y); //Creating points containing the distance between each point.
Point BC = new Point(B.X - C.X, B.Y - C.Y);
Point CA = new Point(C.X - A.X, C.Y - A.Y);

float SAB = (B.X - A.X) / (B.Y - A.Y); //calculate per Y, X incriments
float SBC = (C.X - B.X) / (C.Y - B.Y);
float SCA = (A.X - C.X) / (A.Y - C.Y);

int Switch; //The point where an edge changes (and where the per Y, X increment needs to be switched)
bool RoL; //whether the longest Y edge is on the left or right (the beginning or end of the span)


if (AB.Y > BC.Y && AB.Y > CA.Y) //finding the longest Y edge to identify the switching point
{
Switch = C.Y;
}
else if (BC.Y > AB.Y && BC.Y > CA.Y)
{
Switch = A.Y;
}
else
{
Switch = B.Y;
}


I'm not sure how to get RoL likewise what to do if a line is flat (all X and no Y or visa versa)

EDIT: Started a new topic about this, its gotten a bit off of the original subject

Any and all help appreciated,
Thanks in advanced,
Bombshell

This topic is closed to new replies.

Advertisement