[5.6]How to use Material.SetShaderPassEnabled?

Hello, I am writing a custom material editor,

currently I can
-toggle shader keywords on off
-only show properties(float/texture/color)when a shader keyword is on

but when I try to toggle a shader PASS on/off, I failed
the material always render all passes in the shader.

here is the code, wonder if I am doing something wrong.(see line 15,16 & 35)

using UnityEngine;
using UnityEditor;
using System;
public class MyUberShader_CustomInspector : ShaderGUI
{
    Material targetMat;
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
    {
        // render the default gui
        base.OnGUI(materialEditor, properties);

        targetMat = materialEditor.target as Material;

        //toggle PASS ON/OFF not working :(
        ShowToggle_ShaderPass("Colin/AllSharedPass/Edge/EDGE");
        //ShowToggle_ShaderPass("EDGE"); //also not working

        //toggle keyword ON/OFF works :)
        if (ShowToggle_ShaderKeyword("forceRED_ON"))
        {
            //show properties used by this keyword
            //.......
        }
    }
    bool ShowToggle_ShaderPass(string passName)
    {
        // see if pass is ON, and show a checkbox
        bool isPassAlreadyToggleOn = targetMat.GetShaderPassEnabled(passName);
        EditorGUI.BeginChangeCheck();
        bool shouldPassToggleOnNow = EditorGUILayout.Toggle("toggle pass - " + passName + " ON?)", isPassAlreadyToggleOn);
        if (EditorGUI.EndChangeCheck())
        {
            // enable or disable the shader pass based on new checkbox value
            Debug.Log("Set pass " + passName + ": " + shouldPassToggleOnNow);
            targetMat.SetShaderPassEnabled(passName, shouldPassToggleOnNow);  //toggle PASS ON/OFF not working :(
        }
        return shouldPassToggleOnNow;
    }
    bool ShowToggle_ShaderKeyword(string keyword)
    {
        // see if redify is set, and show a checkbox
        bool redify = Array.IndexOf(targetMat.shaderKeywords, keyword) != -1;
        EditorGUI.BeginChangeCheck();
        redify = EditorGUILayout.Toggle("toggle keyword - " + keyword + " ON?", redify);
        if (EditorGUI.EndChangeCheck())
        {
            // enable or disable the keyword based on checkbox
            if (redify)
                targetMat.EnableKeyword(keyword);
            else
                targetMat.DisableKeyword(keyword);
        }
        return redify;
    }
}

and here is the shader code

Shader "Colin/MyUberShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque"}

        UsePass "Colin/AllSharedPass/SurfaceColor/SURFACECOLOR"
        UsePass "Colin/AllSharedPass/Edge/EDGE"   
    }
    CustomEditor "MyUberShader_CustomInspector"
}

I made a fix for this but I think it’s not due until 5.6.0p1. (I can’t check to be sure right now)

I recommend trying it in that version when it comes out, which is very soon, and post back here if it still doesn’t work then.

Regards,

1 Like

I still have this problem in 2017.1.0f3. Shader passes disabled with SetShaderPassEnabled still render.

Now I see “Note that the Shader pass enable/disable flag only affects custom rendering pipelines that use the RenderLoop class” in the docs (this is missing in the class comments, which simply say “Enables or disables a Shader pass on a per-Material level”), but that doesn’t make sense. Why can’t we simply disable passes?

It should work in all rendering pipelines. I’m using it for the new particle standard shaders, which is why it got added to the non-custom rendering pipelines. You could submit a bug report if you have a case where it doesn’t work.

I will update the docs too.

I can do that, but it doesn’t work in any case for me, so maybe I’m doing something wrong. Just as a quick sanity check:

In my shader I have

Pass
{
Name “Main”
}

and I’ve disabled the shader with mat.SetShaderPassEnabled(“Main”, false) from a MaterialEditor. I see the pass name in the “Disabled Shader Passes” array in the debug view for the material. I also tried the full path, eg. “MyShader/Main” (which would make much more sense when using UsePass), but no luck there either.

Ah ok. For me, I use it like this:

Shader:

GrabPass
{
    Tags { "LightMode" = "Always" }
    "_GrabTexture"
}

Script:

material.SetShaderPassEnabled("Always", useDistortion);

So I suspect it works on the “LightMode” tag rather than the actual name of the pass.

EDIT: Yep. From the docs:

Hmm, so it’s just a poorly-named method. I could repurpose that tag (since this isn’t a lit scene), but that seems like a bad idea. Hopefully we’ll get a real way to control which passes are used, since that seems like a basic control.

I know this is a bit of an old thread, but I thought I’d chime in – that’s a very confusingly named function, and the docs don’t really do a lot to help. Even the argument name for the function is “passName” rather than “lightMode”. This is further confused by the function “findPass()” also taking a “passName” (except it’s actually the name of the pass).

