[Released] GPU Instancer Pro

Do you have an expected release date for the “crowd animations” tool that will be compatible with the Pro version?

The crowd extension is still in development and does not have a confirmed release date yet. My estimated release timeline is approximately 2-3 months, but I can’t make any promises, as delays are possible.

Hi @GurhanH,

I am working with GPU Instancer Pro and trying to dynamically update the material properties of my instances, specifically the TVE _ThirdIntensityValue property (its Detail value). I have followed the standard approach by creating a GraphicsBuffer, registering it with the GPUI renderer using AddMaterialPropertyOverride, and updating the buffer using SetData at runtime. However, the changes to the specific material property do not reflect on my instances.

Here’s a brief outline of my setup:

  1. I register a GraphicsBuffer for _ThirdIntensityValue and link it to my prototype’s renderer.
  2. Each instance gets an index, and the corresponding value in the buffer is updated.
  3. I call SetData on the buffer whenever an intensity value changes.

Despite this, the material does not seem to update correctly. I also tested a TVE element with GPU instancing and successfully changed the instance color—it worked great! I have a question: Do I need to modify the default TVE shader to implement this properly, or is there a built-in way to support instance-based material property overrides without shader modifications? I would appreciate any guidance.

Here is the core of my implementation:

using UnityEngine;
using System.Collections.Generic;
using GPUInstancerPro;

public class GPUInstanceManager : MonoBehaviour
{
public static GPUInstanceManager Instance { get; private set; }

private class InstanceData
{
    public int rendererKey;
    public Matrix4x4[] matrices;
    public float[] thirdIntensityValues; // Для TVE _ThirdIntensityValue
    public GraphicsBuffer intensityBuffer;
    public Queue<int> freeIndices = new Queue<int>();
    public int count;
}

private Dictionary<GameObject, InstanceData> _prototypeData = new Dictionary<GameObject, InstanceData>();
private Dictionary<int, (GameObject, int)> _instanceMap = new Dictionary<int, (GameObject, int)>();
private int _nextInstanceID;
private const int INITIAL_BUFFER_SIZE = 512;

private void Awake() => Instance = this;

public int RegisterInstance(ref GameObject prototype, Vector3 position, Quaternion rotation, Vector3 scale)
{
    if (!_prototypeData.TryGetValue(prototype, out InstanceData instanceData))
    {
        instanceData = new InstanceData
        {
            matrices = new Matrix4x4[INITIAL_BUFFER_SIZE],
            thirdIntensityValues = new float[INITIAL_BUFFER_SIZE],
            intensityBuffer = new GraphicsBuffer(
                GraphicsBuffer.Target.Structured,
                INITIAL_BUFFER_SIZE,
                sizeof(float)
            )
        };

        if (GPUICoreAPI.RegisterRenderer(this, prototype, out instanceData.rendererKey))
        {
            GPUICoreAPI.AddMaterialPropertyOverride(
                instanceData.rendererKey,
                "_ThirdIntensityValue",
                instanceData.intensityBuffer
            );
        }

        _prototypeData.Add(prototype, instanceData);
    }

    int instanceIndex = GetNextAvailableIndex(instanceData);
    UpdateInstanceData(instanceData, instanceIndex, position, rotation, scale, 0f);
    return RegisterInstanceID(prototype, instanceData, instanceIndex);
}

private int GetNextAvailableIndex(InstanceData data)
{
    return data.freeIndices.Count > 0 ? data.freeIndices.Dequeue() : data.count++;
}

private void UpdateInstanceData(InstanceData data, int index, Vector3 pos, Quaternion rot, Vector3 scale, float intensity)
{
    data.matrices[index] = Matrix4x4.TRS(pos, rot, scale);
    data.thirdIntensityValues[index] = intensity;
    data.intensityBuffer.SetData(data.thirdIntensityValues);
    GPUICoreAPI.SetTransformBufferData(data.rendererKey, data.matrices);
}

private int RegisterInstanceID(GameObject prototype, InstanceData data, int index)
{
    int instanceID = _nextInstanceID++;
    _instanceMap.Add(instanceID, (prototype, index));
    return instanceID;
}

public void UpdateIntensity(int instanceID, float intensity)
{
    if (_instanceMap.TryGetValue(instanceID, out (GameObject prototype, int index) data))
    {
        InstanceData instanceData = _prototypeData[data.prototype];
        instanceData.thirdIntensityValues[data.index] = intensity;
        instanceData.intensityBuffer.SetData(instanceData.thirdIntensityValues);
    }
}

public void UnregisterInstance(int instanceID)
{
    if (_instanceMap.TryGetValue(instanceID, out (GameObject prototype, int index) data))
    {
        InstanceData instanceData = _prototypeData[data.prototype];
        instanceData.freeIndices.Enqueue(data.index);
        instanceData.thirdIntensityValues[data.index] = 0f;
        instanceData.intensityBuffer.SetData(instanceData.thirdIntensityValues);
        GPUICoreAPI.DisposeRenderer(instanceID);
        _instanceMap.Remove(instanceID);
    }
}

private void OnDestroy()
{
    GPUICoreAPI.DisposeAll();
}

}

Hi there,
You need to modify the shader to add instance-based material variations. You can refer to the documentation for details on manually setting up shaders to include material variations.

Alternatively, there is a tool that can automatically add material variations to shaders, which might be helpful. However, it may not work with every shader or property.

How to Use the Tool:

  1. Right-click in the Project window and select Create → Rendering → GPU Instancer Pro → Material Variation Definition. This will create a Material Variation Definition ScriptableObject.
  2. Assign your material to the Material field.
  3. Define a Buffer Name (e.g., "gpuiProFloat4Variation").
  4. Under Variation Properties, click Add Property and select _ThirdIntensityValue from the dropdown.
  5. Click Generate Shader.

This will create a new shader with a material variation setup for _ThirdIntensityValue. The generated shader file can be found at the “Variation Shader” field.

Implementing the Variation Shader:

  1. Create a copy of the original material and assign the newly generated variation shader to it (located under GPUInstancerPro/Variation/).

  2. Assign this new material to your prefab’s renderers.

  3. In your script, enable material variations by calling:
    material.EnableKeyword("GPUI_MATERIAL_VARIATION");

  4. Add an override using the Buffer Name you defined:
    GPUICoreAPI.AddMaterialPropertyOverride(instanceData.rendererKey, "gpuiProFloat4Variation", instanceData.intensityBuffer);

Important Notes:

  • The tool generates float4 buffers by default. So, instead of using:
    float[] thirdIntensityValues;

    you should use:

    Vector4[] thirdIntensityValues;

    Then, access the property values via Vector4.x.

  • If you add another property later to the Material Variation Definition asset, the buffer size should double. For example, if you have 100 instances, the buffer size should be 200, and values should be accessed like this:

 data.thirdIntensityValues[index * 2 + 0] = intensity;
 data.thirdIntensityValues[index * 2 + 1] = anotherProperty;

Huge thank you for your guidance on the material variation setup! With your help, the instance-based properties are now working flawlessly in GPU Instancer Pro. :raised_hands:

Like many other users, I am eagerly awaiting the release of crowd animations for the pro version.

Keep up the amazing work! Thank You


1 Like

In one of older versions of GPU Instancer, there was a feature that would detect all the prefabs in the scene and import them right into the prefab manager. Is that feature still available in the latest GPU Instance pro version? I am not sure if I just cant find it or if the feature has been discontinued. It was really handy.

Hi there,
The Scene Prefab Importer tool is integrated into the Prefab Manager.

When you click the Add button in the Prefab Manager, the Prefab Selector window will open. Prefabs that have instances in the current scene will be listed at the top, along with their instance counts.

You can select multiple prefabs using Shift + Click or Ctrl + Click, then add them to the manager.

Is it possible to modify the density multiplier on grass details? It would be nice to lower the grass density on lower graphics quality settings, but it’s not exposed in the profile and I can’t find anything exposed on the detail manager to change this at runtime. Is it not possible?

Hi there,
You can find an example code in this post that demonstrates how to modify detail prototype densities at runtime:

Can GPUI Prefab Manager be used with Bakelight scenes?

Baked lighting is not supported. Please refer to the Known Limitations for more details.

Hi @GurhanH , is there a way to render the prefab manager instances in edit mode? I manually paint a lot of ivy vegetation elements on non-terrain objects so i have a lot of them in Edit Mode.

Or is it alternatively possible to render them via the Detail Manager instead of the Prefab Manager, even if they are not part of the terrain?

Hi there,
I’ve added an edit mode renderer to the Prefab Manager for prototypes where the ‘Disable Default Renderers’ button is clicked under Advanced Actions. This feature will be available in the next update, which should be released within the week.

Using the Detail Manager without a Unity terrain, as well as implementing no-GameObject workflows while maintaining instance control in edit mode, are both planned as extension packages. However, these are still in development and do not yet have release dates.

I am using GPU instancer in a situation where I have trees on multiple terrains.

  1. I want to disable some terrains, and I guess I can do that by disabling the tree manager? How?

  2. Now I want to move a terrain and want GPU instancer via tree manager to also update the tree positions. What do I need to do?

More performance questions:

  1. Trees on a unity terrain with lodgroups have a high overhead cost. Even when not visible, the lodgroup calculations seem still to be done. GPU instancer is much faster here. But what happens if I set draw distance of a tree prototype in GPU instancer to close (cull close, don’t draw far away). Does it still have the same overhead cost for “lodgroup” calculation + frustum cull + oclussion cull? Or does the system get faster when I stop drawing trees close to camera? Not talking about pixel costs here, but about overhead costs.

