0

I've written a shader that uses a mesh's vertex colors and also has a function that clips all vertices that have an interpolated vertex color in the blue channel greater than 0.5 (discards all blue vertices).

I'm trying to create a voice command that allows the user to call the function when they are ready. However, in Microsoft's Mixed Reality Toolkit Speech Input Handler, it only allows me to call functions from components (Mesh Renderer, Mesh Filter, Mesh Collider, etc.) of the GameObject I'm referencing, not the shader or material of the object.

How can I make it so that a voice command can call my 'hide' function that I've shared below?

I've tried to code the 'hide' functionality outside of the shader but that didn't seem to work.

Shader "Custom/VertexColor" { // Where it will appear inside of the Shader Dropdown Menu of the Material / Name of the shader
Properties{
    _Toggle ("Toggle context", int) = 0
}
SubShader{
    Tags { "RenderType" = "Opaque" }
    LOD 200


    CGPROGRAM
    #pragma surface surf Lambert vertex:vert
    #pragma target 3.0
    #include "UnityCG.cginc"



    struct Input {
        float4 vertColor;
    };

    void vert(inout appdata_full v, out Input o) {
        UNITY_INITIALIZE_OUTPUT(Input, o);
        o.vertColor = v.color;
    }

    void surf(Input IN, inout SurfaceOutput o) {

        #include "UnityCG.cginc"
        int _Toggle = 0;
        o.Albedo = IN.vertColor.rgb;
        clip(_Toggle * (0.5f - IN.vertColor.b));

    }



    ENDCG
}
    FallBack "Diffuse"
}

I expected the Speech Input Handler to allow me to call functions from a shader but it does not.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HoloToolkit.Unity;

public class AnchorScript : MonoBehaviour
{
public Renderer rend;
Vector3 scale;

public WorldAnchorManager worldAnchorManager;
// Start is called before the first frame update
void Start()
{
    transform.localScale = scale;
    TurnOffContext();
}

private void Update()
{
    SaveScale();
}
public void AnchorIt()
{
    worldAnchorManager.AttachAnchor(this.gameObject);
    this.gameObject.GetComponent<Renderer>().material.color = Color.red;
}

public void ReleaseAnchor()
{
    worldAnchorManager.RemoveAnchor(this.gameObject);
    this.gameObject.GetComponent<Renderer>().material.color = Color.green;
}

public void ShowDifferences()
{
    gameObject.GetComponent<Renderer>().enabled = true; 
}

public void HideAugmentations()
{
    gameObject.GetComponent<Renderer>().enabled = false;
}
public void TurnOffContext()
{
    rend.material.SetFloat("_Toggle", 1);
}

public void SaveScale()
{
    scale = gameObject.GetComponent<Transform>().localScale;
}
}

I put the TurnOffContext() function in the Start() function just to test to see if it works. In theory it should immediately set the _Toggle property to 1 which should discard all the blue pixels, but I still see the full original mesh upon starting my app. I also tried not putting it in the Start() function and just using my voice command which runs the function TurnOffContext(), but that also didn't work.

Here is a screenshot of my GameObject's material's inspector panel. https://i.stack.imgur.com/PlXqi.jpg

Ruzihm
  • 19,749
  • 5
  • 36
  • 48
