0

I am trying to modify a custom shader's properties form C#. I'm able to see the properties updating in the inspector, but I don't see the changes reflected in the game unless I manually enter modify the values in the inspector.

Here is the relevant shader code:

Shader "UCLA Game Lab/Wireframe/Single-Sided"
{
    Properties
    {
        _Color("Line Color", Color) = (1,1,1,1)
        _MainTex("Main Texture", 2D) = "white" {}
        _Thickness("Thickness", Float) = 1
        _ValleyDepth("Depth", Range(0, 2)) = 0
        _HeightMap("Heightmap", 2D) = "black" {}
        _Scale("Scale", Range(0, 5)) = 1
        _Mask("Mask", 2D) = "black" {}
        _MaskWidth("Mask Width", Range(0, 120)) = 5
        _MaskHeight("Mask Height", Range(0, 120)) = 10

        [PerRendererData]_TravelX("Travel X", Float) = 0
        _TravelY("Travel Y", Float) = 0

    }

        SubShader
        {
            Pass
            {
                //Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }

                Blend SrcAlpha OneMinusSrcAlpha
                //ZWrite Off
                LOD 200

                CGPROGRAM
                

                #pragma target 5.0
                #include "UnityCG.cginc"
                #include "UCLA GameLab Wireframe Functions.cginc"
                #pragma vertex vert
                #pragma fragment frag
                #pragma geometry geom


                float _ValleyDepth;
                sampler2D _HeightMap;
                float _Scale;
                float3 _WorldPOS;
                float _TravelX;
                float _TravelY;



                float random(float2 uv)
                {
                    return (_ValleyDepth * frac(sin(dot(uv,float2(12.9898,78.233)))*43758.5453123)  );
                }

        // Vertex Shader
        UCLAGL_v2g vert(appdata_base v)
        {

            _WorldPOS = mul(unity_ObjectToWorld, v.vertex); // ben

            //v.vertex.xyz += v.normal * random(v.texcoord);
            v.texcoord *= _Scale;
            v.texcoord.x += _TravelX;
            v.texcoord.y += _TravelY;
            fixed height = tex2Dlod(_HeightMap, float4(v.texcoord.xy, 0, 0)).r;
            v.vertex.xyz += v.normal * height * _ValleyDepth;
            return UCLAGL_vert(v);
        }

        // Geometry Shader
        [maxvertexcount(3)]
        void geom(triangle UCLAGL_v2g p[3], inout TriangleStream<UCLAGL_g2f> triStream)
        {
            UCLAGL_geom(p, triStream);
        }

        // Fragment Shader
        float4 frag(UCLAGL_g2f input) : COLOR
        {
            return UCLAGL_frag(input);
        }

    ENDCG
}
        }
}


And here is the c#:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveController : MonoBehaviour
{
    public Material heightMapX;

    

    public float speedX = 0f;
    public float speedY = 0f;



    Renderer thisRend; //Renderer of our Cube


    // Start is called before the first frame update
    void Start()
    {

        thisRend = GetComponent<Renderer>(); // grab the renderer component on our cube



        //heightMapX.EnableKeyword("_travelX"); 
        heightMapX.SetFloat("_TravelX", speedX);
        heightMapX.SetFloat("_TravelY", speedY);
    }

    // Update is called once per frame
    void Update()
    {

        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            speedX += 5f;
            //heightMapX.SetFloat("_TravelX", speedX);
            thisRend.material.SetFloat("_TravelX", speedX);
        }
        if (Input.GetKeyDown(KeyCode.RightArrow))
        {
            speedY -= 5f;
            //heightMapX.SetFloat("_TravelY", speedY);
            thisRend.material.SetFloat("_TravelY", speedY);
        }

    }
}

I tried using the [PerRendererData] directive on the shader variables but it didn't help.

1
  • Please don't delete and re-post questions! Commented Feb 19, 2021 at 8:41

2 Answers 2

2

As Poultryghast mentioned, I'd recommend getting into the habit of using MaterialPropertyBlocks, as the performance gains are quite notable.

Here's another link to a more condensed and high-level view article explaining MPBs. https://thomasmountainborn.com/2016/05/25/materialpropertyblocks/

Essentially, what you're looking for is more something along the lines of :

private MaterialPropertyBlock myBlock;
private Renderer renderer;
    
public float speedX = 0f;
public float speedY = 0f;
    
void Awake()
{
    myBlock = new MaterialPropertyBlock(); // create a new PropertyBlock
    renderer = GetComponent<Renderer>(); // get your current renderer
}
    
void Start()
{
    renderer.GetPropertyBlock(myBlock); // retrieve the renderer's existing Property Block 
                                        // and store it into "myBlock"
    
    myBlock.SetFloat("_TravelX", speedX); // make your value changes in your new PB
    myBlock.SetFloat("_TravelY", speedY);
    
    renderer.SetPropertyBlock(myBlock); // apply your values onto the renderer's existing Block
}

From here, you can simply apply this same logic in your Update() method.

I'd recommend changing the values inside your if (Input.GetKeyDown(KeyCode)) statements, and calling the renderer.SetPropertyBlock(myBlock) at the end of the Update loop.

Either way, I believe this is a lot more optimized than going through the material everytime, but this has already been pointed out, and the article discusses it at greater lengths ;)

Sign up to request clarification or add additional context in comments.

1 Comment

So I switched to the material property block (thanks for the tip about performance!) and I still have the same issue: updates in inspector...but not in scene, unless I manually change values. I tried to paste the code but I went over the char limit.
0

You never actually set the material heightMapX back to the renderer. Try thisRend.material = heightMapX to actually apply the values of the material to the shader. Also, every time you do this it actually creates a new material which affects performance if you are doing this every frame. It's a little bit advanced but you can solve this problem using MaterialPropertyBlocks and GPU Instancing. This is a good tutorial if you are interested.

1 Comment

good catch. I may have been sloppily switching between methods. I tried it out with thisRend.material = heightMapX and still ran into the same issue. I thin switched to using the material property block and am still facing the same issue. Is GPU instancing required to solve this?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.