Reputation: 1
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.
Upvotes: 0
Views: 1560
Reputation: 21
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 ;)
Upvotes: 2
Reputation: 58
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.
Upvotes: 0