Cooljest4123
Cooljest4123

Reputation: 1

passing a position mutiple times into a shader in unity

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

Answers (1)

TEEBQNE
TEEBQNE

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

Related Questions