Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Manually running shadow pass

Discussion in 'General Graphics' started by Cross22, Jul 21, 2017.

  1. Cross22

    Cross22

    Joined:
    Sep 26, 2014
    Posts:
    22
    The documentation states that Shader Replacement is being used to run the shadow / depth pre-pass on the scene. For some special effects work I would like to do that manually, i.e. have a camera render the scene with the simplistic shadowcaster replacement. How would I do that?

    I am assuming Camera.RenderWithShader(Shader shader, string replacementTag) should do that, but what is the replacementTag? Is it "RenderType" ? And what is the name of the default shader that Unity uses for shadow casters?
     
  2. andrewgotow

    andrewgotow

    Joined:
    Dec 28, 2013
    Posts:
    18
    The documentation for Shader Replacement is a little confusing. Here's how it works.

    Shaders contain "Tag" blocks, which can contain a set of key-value pairs. `"RenderType"="Transparent"` for example, defines a key of "RenderType", and a value of "Transparent". All the built-in Unity shaders have a series of useful tags defined, which outline how they're used. (You can see an example list of RenderType values here)

    When you specify a replacement tag in `Camera.RenderWithShader(...)`, the camera will isolate only the objects who's shaders have that key defined in their "Tags" block. From there, your replacement shader will be evaluated, and the pass with the matching tag value will be used. Essentially, objects in the scene will be rendered with the replacement pass tagged with an exact match of key and value.

    For example, if I had a replacement shader that drew opaque objects as red, and transparent as green, it would look like this.

    Code (CSharp):
    1. SubShader {
    2.     Tags {"RenderType"="Opaque"}
    3.     UsePass "Red"
    4. }
    5. SubShader {
    6.     Tags {"RenderType"="Transparent"}
    7.     UsePass "Green"
    8. }
    9.  
    10. ...
    11.  
    12. myCamera.RenderWithShader(colorShader, "RenderType");
    The scene would filter out all shaders that don't have a "RenderType" tag, and then would render the first subshader on all objects with `"RenderType"="Opaque"`, and the second subshader on all objects with `"RenderType"="Transparent"`. If the replacement shader does not feature a subshader matching an object's tag value, that object will not be rendered.

    Armed with this, you could render a simple shadow pass using `Camera.RenderWithShader(myShader, "RenderType")`, and then define a subshader that outputs depth, tagged with `"RenderType"="Opaque"`, effectively rendering all the depth of all opaque objects to your target.

    ------------------------------------------------------

    A quick note - Unity uses a special pass to render values to the shadow-buffer in "normal" shaders, you might've seen `"LightMode"="ShadowCaster"` buried in some standard shaders. The correct way to render shadows would be to render every object into the shadow buffer using this pass, as defined by the shader currently assigned to the renderer. That way, if someone were to write a shader with a fancy custom shadow pass (for volumetric particles or something), it would still work with your replacement effect.

    Unfortunately, this is impossible using the existing API as far as I can tell, and most certainly is not possible using the "shader replacement" system. For your purposes this is fine and the above solution will work, but for anyone writing a package for the Asset Store or distribution who is reading this, you will receive bug reports from your uses who wish to implement custom shadow passes if you use standard shader replacement.
     
    Last edited: Jul 25, 2017
  3. Cross22

    Cross22

    Joined:
    Sep 26, 2014
    Posts:
    22
    Thank you Andrew! I did go with the first approach, writing a shader that only writes to the (attached) depth buffer and then invoking it using RenderWithShader(myShader, "RenderType"). Works as expected.
    Still seems like this ought be possible by using Unity's built-in replacement shaders, rather than adding my own though.