Materials Property Block not being assigned correc

Hello resident shader and coding experts,

I looked up Materials Property Blocks recently as a solution to reduce the load on my mobile device from instancing a bunch of the same shaders using the simpler GetComponent.material.SetInt() method. However, I’m having trouble getting the script to assign the Properties block correctly at run time.

I have the following shader with the properties listed below:

Properties
{
_Color(“Color”, Color) = (1,1,1,1)
_MainTex(“Albedo (RGB)”, 2D) = “white” {}
_NormalMap(“Normal map”, 2D) = “normal” {}
_Glossiness(“Smoothness”, Range(0,1)) = 0.5
_Metallic(“Metallic”, Range(0,1)) = 0.0
_GlowStrength(“Glow Strength”, range(0,1)) = 1
[HDR] _Emission(“Emission”, color) = (0,0,0)
[Toggle] _CenterGlow(“Glow from Center”, float) = 0
[PerRendererData] _StencilMask(“Stencil Mask”, Int) = 0
}

The property I’m trying to change per object is the _StencilMask. I have 3 copies of the same mesh, each with 6 parts. Each corresponding part has the same material, for a total of 6 materials. But they all use the same shader. I could use a single material for all of them, but since some of them has normal maps I’d rather not do it through code.

I have the following script to assign the _StencilMask integer (#1-3) at run time to each set of objects:

public class SetStencil : MonoBehaviour
{
public GameObject[ ] p1Stuff = new GameObject[5], p2Stuff = new GameObject[5], p3Stuff = new GameObject[5];
private MaterialPropertyBlock mask1, mask2, mask3;
void Awake() //sets up property renderer compponents for property blocks.
{
mask1 = new MaterialPropertyBlock();
mask2 = new MaterialPropertyBlock();
mask3 = new MaterialPropertyBlock();
mask1.SetInt(“_StencilMask”, 1);
mask2.SetInt(“_StencilMask”, 2);
mask3.SetInt(“_StencilMask”, 3);
}
private void AssignMask(GameObject[ ] _obj, MaterialPropertyBlock _mask)
{
for (int i = 0; i < 5; i++)
{
_obj.GetComponent().SetPropertyBlock(_mask);
Debug.Log(_obj.GetComponent().HasPropertyBlock());
}
}
private void Start()
{
AssignMask(p1Stuff, mask1);
AssignMask(p2Stuff, mask2);
AssignMask(p3Stuff, mask3);

//Debug.Log(“mask 1 is " + mask1.GetInt(”_StencilMask")); //These are correct
//Debug.Log(“mask 2 is " + mask2.GetInt(”_StencilMask"));
//Debug.Log(“mask 3 is " + mask3.GetInt(”_StencilMask"));
//Debug.Log(“artery 1 mask is " + p1Stuff[0].GetComponent().material.GetInt(”_StencilMask")); //These are incorrect.
//Debug.Log(“artery 2 mask is " + p2Stuff[0].GetComponent().material.GetInt(”_StencilMask"));
//Debug.Log(“artery 3 mask is " + p3Stuff[0].GetComponent().material.GetInt(”_StencilMask"));
}
}

All the objects are assigned correctly in editor, but for some reason, the AssignMask() functions are either being ignored or is not assigning the values correctly, and I can’t seem to figure out why. The materials property blocks are being assigned correctly, as shown by the debug logs below, but they aren’t being transferred to the material.

If the script was working, p1Stuff[0], p2Stuff[0], and p3Stuff[0], should have the _StencilMask values of 1,2 and 3, respectively. They are all currently 0 (default), so the _StencilMask is not being assigned.

I’m at a loss as to what I’m doing wrong. It might be something so obvious that i’m overlooking. Any help in the matter would be much appreciated.

Thanks in advance. :slight_smile:

First thing:

This does nothing. It’s a legacy thing related to sprite rendering. All it does is hide the value from the inspector.

Second thing:

I’m going to assume that in the shader you have something like this:

Stencil {
    Ref [_StencilMask]
    // etc

You cannot affect anything outside of the CGPROGRAM block (or HLSLPROGRAM) via MaterialPropertyBlock settings. Anything to do with render state, which is most of the stuff outside of the CGPROGRAM block, must be set on the material directly.

If you’re targeting Direct3D 12, and really do mean the Stencil Ref and not Stencil Mask (which is a thing) as I suspect you are, and not using Surface Shaders, then you can actually output the stencil ref in the shader using SV_StencilRef. However, I’m going to guess at least two of those aren’t true.

Hi, thank you for the response.

Didn’t know PerRendererData was outdated. I just found a tutorial on it and followed it.

I have 2 shaders, one for the stencil mask, and another for the stenciled geometry. The geometry shader does have ref [_StencilMask]. Both shader works as it should, it’s just the properties block that doesn’t. Just started writing shaders a little while ago, so I’m not all that familiar with how to write shaders. Most of what I know are just cobbled together from Youtube, some online tutorials, and just random trial and error from messing with shaders I’ve downloaded. I’d love to find a food reference.

I am using a surface shader. I don’t actually know how to work with anything other than surface shaders since it was what I started with simply out of necessity. I’m not sure what Android phones use in terms of DirectX. The full shader are the following:

Shader "Custom/StandardWithGlowStencil"
{
    Properties
    {
        _Color("Color", Color) = (1,1,1,1)
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _NormalMap("Normal map", 2D) = "normal" {}
        _Glossiness("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metallic", Range(0,1)) = 0.0
        _GlowStrength("Glow Strength", range(0,1)) = 1
        [HDR] _Emission("Emission", color) = (0,0,0)
        [Toggle] _CenterGlow("Glow from Center", float) = 0
        [PerRendererData] _StencilMask("Stencil Mask", Int) = 0 //Sentcil Mask reference number. Must have a matching stencil mask with the same value.
    }
        SubShader
        {
            Tags { "RenderType" = "Opaque" }
            Cull off
            ZWrite On
            LOD 200

            Cull Back
            Stencil
            {
                Ref[_StencilMask]
                Comp equal
                Pass keep
                Fail keep
            }
          
            CGPROGRAM //Render frontfaces

            #pragma surface surf Standard fullforwardshadows      
            #pragma target 3.0

            sampler2D _MainTex;
            sampler2D _NormalMap;

            struct Input
            {
                float2 uv_MainTex;
                float2 uv_NormalMap;
                float3 worldPos;
                float3 viewDir;
            };

            half _Glossiness;
            half _Metallic;
            fixed4 _Color;
            half3 _Emission;
            float _GlowStrength;
            float _CenterGlow;

            UNITY_INSTANCING_BUFFER_START(Props)
            UNITY_INSTANCING_BUFFER_END(Props)

            void surf(Input IN, inout SurfaceOutputStandard o)
            {
                fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;

                //Rendering segment
                o.Albedo = c.rgb;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
                o.Metallic = _Metallic;
                o.Smoothness = _Glossiness;
                o.Alpha = c.a;

                //Glow effect segment
                half factor = dot(normalize(IN.viewDir), o.Normal);
                if (_CenterGlow == 1) //center glow
                {
                    o.Emission = (_Emission * factor) * _GlowStrength * 2;
                }
                else //rim glow
                {
                    o.Emission = (_Emission / factor) * _GlowStrength;
                }
            }
            ENDCG
        }
            FallBack "Diffuse"
}

I’ve gone back to the usual method of using the Getcomponent. I don’t think have 18 instances of a material will cause too much of a problem?

18 materials should not be an issue, no.