1

I have a shader that displays a mesh's vertex colors and has the ability to clip vertices that are blue. I'd like the user to be able to turn this setting on/off while using the application.

I've tried the suggestions offered here Find and change property name in a shader and here How do I discard pixels based on vertex color and turn that on or off in a MonoBehaviour? but so far none of those have worked for me.

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

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

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

private void Update()
{
    SaveScale();
    TurnOffContext();
}
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);
    Debug.Log("Toggle ");
}

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

I expect this script to cause my mesh's blue parts to be discarded, because in my shader's fragment I have

clip(_Toggle * (0.5f - IN.vertColor.b)); 

Specifically the part that doesn't work is the line

rend.material.Setfloat("_Toggle", 1); 

The function is being called, but it's not setting the _Toggle value to 1 as expected.

If I manually change the _Toggle setting in the inspector panel from 0 to 1, the blue parts of the mesh are discarded, which is the expected result. The script I pasted above should have the same result as manually changing the setting in the inspector panel.

espeon
  • 57
  • 1
  • 8

1 Answers1

1

I don't know why your current solution doesn't work (it looks fine to me), but i know that you can work around this whole thing using shader variants.

Inside your shader, add this line to the pragmas:

#pragma multi_compile __ DISCARD_BLUE

And then change your clip statement to this:

#ifdef DISCARD_BLUE
clip(0.5f - IN.vertColor.b);
#endif

To enable and disable this feature, just change the C# script to use rend.material.EnableKeyword("DISCARD_BLUE") and rend.material.DisableKeyword("DISCARD_BLUE") respectively. This has the added benefit that it saves a few instructions when the keyword is disabled. You can also enable/disable the keyword globally like this:

Shader.Find("Custom/VertexColor").EnableKeyword("DISCARD_BLUE");

Other than that, have you made absolutely sure that your script references the correct renderer?

Kalle Halvarsson
  • 1,240
  • 1
  • 7
  • 15
  • Thanks for your response, I'll try that out shortly. To address your part about the script referencing the correct renderer: in my Start function, you can see my code `rend = gameObject.GetComponent();` This makes references the renderer of the gameObject that the script is attached to, correct? And since I want to change the shader setting on the gameObject that the script is attached to, that would be the correct renderer to reference? I'm a complete beginner when it comes to shaders and pretty new to Unity too, so I'm not sure – espeon Jun 20 '19 at 14:05
  • I've tried your suggestion -- still upon entering playmode, the blue pixels are not discarded. I have the `TurnOffContext()` function in the Start and Update functions of the script, so it's definitely getting called, but it doesn't have any effect. I tried to enable the keyword globally using `Shader.Find("Custom/VertexColor").EnableKeyword("DISCARD_BLUE");` as you suggested, but I get an error "Member 'Shader.EnableKeyword(string)' cannot be accessed with an instance reference; qualify it with a type name instead" and I'm not sure how to fix this, event after some Googling – espeon Jun 20 '19 at 15:29
  • 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
  • Good to hear! Note though that changes made to sharedMaterial will persist after exiting play mode. For the global shader keyword - my bad, turns out it is a static call. Use Shader.EnableKeyword instead, without the "Find". – Kalle Halvarsson Jun 22 '19 at 11:11