espeon
  • 57
  • 1
  • 8
  • There's a lot going on here. You need to apply the shader to the objects whose surfaces you want to process this way. You also should add an int parameter to the shader and call [`Material.SetInt`](https://docs.unity3d.com/ScriptReference/Material.SetInt.html) in a C# component to set it to 1 when you want to hide, and 0 if not. then in the shader's `surf` function, if the hide int is `1` you call `clip(0.5f - IN.vertColor.b);` – Ruzihm Jun 18 '19 at 18:57
  • Hi Ruzihm, I appreciate your help. I've applied the shader to a material which I've applied to my GameObject. How do I add an int parameter to the shader? I have a separate script attached my GameObject as a component that is solely for voice commands. How exactly would I call Material.SetInt in this script? In my shader, I've declared an int called toggle and initialized it to 1. I also created an if statement within my fragment that calls the hide function if toggle == 1. However, the hide function is never executed and my gameObject appears the same – espeon Jun 18 '19 at 19:46
  • see [this question](https://stackoverflow.com/questions/52922082/find-and-change-property-name-in-a-shader) and [this answer](https://stackoverflow.com/a/37749687/1092820) for some help with setting shader properties. Also, get rid of the `hide` function and just put `clip(toggle * (0.5f - IN.vertColor.b) );` in the `surf` function. – Ruzihm Jun 18 '19 at 20:13
  • I set up the toggle shader property in the same way as in the answer from the second link, and I get a Shader error: undeclared identifer 'toggle'. Any idea whats wrong? Here's what my shader looks like right now. It's not the underscore in the name -- I've tried with and without the underscore. https://gist.github.com/MichaelZhou/c3056f5efed0129171fc4e84770062e7 – espeon Jun 18 '19 at 20:29
  • You need to add `int _Toggle;` in the line after `#include "UnityCG.cginc"` and then change the clip call to `clip(_Toggle * (0.5f - IN.vertColor.b) );` – Ruzihm Jun 18 '19 at 20:35
  • 1
    Thanks for your patience Ruzihm, that worked! I'm still having trouble getting the code in the GameObject component to toggle the shader setting. I wrote a function that contains rend.material.SetFloat("_Toggle", 1); in my component like how its shown in the first link you gave me. I set my Renderer reference to my GameObject. I'm calling my function in the start function of my C# component. The GameObject should now render the blue parts as transparent upon startup, but it doesn't. Any ideas? – espeon Jun 19 '19 at 18:37
  • I can't tell where the problem could be... can you edit your question to include the code you currently have and a screenshot of the material in the unity inspector? It might be configured wrong there – Ruzihm Jun 19 '19 at 18:56
  • I've edited my question to include my updated shader code as well my C# component where I'm calling a function to edit my shader properties. I also included a screenshot of my GameObject's material's inspector panel, although I'm not sure if that's exactly what you wanted – espeon Jun 19 '19 at 19:22
  • In the inspector for the GameObject, I've set the Renderer to be the GameObject https://imgur.com/a/XI2L1BI – espeon Jun 19 '19 at 20:16
  • The issue definitely lies in `rend.material.SetFloat("_Toggle", 1);` not changing the _Toggle value of the shader. Did I do something incorrectly in configuring that? – espeon Jun 19 '19 at 20:25

1 Answers1

1

You have a redundant #include in your shader code, and you're declaring the _Toggle variable in the wrong place. Also, you shouldn't initialize int _Toggle, you only want to declare it so that unity can set its value.

Get rid of the 2nd #include "UnityCG.cginc", get rid of the int _Toggle = 0; and put int _Toggle; (note: no initialization) somewhere below CGPROGRAM.

Also, change the Properties block to use capital I Int:

Shader "Custom/VertexColor" { // Where it will appear inside of the Shader Dropdown Menu of the Material / Name of the shader
Properties{
    _Toggle ("Toggle context", Int) = 0
}
SubShader{
    Tags { "RenderType" = "Opaque" }
    LOD 200


    CGPROGRAM
    #pragma surface surf Lambert vertex:vert
    #pragma target 3.0
    #include "UnityCG.cginc"

    int _Toggle;

    struct Input {
        float4 vertColor;
    };

    void vert(inout appdata_full v, out Input o) {
        UNITY_INITIALIZE_OUTPUT(Input, o);
        o.vertColor = v.color;
    }

    void surf(Input IN, inout SurfaceOutput o) {
        o.Albedo = IN.vertColor.rgb;
        clip(_Toggle * (0.5f - IN.vertColor.b));
    }

    ENDCG
}
    FallBack "Diffuse"
}

Then, in your c# code, you need to use SetInt instead of SetFloat because _Toggle is an int.

Also, use renderer.sharedMaterial instead of renderer.material!

Ruzihm
  • 19,749
  • 5
  • 36
  • 48
  • I've implemented these changes, but I can still see the blue pixels being rendered upon startup of my App in the Unity Game Window. Could this be an issue with my AnchorScript.cs? I've wrote a function that sets the _Toggle to 1 and then I'm calling that function in the Start() function of the component, but my mesh is still showing blue pixels when I start the app. – espeon Jun 19 '19 at 19:43
  • try setting `Toggle context` to `1` in the inspector and let me know if it continues to render blue pixels on the mesh. The mesh is using the `VertexColorMaterial` right? – Ruzihm Jun 19 '19 at 20:06
  • Setting Toggle context to 1 causes the blue pixels to be discarded. The mesh is indeed using the VertexColorMaterial. So the issue is that my C# component isn't setting _Toggle to 1 – espeon Jun 19 '19 at 20:09
  • @MichaelZhou Aha! You need to use `SetInt` instead of `SetFloat` because `_Toggle` is an `int`. – Ruzihm Jun 19 '19 at 20:26
  • Unfortunately, that didn't work! Also, is _Toggle an int? Because if I select the shader as described in that first link you sent me yesterday, and I examine the inspector panel, it says _Toggle is a float for some reason. https://imgur.com/a/7UhJPmZ Once again thank you so much for your help, I really appreciate it :) – espeon Jun 19 '19 at 20:34
  • @MichaelZhou hmm, that's pretty weird, since you have `_Toggle ("Toggle context", int) = 0` and `int _Toggle;` Try capitalizing the I in `Int` here `_Toggle ("Toggle context", Int) = 0` – Ruzihm Jun 19 '19 at 20:38
  • After capitalizing the I in Int, it's still listed as a Float in the shader's inspector panel, and still isn't being changed by my C# component – espeon Jun 19 '19 at 20:43
  • @MichaelZhou Hmm, that's really weird. Try changing `Int` to `Float`, `int` to `float`, and `SetInt` to `SetFloat`. Also, just to check if unity's silently having a problem compiling the shader, can you change the description from `"Toggle context"` to something a little different like `"Toggle_context"` just to see if that change updates in the inspector? – Ruzihm Jun 19 '19 at 20:52
  • I made those changes, still no difference. I changed the description to `Toggle_context` and that change updated in the inspector, so I don't think it's a compilation issue. Also, I put a `Debug.Log("Test");` line in my TurnOffContext() function, so it's definitely being called correctly, it's just not updating the shader property as expected. Could it be an issue with the way I configured my Renderer "Rend"? I don't really know how that part works, I just copied it from that link you sent me and then selected my GameObject in the inspector panel – espeon Jun 19 '19 at 21:02
  • Sure, it's possible that it's linked to a reference to the wrong renderer. Try making `rend` `private` then add `rend=gameObject.GetComponent();` at the beginning of `Start` to ensure that it's getting a reference to that gameobject's renderer. – Ruzihm Jun 19 '19 at 21:06
  • Ahhh it's still not working. This is frustrating. I'll have to do more research on setting shader properties, hopefully I can a solution. If you're out of ideas, I'm still very thankful for your help – espeon Jun 19 '19 at 21:13
  • ah yeah, good luck. It might be worth posting it as a separate question specifically about Unity refusing to set the property so more people see it. If you make a new question be sure to include a link to [this question](https://stackoverflow.com/questions/52922082/find-and-change-property-name-in-a-shader) and explain that it doesn't help you so people don't mark your question as a duplicate. – Ruzihm Jun 19 '19 at 21:15
  • 1
    I think I've solved the problem. Apparently using the `renderer.material` accessor generates a new material instance. Using `renderer.sharedMaterial` fixed the issue for me. Credit to user Glurth on Unity Answers for sharing this with me, although he himself noted that he was unsure that it should work after reading the Unity documentation. – espeon Jun 20 '19 at 21:35