Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Non-instanced properties set for instanced shader

Discussion in 'General Graphics' started by Nihil688, Oct 24, 2018.

  1. Nihil688

    Nihil688

    Joined:
    Mar 12, 2013
    Posts:
    503
    I'm a bit confused as to what that means and where it's encountered. I have one mesh renderer to which I'm changing it's emissive color in an update and it batches everything in 1 draw call and I have another mesh renderer where I change it's texture and it gives me the above error whilst both are using MaterialPropertyBlocks. Triangles are less than 300 in both and the shaders are pretty standard.

    Also if I disable Instancing I get "objects have different material property blocks set" which is even weirder.

    I've used U2017.7 and U2018.2.6
     
    Last edited: Oct 24, 2018
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    You can only instance objects with properties that have been setup for instancing within the shader. Not all properties for all shaders are setup to be instanced, in fact most material properties aren't. Additionally changing a material's texture, be it via a material property block or on the material itself, will prevent instancing (or batching) from working, period. It's simply not possible to setup a texture property to work with instancing due to how instancing and textures work on GPUs.

    Further, static or dynamic batching requires a single unmodified material. Any modifications, even with material property blocks, will break batching. This is by design, again, due to how GPUs work.
     
    mannyhams likes this.
  3. Nihil688

    Nihil688

    Joined:
    Mar 12, 2013
    Posts:
    503
    Can it still be batched somehow? Even without instancing? I knew that I was missing something when I put back PerRendererData in the textures

    Code (CSharp):
    1. // Upgrade NOTE: upgraded instancing buffer 'TerahardTestingInstancedProperties' to new syntax.
    2.  
    3. Shader "Terahard/Testing/InstancedProperties"
    4. {
    5.     Properties
    6.     {
    7.         [PerRendererData]_Color("Color", Color) = (0.9044118,0.6371242,0.4921064,0)
    8.         [PerRendererData]_Albedo("Albedo", 2D) = "white" {}
    9.         [PerRendererData]_EmissiveColor("EmissiveColor", Color) = (0,0,0,0)
    10.         [PerRendererData]_Emissive("Emissive", 2D) = "white" {}
    11.         [PerRendererData]_Metallic("Metallic", Range( 0 , 1)) = 0.3
    12.         [PerRendererData]_Smoothness("Smoothness", Range( 0 , 1)) = 0.3
    13.         [HideInInspector] _texcoord( "", 2D ) = "white" {}
    14.         [HideInInspector] __dirty( "", Int ) = 1
    15.     }
    16.  
    17.     SubShader
    18.     {
    19.         Tags{ "RenderType" = "Opaque"  "Queue" = "Geometry+0" "IsEmissive" = "true"  }
    20.         Cull Back
    21.         CGPROGRAM
    22.         #pragma target 3.0
    23.         #pragma multi_compile_instancing
    24.         #pragma surface surf Standard keepalpha addshadow fullforwardshadows
    25.         struct Input
    26.         {
    27.             float2 uv_texcoord;
    28.         };
    29.  
    30.         uniform sampler2D _Albedo;
    31.         uniform float4 _Albedo_ST;
    32.         uniform sampler2D _Emissive;
    33.         uniform float4 _Emissive_ST;
    34.  
    35.         UNITY_INSTANCING_BUFFER_START(TerahardTestingInstancedProperties)
    36.             UNITY_DEFINE_INSTANCED_PROP(float4, _EmissiveColor)
    37. #define _EmissiveColor_arr TerahardTestingInstancedProperties
    38.             UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
    39. #define _Color_arr TerahardTestingInstancedProperties
    40.             UNITY_DEFINE_INSTANCED_PROP(float, _Smoothness)
    41. #define _Smoothness_arr TerahardTestingInstancedProperties
    42.             UNITY_DEFINE_INSTANCED_PROP(float, _Metallic)
    43. #define _Metallic_arr TerahardTestingInstancedProperties
    44.         UNITY_INSTANCING_BUFFER_END(TerahardTestingInstancedProperties)
    45.  
    46.         void surf( Input i , inout SurfaceOutputStandard o )
    47.         {
    48.             float4 _Color_Instance = UNITY_ACCESS_INSTANCED_PROP(_Color_arr, _Color);
    49.             float2 uv_Albedo = i.uv_texcoord * _Albedo_ST.xy + _Albedo_ST.zw;
    50.             o.Albedo = ( _Color_Instance * tex2D( _Albedo, uv_Albedo ) ).rgb;
    51.             float2 uv_Emissive = i.uv_texcoord * _Emissive_ST.xy + _Emissive_ST.zw;
    52.             float4 _EmissiveColor_Instance = UNITY_ACCESS_INSTANCED_PROP(_EmissiveColor_arr, _EmissiveColor);
    53.             o.Emission = ( tex2D( _Emissive, uv_Emissive ) * _EmissiveColor_Instance ).rgb;
    54.             float _Metallic_Instance = UNITY_ACCESS_INSTANCED_PROP(_Metallic_arr, _Metallic);
    55.             o.Metallic = _Metallic_Instance;
    56.             float _Smoothness_Instance = UNITY_ACCESS_INSTANCED_PROP(_Smoothness_arr, _Smoothness);
    57.             o.Smoothness = _Smoothness_Instance;
    58.             o.Alpha = 1;
    59.         }
    60.  
    61.         ENDCG
    62.     }
    63.     Fallback "Diffuse"
    64. }
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    This doesn't actually do anything apart from hide it in the inspector window.

    Instancing and batching are at odds with each other.

    Instancing = single mesh and single material; bool, float, vector, and color values modifiable per-instance via material property blocks. Has to be exactly the same mesh and material asset on all instances. Textures, buffers, or array properties cannot be modified per-instance.
    If you want to use multiple textures you need to use a Texture2DArray you construct yourself, and set a per-instance index.

    Batching = multiple meshes with single material. Nothing can be modified on the material.
    If you want to modify per mesh values when using batching you need to store data in the meshes' vertices (like in the vertex color or additional UV channels & components). You can also use a Texture2DArray, just store the index in the UV data as well. Unity's meshes have a Vector4/float4 worth of data that can be stored per-UV. By default Unity only uses the first two components of the first two UV sets, with the secondary UV only being used on lightmapped objects.
     
  5. ivangarciafilho

    ivangarciafilho

    Joined:
    Sep 2, 2015
    Posts:
    27
    Even Changing JUST the _Color value breaks instancing ...
    SAME mesh, SAME material, DIFFERENT colors per instance, it's just breaking the instancing value;





    SHADER CODE
    Code (CSharp):
    1. Shader "Custom/InstancingShader"
    2. {
    3.     Properties
    4.     {
    5.         [PerRendererData]_Color ("Color", Color) = (1,1,1,1)
    6.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    7.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    8.         _Metallic ("Metallic", Range(0,1)) = 0.0
    9.     }
    10.     SubShader
    11.     {
    12.         Tags { "RenderType"="Opaque" }
    13.         LOD 200
    14.  
    15.         CGPROGRAM
    16.         // Physically based Standard lighting model, and enable shadows on all light types
    17.         #pragma surface surf Standard fullforwardshadows
    18.  
    19.         // Use shader model 3.0 target, to get nicer looking lighting
    20.         #pragma target 3.0
    21.  
    22.         sampler2D _MainTex;
    23.  
    24.         struct Input
    25.         {
    26.             float2 uv_MainTex;
    27.         };
    28.  
    29.         half _Glossiness;
    30.         half _Metallic;
    31.         fixed4 _Color;
    32.  
    33.         // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    34.         // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    35.         // #pragma instancing_options assumeuniformscaling
    36.         UNITY_INSTANCING_BUFFER_START(Props)
    37.             // put more per-instance properties here
    38.         UNITY_INSTANCING_BUFFER_END(Props)
    39.  
    40.         void surf (Input IN, inout SurfaceOutputStandard o)
    41.         {
    42.             // Albedo comes from a texture tinted by color
    43.             fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    44.             o.Albedo = c.rgb;
    45.             // Metallic and smoothness come from slider variables
    46.             o.Metallic = _Metallic;
    47.             o.Smoothness = _Glossiness;
    48.             o.Alpha = c.a;
    49.         }
    50.         ENDCG
    51.     }
    52.     FallBack "Diffuse"
    53. }
    54.  
    PROPERTY BLOCK CODE

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [ExecuteInEditMode, ExecuteAlways]
    6. public class SetMaterialProperty : MonoBehaviour
    7. {
    8.     [SerializeField] private Color itsColor;
    9.     [SerializeField] private MeshRenderer itsRenderer;
    10.  
    11.     private MaterialPropertyBlock instancePropertyBlock;
    12.     private const int colorConstantID = 171;
    13.  
    14.     private void OnEnable() { SetPropertyBlock(); }
    15.  
    16. #if UNITY_EDITOR
    17.     private void OnValidate() { SetPropertyBlock(); }
    18. #endif
    19.  
    20.  
    21.     private void SetPropertyBlock()
    22.     {
    23. #if UNITY_EDITOR
    24.         //Debug.Log(Shader.PropertyToID("_Color")); //_Color = 171
    25.  
    26. #if UNITY_EDITOR
    27.         if (itsRenderer == null)
    28.         {
    29.             itsRenderer = GetComponent<MeshRenderer>();
    30.         }
    31. #endif
    32.         instancePropertyBlock = new MaterialPropertyBlock();
    33.         itsRenderer.GetPropertyBlock(instancePropertyBlock);
    34.         instancePropertyBlock.SetColor(colorConstantID, itsColor);
    35.         itsRenderer.SetPropertyBlock(instancePropertyBlock);
    36.  
    37.         #endif
    38.     }
    39. }
    40.  
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    Your _Color value isn't setup as an instanced property. See my previous post, and the comments about instancing in your own example shader.
     
    chrismarch likes this.
  7. ivangarciafilho

    ivangarciafilho

    Joined:
    Sep 2, 2015
    Posts:
    27
    Thanks, i'm an idiot, i just saw in Unity's documentaiton that i just had to put the attribute [PerRendererData] and everything would happen by "magic" ... I managed to fix It...

    However, is there a way of storing the material property blocks on the scene instead of setting all of them upon start? because I'm "kinda modeling" an whole scene mainly based on triangle primitives to mostly ABUSE of geometry instancing to reduce overdraw, improve culling on a triangle basis (literally).

    Everything in this scene is entirely made out of "Quads" and we want to turn them into triangles
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    Oh boy! Brute forced visibility buffer... I'd be worried your CPU time in culling is going to way exceed the render time you're saving going this route.

    I suspect the route you probably should be taking is using DrawProcedural rather than direct instancing.


    But to answer your question, no. Having scripts that set the values via a material property block at start / awake is the closest you can get. Technically that is storing them in the scene if the script is using serialized variables to hold the data, but I know that's not quite what you meant. The material property block itself doesn't seem serializable, and won't stay "applied" to a renderer.
     
  9. ivangarciafilho

    ivangarciafilho

    Joined:
    Sep 2, 2015
    Posts:
    27
    Thanks for the AWESOME refferences!!!
    We did just a few simple culling techniques ... We are just rellying on :
    • Each quad has an LODGroup component with up to 2 LODs (LOD0/CULL);
    • Each object made out of quads are using one of 4 layers (tiny,small,medium,large);
    • 4 Cameras rendering different layers (tiny, small, medium, llarge) at different Camera.layerCullDistances (0.6m, 1.8m, 5.4m, 16,2m) and different Camera.cullingMatrix (15FOV, 30FOV, 60FOV, 90FOV);
    • Rendering each light with diffferent Light.layerShadowCullDistances (0.3m, 0.9m, 2.7m, 8,1m);
    • Rendering each camera at different framerates and using reprojection + motion blur (15FPS, 30FPS, 60FPS, 120FPS);
    • Rendering the shadows on each camera also at different framerates using reprojection + motion blur (12FPS, 24FPS, 48FPS, 96FPS);
    • Standard Z test culling;
    • Standard triangle sorting;
    The reason for not using a Draw Procedural approach is just because we have to mainly use global illumination and lights/shadows bouncing realtime, i didn't figure out how to create a surface shader with those buffers.
     
    Last edited: Mar 27, 2019
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    My experience has been that multiple cameras is almost never the answer. For us with a relatively simple scene setup, the culling time per camera, even when only a small number of things were visible via layers, was 3~4ms. That was hundreds of times longer than just rendering everything!

    Modern GPUs are surprisingly good at handling overdraw of opaque geometry, and if you’re already using individual quads, Unity is going to be sorting these for you. So overdraw is almost moot unless you’re getting to the multiple millions of vertices.

    My personal thought for this kind of thing would be to go with instanced batches of 64 vertices and use hierarchical LOD to swap.
    http://advances.realtimerendering.c...siggraph2015_combined_final_footer_220dpi.pdf
    https://blogs.unity3d.com/2018/01/1...ting-with-automatic-performance-improvements/
     
    Last edited: Mar 27, 2019
    Jackrabbit82 likes this.
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,229
    Btw draw procedural can work with Unity’s lighting system, with some extra work. You’re not going to get any of the real time bounced lighting either way though since that only works with lightmapped static (non-instanced!) geometry, or lighting from lightprobes which is going to look really weird if each quad/tri is being “lit” individually.