I’d been trying to use this for awhile and while I’d really prefer a function that allows for disabling / enabling by true pass name, this will work. Having two slightly more generic functions on a material like “SetPassTagEnabled(tagName, tagValue, enabled)” and “SetPassNameEnabled(passName, enabled)” would really be useful, though.

Thanks!

Edit: Turns out I actually can’t do what I want with this function because the rendering engine looks for specific keywords in the LightMode.

Would love to get a definitive answer on this as I haven’t gotten it to work.

I’m trying to make the ShadowCaster pass optional and toggle-able via a ShaderGUI checkbox. I can use:

material.SetShaderPassEnabled("ShadowCaster", useShadows);

And then query it via:

material.GetShaderPassEnabled("ShadowCaster");

And I get the expected result (true or false, depending on the checkbox) but in game the mesh is still always casting shadows regardless of the pass’s enabled state.

Please share your shader with us too (or at least the relevant bits)

Thanks,

This feature seems broken, yes, it works for turning on and off LightMode “Always”.
However, the docs say:

“For example, if the Shader has a “refraction” pass but you only want to enable it on Materials that have a refraction Texture assigned, pass “refraction” as passName and false for enabled for Materials without a refraction Texture assigned. Note that the Shader pass enable/disable flag only affects custom rendering pipelines that use the RenderLoop class (via RenderLoop.DrawRenderers or RenderLoop.DrawShadows).”

I tried using the string “refraction”, as “LightMode”, “Name”, “PassName”. No success…

I could be wrong as I’ve not experimented with scripted render loops (I used the “Always” pass for my outline pass), but I think the problem is in the documentation rather than the feature.

“passName Shader pass name (case insensitive).” would be easier to understand as something like:
“passName Render loop pass name (case insensitive).”

So if you are using a scripted render loop with a “refraction” pass, you could use that name. If you are using the default render loop then you are limited to those passes. I’d guess it doesn’t work on ShadowCaster because that pass is enabled or disabled per light depending on the light settings, so the manual setting gets ignored.

Is this per material or per shader? Seems to apply to all my material even if I clone them…

Any updates on this? I am trying to do what derrickMT is trying to do - enable / disable the shadow casting pass using material.SetShaderPassEnabled but it does not work, the shadow casting pass always runs. I tried both the pass name and the lightmode. Neither one works to shut off the pass for shadow casting. I think the shadow casting renderer does not care about whether the shadow casting pass is enabled or not.

The documentation could really do with clarifying for this.

It looks at first glance like SetShaderPassEnabled() was a method to enable/disable named passes programatically - for example, to enable/disable one or more overlaid VFX passes on a character shader*

But nope, it doesn’t seem to work like that at all… and there doesn’t seem to be another method of programatically disabling/enabling a pass.

(*perhaps in cases where you’ve already got rather too many multi_compiles and shader_features and don’t want to add yet another…)

1 Like

+1 for this. Misleading documentation. And it would be far more useful in many cases for it it actually allow toggling named passes, as the method argument implies.

3 Likes

So let me get this straight, in unity, you cannot disable shader passes. You can only change the Tags { "LightMode" = "ForwardBase" } to Tags { "LightMode" = "ForwardAdd" } etc. but not disable the pass or do anything based on the `Name “MYPASSNAME”.

So the only way to do this is to make 2 or 10 copies of your shader? If you want to save unnecessary passes? Goddamn

3 Likes

Currently that is the workaround I’m using, which is why I have around 20 different shader files. It would be quite useful to be able to reduce this number by swapping passes.

@richardkettlewell crazy question for an old thread:

could you explain to me why this doesn’t work?

public class StandardShaderUI : ShaderGUI
{
    override public void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
    {
        base.OnGUI(materialEditor, properties);

        var target = materialEditor.target as Material;

        if (GUI.changed)
        {
            Debug.Log("Changed");
            Debug.Log(target.GetFloat("_DebugDepthPass"));
            target.SetShaderPassEnabled("ShadowCaster", target.GetFloat("_DebugDepthPass") < 0.5f);
        }
    }
}

I can detect the changes and I can see the float is properly set, and yet, ShadowCaster continue to be called when I look at Frame Debugger.

What’s wrong with my approach? Do I HAVE TO draw the toggle myself or something?

(just to clarify I want the depth pass to be skipped, when debug flag is 1)

(and again I made sure my ShadowCaster pass is actually using “LightMode” = “ShadowCaster”)

I desperately need an example somewhere that show me SetShaderPassEnabled actually works, and I know they work, so there must be some simple oversight on my part.

The script looks ok to me.

We use this function in the standard particle shader, and it looks equivalent to your usage.
If you have exhausted all options, you could report it as a bug… even if it’s your mistake, it should motivate someone to improve the docs page, as it’s not really your fault if the docs are rubbish for this :frowning: