// Start is called before the first frame update void Start() { m_BatchRendererGroup = new BatchRendererGroup(this.OnPerformCulling, IntPtr.Zero); int itemCount = itemGridSize * itemGridSize; m_itemCount = itemCount; // Bounds UnityEngine.Bounds bounds = new Bounds(new Vector3(0, 0, 0), new Vector3(1048576.0f, 1048576.0f, 1048576.0f)); m_BatchRendererGroup.SetGlobalBounds(bounds); // Register mesh and material if (m_mesh) { m_meshID = m_BatchRendererGroup.RegisterMesh(m_mesh); } if (m_material) { m_materialID = m_BatchRendererGroup.RegisterMaterial(m_material); } // Batch metadata buffer int objectToWorldID = Shader.PropertyToID("unity_ObjectToWorld"); int matrixPreviousMID = Shader.PropertyToID("unity_MatrixPreviousM"); int worldToObjectID = Shader.PropertyToID("unity_WorldToObject"); int colorID = Shader.PropertyToID("_BaseColor"); // Generate a grid of objects... int bigDataBufferVector4Count = 4 + itemCount * (3 * 3 + 1); // 4xfloat4 zero + per instance = { 3x mat4x3, 1x float4 color } m_sysmemBuffer = new NativeArray <Vector4>(bigDataBufferVector4Count, Allocator.Persistent, NativeArrayOptions.ClearMemory); m_GPUPersistentInstanceData = new GraphicsBuffer(GraphicsBuffer.Target.Raw, (int)bigDataBufferVector4Count * 16 / 4, 4); // 64 bytes of zeroes, so loads from address 0 return zeroes. This is a BatchRendererGroup convention. int positionOffset = 4; m_sysmemBuffer[0] = new Vector4(0, 0, 0, 0); m_sysmemBuffer[1] = new Vector4(0, 0, 0, 0); m_sysmemBuffer[2] = new Vector4(0, 0, 0, 0); m_sysmemBuffer[3] = new Vector4(0, 0, 0, 0); // Matrices UpdatePositions(m_center); // Colors int colorOffset = positionOffset + itemCount * 3 * 3; for (int i = 0; i < itemCount; i++) { Color col = Color.HSVToRGB(((float)(i) / (float)itemCount) % 1.0f, 1.0f, 1.0f); // write colors right after the 4x3 matrices m_sysmemBuffer[colorOffset + i] = new Vector4(col.r, col.g, col.b, 1.0f); } m_GPUPersistentInstanceData.SetData(m_sysmemBuffer); var batchMetadata = new NativeArray <MetadataValue>(4, Allocator.Temp, NativeArrayOptions.UninitializedMemory); batchMetadata[0] = CreateMetadataValue(objectToWorldID, 64, true); // matrices batchMetadata[1] = CreateMetadataValue(matrixPreviousMID, 64 + itemCount * UnsafeUtility.SizeOf <Vector4>() * 3, true); // previous matrices batchMetadata[2] = CreateMetadataValue(worldToObjectID, 64 + itemCount * UnsafeUtility.SizeOf <Vector4>() * 3 * 2, true); // inverse matrices batchMetadata[3] = CreateMetadataValue(colorID, 64 + itemCount * UnsafeUtility.SizeOf <Vector4>() * 3 * 3, true); // colors // Register batch m_batchID = m_BatchRendererGroup.AddBatch(batchMetadata, m_GPUPersistentInstanceData.bufferHandle); m_initialized = true; }
// During initialization, we will allocate all required objects, and set up our custom instance data. void Start() { // Create the BatchRendererGroup and register assets m_BRG = new BatchRendererGroup(this.OnPerformCulling, IntPtr.Zero); m_MeshID = m_BRG.RegisterMesh(mesh); m_MaterialID = m_BRG.RegisterMaterial(material); // Create the buffer that holds our instance data m_InstanceData = new GraphicsBuffer(GraphicsBuffer.Target.Raw, (kExtraBytes + kBytesPerInstance * kNumInstances) / sizeof(int), sizeof(int)); // Place one zero matrix at the start of the instance data buffer, so loads from address 0 will return zero var zero = new Matrix4x4[1] { Matrix4x4.zero }; // Create transform matrices for our three example instances var matrices = new Matrix4x4[kNumInstances] { Matrix4x4.Translate(new Vector3(-2, 0, 0)), Matrix4x4.Translate(new Vector3(0, 0, 0)), Matrix4x4.Translate(new Vector3(2, 0, 0)), }; // Convert the transform matrices into the packed format expected by the shader var objectToWorld = new PackedMatrix[kNumInstances] { new PackedMatrix(matrices[0]), new PackedMatrix(matrices[1]), new PackedMatrix(matrices[2]), }; // Also create packed inverse matrices var worldToObject = new PackedMatrix[kNumInstances] { new PackedMatrix(matrices[0].inverse), new PackedMatrix(matrices[1].inverse), new PackedMatrix(matrices[2].inverse), }; // Make all instances have unique colors var colors = new Vector4[kNumInstances] { new Vector4(1, 0, 0, 1), new Vector4(0, 1, 0, 1), new Vector4(0, 0, 1, 1), }; // In this simple example, the instance data is placed into the buffer like this: // Offset | Description // 0 | 64 bytes of zeroes, so loads from address 0 return zeroes // 64 | 32 uninitialized bytes to make working with SetData easier, otherwise unnecessary // 96 | unity_ObjectToWorld, three packed float3x4 matrices // 240 | unity_WorldToObject, three packed float3x4 matrices // 384 | _BaseColor, three float4s // Compute start addresses for the different instanced properties. unity_ObjectToWorld starts // at address 96 instead of 64, because the computeBufferStartIndex parameter of SetData // is expressed as source array elements, so it is easier to work in multiples of sizeof(PackedMatrix). uint byteAddressObjectToWorld = kSizeOfPackedMatrix * 2; uint byteAddressWorldToObject = byteAddressObjectToWorld + kSizeOfPackedMatrix * kNumInstances; uint byteAddressColor = byteAddressWorldToObject + kSizeOfPackedMatrix * kNumInstances; // Upload our instance data to the GraphicsBuffer, from where the shader can load them. m_InstanceData.SetData(zero, 0, 0, 1); m_InstanceData.SetData(objectToWorld, 0, (int)(byteAddressObjectToWorld / kSizeOfPackedMatrix), objectToWorld.Length); m_InstanceData.SetData(worldToObject, 0, (int)(byteAddressWorldToObject / kSizeOfPackedMatrix), worldToObject.Length); m_InstanceData.SetData(colors, 0, (int)(byteAddressColor / kSizeOfFloat4), colors.Length); // Set up metadata values to point to the instance data. Set the most significant bit 0x80000000 in each, // which instructs the shader that the data is an array with one value per instance, indexed by the instance index. // Any metadata values used by the shader and not set here will be zero. When such a value is used with // UNITY_ACCESS_DOTS_INSTANCED_PROP (i.e. without a default), the shader will interpret the // 0x00000000 metadata value so that the value will be loaded from the start of the buffer, which is // where we uploaded the matrix "zero" to, so such loads are guaranteed to return zero, which is a reasonable // default value. var metadata = new NativeArray <MetadataValue>(3, Allocator.Temp); metadata[0] = new MetadataValue { NameID = Shader.PropertyToID("unity_ObjectToWorld"), Value = 0x80000000 | byteAddressObjectToWorld, }; metadata[1] = new MetadataValue { NameID = Shader.PropertyToID("unity_WorldToObject"), Value = 0x80000000 | byteAddressWorldToObject, }; metadata[2] = new MetadataValue { NameID = Shader.PropertyToID("_BaseColor"), Value = 0x80000000 | byteAddressColor, }; // Finally, create a batch for our instances, and make the batch use the GraphicsBuffer with our // instance data, and the metadata values that specify where the properties are. Note that // we do not need to pass any batch size here. m_BatchID = m_BRG.AddBatch(metadata, m_InstanceData.bufferHandle); }