In this thread someone says "use the instance id in your shaders to index into the arrays." Is there really a way to access the instance id as a integer value in the shader? On this page about GPU Instancing it refers to UNITY_INSTANCE_ID but if I try using that in my shader I get an error. Same for SV_InstanceID which it mentions. The UNITY_SETUP_INSTANCE_ID() function is working fine for me, but I don't know how to access the actual ID. I'm on 2017.2 so I'm not out of date. I guess my thought is, if I have some attributes I want to vary, I could have it changed based on the ID without making additional properties for the MaterialPropertyBlock.
That page you linked to is the documentation for 5.4. The macros for instancing have changed since then. UNITY_INSTANCE_ID is now UNITY_VERTEX_INPUT_INSTANCE_ID. https://docs.unity3d.com/Manual/GPUInstancing.html If you want to access the actual id, it's stored in the local variable unity_InstanceID after UNITY_SETUP_INSTANCE_ID(?) is called. Also what all of the macros are and what they do are defined in the UnityInstancing.cginc file. You can look at that by downloading the built in shader source here: https://unity3d.com/get-unity/download/archive
Okay, UNITY_GET_INSTANCE_ID(v) does what I want. I overlooked it as first because it gives me an "undeclared identifier" error but if I ignore that and run it anyways, it does what I want. Thanks.
I've got a shader I'm working on that I'm using with Graphics.DrawMeshInstanced() and it draws my instances just fine (I'm using UNITY_SETUP_INSTANCE_ID() at the top of my vertex shader), but when I try to access unity_InstanceID after that, I get an undeclared identifier. I thought it was supposed to be globally accessible... any idea what's happening here?
alright, I wrapped the line using unityInstanceID in a #ifdef UNITY_INSTANCING_ENABLED and now it doesn't complain about an undeclared indentifier. Am I dense? Not sure why this should work because that is the only thing I changed and I didn't do anything that would have added a #define anywhere. Maybe I don't really understand preprocessor macros?
If UNITY_INSTANCING_ENABLED isn't defined, a lot of macros and functions related to instancing don't do anything. Most of the time they're literally blank. Accessing the unity_InstanceID variable directly in that case will cause the error since it was never defined, as it is only defined when UNITY_INSTANCING_ENABLED is also defined. So what defines that define? It's part of the #pragma multi_compile_instancing mentioned in that link about instancing. If you're using a surface Shader, that pragma is added automatically. Technically I believe that multi compile enables one of a few other defines, and the UNITY_INSTANCING_ENABLED gets defined in one of the cginc files when any of the ones from the multi compile is enabled, but that's not important.
I'm still a little confused because the only difference was wrapping the line that uses unity_InstanceID in the #ifdef, but hey... it's working now so I'm just gonna roll with it. Can I assume that unity_InstanceID will begin at 0 and go up to however many instances I'm drawing in that frame?
An #ifdef is a block of code that will be included or omitted from the shader entirely based on a define. That define can come from the shader code itself (#define PIZZA 1), or from the compiler (like which API is in use), or from Unity (like which version of unity is in use, or those #pragma multi_compile lines). When using #pragma multi_compile or shader_feature, Unity will actually compile multiple different versions of the shader (variants) that it will switch between depending on when needed. This can mean you have several million possible versions of a "single" shader. So, if you have a bit of code like: // near the start of the CGPROGRAM block #pragma multi_compile _ PIZZA #ifdef PIZZA float _pizzaRadius; #endif // in a surf, vert, of frag function float radius = _pizzaRadius; You'll get an error as _pizzaRadius only exists in the variant that PIZZA is defined for, but Unity will compile one version with out PIZZA defined and one with. Instead you need to do this: // near the start of the CGPROGRAM block #pragma multi_compile _ PIZZA #ifdef PIZZA float _pizzaRadius; #endif // in a surf, vert, of frag function #ifdef PIZZA float radius = _pizzaRadius; #else float radius = 0; #endif In that case it'll work because it's not trying to access the _pizzaRadius when it doesn't exist. Roughly, yes. Unity has a unity_BaseInstanceID it adds to the unity_InstanceID that I don't know the purpose of, but I assume there will always be an instance of "0" rendered at some point in the frame.
Mostly it's like what @bgolus said. Thank you very much for that! unity_BaseInstanceID is used where a single instancing array can be shared between multiple draws. This only happens when you use individual prefabs in the scene and let unity renderloop automatically do instancing for you. In that case, imagine you have 100 cubes and 100 spheres in the scene sharing the same material. They will be batched together and unity figures out it is more efficient to upload the instancing array (via a constant buffer update) only once with 200 instances of data, and issue two draws out of it - the first will draw with baseInstanceID being 0, and the second will draw with baseInstanceID being 100. But any way, the instanceID is mostly reset for each draw, so it won't accumulate over a frame or whatever time period. It is only unique for unity to address the instancing array.
Sorry for necro-posting but is this why the instancing CBuffer is dynamicIndexed? And is that bad for performance versus immediate indexed? I guess if we render our Meshes from script with DrawMeshInstanced we can get rid of the baseInstanceID right? Or is it not worth the hasle?