Search code examples
c#unity-game-engineshader

Change camera shader property


I need to set a variable of a shader without a material that wraps around it.

I'll explain the problem and why it's not like the question "How can I access shader variables from script?".

Problem

My shader is similar to this (99% of irrelevant boilerplate code removed):

Shader "Hidden/XShader"
{
    Properties
    {
        _x ("", float) = 0
    }

    SubShader
    {
        Pass
        {
            float _x;

            half4 frag(v2f i) : SV_Target
            {
                // "col" and "wpos" have been correctly defined

                if (wpos.x >= _x)
                {
                    col.r = 1;
                } else {
                    col.r = 0;
                }

                return col;
            }
        }
    }
}

This shader is set through the Edit->Project Settings->Graphics->Deferred option. It is the default shader that the main camera uses.

Now I need to set the _x value from code attached to the camera:

public class XCameraController : MonoBehaviour
{
    public float x;

    void Update()
    {
        <something>.SetFloat("_x", x);
    }
}

The <something> placeholder would normally be a material, as SetFloat() is defined there. But the camera and shader do not have a material. The concept of material doesn't even apply to the default shader.

I've searched online and in the documentation for hours. I admit I failed and am at a loss here. I guess it must be simple but I can't find any documentation.

I don't expect an implemented solution, a pointer where I can find help will suffice!


Solution

  • But the camera and shader do not have a material. The concept of material doesn't even apply to the default shader.

    True but materials simply exposes all properties from a shader so it is relevant here since you want to change the shader properties.

    You have a custom shader but that's not used to render a GameObject but the camera. A material is still needed to change the shader. If you don't want to use a material then you can use the Shader.SetGlobalXXX functions such as Shader.SetGlobalFloat("_x", 3) but it will change all the shader properties. This is unrealistic.


    The proper way to do this is to create a temporary material you will use to modify the shader, change the shader properties, then update the shader the camera is using. To do this, you have to:

    Find the shader or get a reference of the shader with a public variable:

    Shader camShader = Shader.Find("Hidden/XShader");
    

    Create material from the shader

    Material camMat = new Material(camShader);
    

    Modify the property as you wish

    camMat.SetFloat("_x", 3);
    

    Apply to the modified shader property to the camera

    Camera.main.SetReplacementShader(camShader, "RenderType");
    

    If you're manually rendering the camera then use Camera.main.RenderWithShader(camShader, "RenderType") instead of Camera.main.SetReplacementShader(camShader, "RenderType").