- Home /
Replacing and stacking shaders for some objects
Hi,
I'm mostly new to shaders, and this will likely show in this question.
I've got several objects in my scene whose rendering I wish to alter depending on how far they are from a character. I'd like to highlight reachable objects, darken unreachable ones, and highlight those the player can attack in a specific overlay.
These can be combined - for instance, we may have an object that's just too far away for "walking" distance, but that can be attacked with a ranged attack.
So far I've read Blurst's article on Replacement shaders and sat through and experimented with Unite '08's Shader programming course. I've also reviewed the shader's getting started and cg docs, which seem to cover the same topic as the Unite talk.
The latter gave me a good idea as to how to go about writing a specific shader, but I'm still at a loss as to how to stack the various operations. I can think of several options:
- Creating various materials and swapping them.
- Having a shader written in Cg that behaves differently based on material properties.
- Using RenderWithShader to overlay a new shader on top of the existing one based on distance.
What would be the best approach here? Any pointers to basic tutorials as to how to perform this sort of operation? I have the distinct feeling that I'm missing a piece of the puzzle.
Answer by robert · Nov 12, 2009 at 01:21 PM
If an object is supposed to have the same basic shader (e.g. diffuse, toon, bumped specular) in all cases (short/long distance, ability to be attacked in a specific way) and you would only want it to be highlighted/darkened, the easiest way to go is just to alter the 'Main Color' of the material from the script:
if(withinWalkingDistance && canBeShot)
renderer.material.color = withinWalkingDistanceColor * canBeShotColor;
Alternatively you can write your own shader with some custom properties, that would control the look of the material and update those properties from your scripts depending on the state of the object every time it changes.
If you'd like to completely change the looks of an object (no shading at all when immune to the attacks, toon shading otherwise), you can swap their materials just as easy:
if(canBeDamaged)
renderer.material = toonShadingMaterial;
(Or, again, write a custom shader that would be altering the shading style depending on the value of some property you set from your scripts, but you should go with the simple material swapping first.)
Unless you're looking for some very fancy effects, there's primarily no need to use replacement shaders in the scenario you described.
Thanks for the pointer Robert. True, changing the material color will probably suffice, but the issue got me thinking about the other interesting possibilities that are available with shaders - for instance, replacing it for one that still displays the tile as is, but with a particular outline.
Swapping the material has the disadvantage that duplicated but slightly different materials need to be kept around for all objects that might be displayed in more than one manner, so it is a lot more inconvenient and can be a management issue.
Good thinking with the outline. You could use the standard toon shader with outline and modify the color of outline directly from a script. Or, as a more advanced approach, you could use the Edge Detection example from Replacement Shaders example project, but extend it to write a color (constant per object) to a separate buffer. Then in the post-pro shader you would detect the edge as usual, but tint the resulting outline with the color you have previously stored in the separate buffer.
Right. Now here's the part of the workflow that escapes me from the replacement example: it appears from the Replacement Shaders example that I get no access to object details, only to the materials. Is this accurate? If so, if I have 10 objects with the same material, how can I selectively tint only a few differently in the post-pro? Are we back to swapping materials? (Like I said, shader noob)
Replacement shaders just look at objects' shaders (precisely: on their tags) and swap them with whatever replacement was defined for this tag. So you can't "actively" query object details. But!.. The replacement shader will get all the properties from the original material. So if you define _OutlineColor property in object's original shader (but just not use it there) and set that color value to whatever you need for each object, in the replacement shader you can just spit out that color and then in the image effect tint the outline with the color. This way you can have single material and...
single replacement shader for all the objects, yet different outline color per object - easily modifiable from a script. BTW: if you modify object's material color (or any other property) at runtime by setting renderer.material.color, only that object's color will be modified, even though many objects might have shared the same material. This is because objects create their own material instances when the game starts. If you'd like to propagate the new value to all instances of the material, use renderer.shared$$anonymous$$aterial ins$$anonymous$$d.