Same questions but on PRO: I’m also considering to purchase GPU instancer pro, hoping for even faster performance.

          1. How are above questions in regard to GPU instancer PRO?

Thanks a lot

GPU Instancer (non-Pro)
1- If you are using a dedicated Tree Manager for each terrain, you can attach the Tree Manager to the terrain GameObject, so it gets disabled or destroyed along with the terrain. However, using multiple Tree Managers may lead to performance issues, as draw calls will not be batched.

  • If you use a single Tree Manager for all terrains, you can remove a terrain using the GPUInstancerAPI.RemoveTerrainFromManager method or by adding the GPUInstancerTerrainRuntimeHandler script to the terrain. This script ensures the terrain is removed from the Tree Manager when the GameObject is disabled or destroyed.
  • Keep in mind that all terrains must have identical tree prototypes in the same order for multi-terrain support to function correctly in the non-Pro version.

2- After moving the terrain positions, call GPUInstancerTreeManager.UpdateTerrains to update the tree instances accordingly.

3- GPU Instancer efficiently handles LOD and culling calculations on the GPU.

  • Distance culling is the first step; if an instance is beyond the defined range, further calculations are skipped.
  • However, draw calls and compute shaders are still executed, even if no instances are currently visible. Since culling happens on the GPU, the CPU cannot determine visibility.
  • If you know that no terrains will be visible at certain times, disabling the manager may improve performance.

GPU Instancer Pro
4- When a terrain GameObject is disabled or destroyed, it is automatically removed from the Tree Manager. Additionally, multiple terrains can have different tree prototypes, even if they were not initially assigned in the Tree Manager. Draw calls for the same prefab from multiple Tree Managers or Prefab Managers will also be automatically batched for optimal performance.

5- When terrain positions change, tree positions update automatically without requiring any additional action.

6- Same as the non-Pro version, but future updates may introduce additional CPU culling options. Additionally, you can disable specific tree prototypes to eliminate any overhead caused by unused prototypes.

1 Like

Very nice!! Thanks for the detailed reply, this sounds like a no brainer to me :slight_smile: switch to PRO makes my life easier, yay!!

hey gurhan,

looking into gpui pro and terrain details it looks as if gpui pro only applies half the “align to ground %” thing specified in the “detail mesh”: it simply is not really aligned as shown in the screen shot attached. this unfortunately leads to a “terrassed” look of the grass on any slope of the terrain which is not acceptable as it breaks pretty much everything.

EDIT: i had a quick look into “GPUIVegetationGeneratorCS”:
adding
p_alignToGround *= 2.0;
fixes the alignment. so i guess it can be fixed on the c# side as well.

1 Like

furthermore the buggy support of motion vectors for vertex animations leads to either:

  • super blurry images in case taa, fsr or dlss are enabled
  • or randomly popping instances of trees or grass
    both ruining the entire image which is not really acceptable…
    do you have have plans to get tis fixed? and maybe even an eta?
1 Like

Hi there,
Terrain alignment is applied based on the Align to Ground (%) value set in the Unity terrain detail prototype. I did not observe alignment issues during testing. Please feel free to contact support email with a repro project, and I can investigate. Certain resolution values might be causing the issue.

Please note that terrain alignment works on a per-instance basis. Rotation is applied according to the terrain normal at the pivot position of each instance. Aligning large detail patches may require per-vertex height calculations in the shader. I will look into adding examples and methods for applying vertex-based terrain alignment in shaders in a future update.

GPUI Pro supports motion vectors for Camera Motion Only by default. Support for Per Object Motion can be enabled under the Experimental section of the Profile. However, Per Object Motion is mainly intended for prefab instancing and is generally not applicable to terrain prototypes.

The Enable Per Object Motion setting was previously named Enable Motion Vectors, which caused confusion. This was corrected in version 0.9.17.

GPUI Pro does not provide motion vectors for vertex animations. Since vertex animations are applied within shaders, the shader itself must also generate the motion vectors. I will explore adding a variant of the included Foliage shader that incorporates vertex-based motion vectors. However, other shaders will still need to implement their own motion vector calculations for vertex animations.

1 Like

hi gurhan,

thanks a lot for your quick reply.

alignment
as i wrote adding p_alignToGround *= 2.0; to the compute shader fixes it and gives me the “exact” same rotation as i get from unity’s built in grass rendering.
it is not related to resolution (how should it as you use the heightmapTextureSize to create the sample points?).
and even if the grass mesh in my example is huge its center should be rotated properly. which it gets using the hack mentioned above. and to make clear: placing a single flower gives my the same non fitting alignment. using the hack this gets fixed as well.

1 Like