Reputation: 1
I am trying to create a track in unity by using nodes, this works however to show the track i am trying to use a shader that changes the textures at the position of the node. This how ever only chnages the texture for the first node and then stops. is this a shader limitation or am i missing something?
Shader code:
Properties
{
_NodePos("Node position", vector) = (0.0, 0.0, 0.0, 0.0)
_Dist("Distance", float) = 5.0
_MainTex("Texture", 2D) = "white" {}
_SecondayTex("Secondary texture", 2D) = "white"{}
_NumOfPeices("Pieces",float) = 5.0
_Color("colour", Color) = (1,1,1,0.5)
_isdone("isdone",float) = 0.0
}
SubShader
{
Tags { "RenderType" = "Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 4.0
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 worldPos : TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
// We compute the world position to use it in the fragment function
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
uniform float4 _Color;
float4 _NodePos;
sampler2D _MainTex;
sampler2D _SecondayTex;
float _Dist;
float _NumOfPeices;
float _isdone;
fixed4 frag(v2f i) : SV_Target
{
// Depending on the distance from the player, we use a different texture
if (distance(_NodePos.xyz, i.worldPos.xyz) < _Dist)
{
return tex2D(_SecondayTex, i.uv);
}
else
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
Script code to give the position:
public void createDirtTrack()
{
foreach (NodeScript n in GD.path)
{
if (GD.path.Contains(n))
{
Mat.SetVector("_NodePos", n.worldPos);
Mat.SetFloat("_Dist", Radius);
}
}}
Final result in the unity scene: enter image description here
Any help would be greatly appreciated
Upvotes: 0
Views: 790
Reputation: 6266
Before rewriting the structure of your code to instance materials, I would try adding PerRendererData above the vector and float.
Properties
{
[PerRendererData]_NodePos("Node position", vector) = (0.0, 0.0, 0.0, 0.0)
[PerRendererData]_Dist("Distance", float) = 5.0
...
I do not have a ton of experience writing shaders, but the docs specify that this value can only be changed by code, is read-only in the editor, and will replace the material data values with whatever values are assigned at runtime.
Edit: Further reading, to use the new values you must access the MaterialPropertyBlock. Your code would need to change a bit.
public void createDirtTrack()
{
foreach (NodeScript n in GD.path)
{
if (GD.path.Contains(n))
{
// grab the renderer of the current node
Renderer tmpRend = n.gameObject.GetComponent<Renderer>();
// create a new material property block
MaterialPropertyBlock tmpBlock = new MaterialPropertyBlock();
// get our properly block of the current nodes renderer
tmpRend.GetPropertyBlock(tmpBlock);
// apply our changes
tmpBlock.SetVector("_NodePos", n.worldPos);
tmpBlock.SetFloat("_Dist", Radius);
// now apply our changes
tmpRend.SetPropertyBlock(tmpBlock);
}
}
}
I am assuming the NodeScript
is on each Node
, which has the Renderer
with the Material
you are changing. If you are calling this code often, I would add a function to NodeScript
, where you cache the renderer and apply the changes locally. As the method has the word create
I assume this is called once to instantiate your track so the use of GetComponent
in the method is not terrible. Let me know if this works for you, I have never used property blocks.
Upvotes: 0