Skip to content

Instantly share code, notes, and snippets.

@MattRix
Last active March 2, 2021 22:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MattRix/1535a026eb8de0e32ee79d9612178fb2 to your computer and use it in GitHub Desktop.
Save MattRix/1535a026eb8de0e32ee79d9612178fb2 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System.Collections;
public class InstancedExample : MonoBehaviour
{
public int instanceCount = 1000;
public Mesh instanceMesh;
public Material instanceMaterial;
public int subMeshIndex = 0;
private int cachedInstanceCount = -1;
private int cachedSubMeshIndex = -1;
private ComputeBuffer positionBuffer;
private ComputeBuffer argsBuffer;
private uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
void Start()
{
argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
UpdateBuffers();
}
void Update()
{
// Update starting position buffer
if (cachedInstanceCount != instanceCount || cachedSubMeshIndex != subMeshIndex)
{
UpdateBuffers();
}
// Pad input
if (Input.GetAxisRaw("Horizontal") != 0.0f)
{
instanceCount = (int)Mathf.Clamp(instanceCount + Input.GetAxis("Horizontal") * 40000, 1.0f, 5000000.0f);
}
// Render
Graphics.DrawMeshInstancedIndirect(instanceMesh, subMeshIndex, instanceMaterial, new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f)), argsBuffer);
}
void OnGUI()
{
GUI.Label(new Rect(265, 25, 200, 30), "Instance Count: " + instanceCount.ToString());
instanceCount = (int)GUI.HorizontalSlider(new Rect(25, 20, 200, 30), (float)instanceCount, 1.0f, 5000000.0f);
}
void UpdateBuffers()
{
// Ensure submesh index is in range
if (instanceMesh != null)
{
subMeshIndex = Mathf.Clamp(subMeshIndex, 0, instanceMesh.subMeshCount - 1);
}
// Positions
if (positionBuffer != null)
{
positionBuffer.Release();
}
positionBuffer = new ComputeBuffer(instanceCount, 16);
Vector4[] positions = new Vector4[instanceCount];
for (int i = 0; i < instanceCount; i++)
{
float angle = Random.Range(0.0f, Mathf.PI * 2.0f);
float distance = Random.Range(20.0f, 100.0f);
float height = Random.Range(-2.0f, 2.0f);
float size = Random.Range(0.05f, 0.25f);
positions[i] = new Vector4(Mathf.Sin(angle) * distance, height, Mathf.Cos(angle) * distance, size);
}
positionBuffer.SetData(positions);
instanceMaterial.SetBuffer("positionBuffer", positionBuffer);
// Indirect args
if (instanceMesh != null)
{
args[0] = (uint)instanceMesh.GetIndexCount(subMeshIndex);
args[1] = (uint)instanceCount;
args[2] = (uint)instanceMesh.GetIndexStart(subMeshIndex);
args[3] = (uint)instanceMesh.GetBaseVertex(subMeshIndex);
}
else
{
args[0] = args[1] = args[2] = args[3] = 0;
}
argsBuffer.SetData(args);
cachedInstanceCount = instanceCount;
cachedSubMeshIndex = subMeshIndex;
}
void OnDisable()
{
if (positionBuffer != null) positionBuffer.Release();
positionBuffer = null;
if (argsBuffer != null) argsBuffer.Release();
argsBuffer = null;
}
}
//from https://docs.unity3d.com/2021.1/Documentation/ScriptReference/Graphics.DrawMeshInstancedIndirect.html
Shader "Instanced/InstancedShader"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader
{
Pass
{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
#pragma target 4.5
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
#include "AutoLight.cginc"
sampler2D _MainTex;
#if SHADER_TARGET >= 45
StructuredBuffer<float4> positionBuffer;
#endif
struct v2f
{
float4 pos : SV_POSITION;
float2 uv_MainTex : TEXCOORD0;
float3 ambient : TEXCOORD1;
float3 diffuse : TEXCOORD2;
float3 color : TEXCOORD3;
SHADOW_COORDS(4)
};
void rotate2D(inout float2 v, float r)
{
float s, c;
sincos(r, s, c);
v = float2(v.x * c - v.y * s, v.x * s + v.y * c);
}
v2f vert (appdata_full v, uint instanceID : SV_InstanceID)
{
#if SHADER_TARGET >= 45
float4 data = positionBuffer[instanceID];
#else
float4 data = 0;
#endif
float rotation = data.w * data.w * _Time.x * 0.5f;
rotate2D(data.xz, rotation);
float3 localPosition = v.vertex.xyz * data.w;
float3 worldPosition = data.xyz + localPosition;
float3 worldNormal = v.normal;
float3 ndotl = saturate(dot(worldNormal, _WorldSpaceLightPos0.xyz));
float3 ambient = ShadeSH9(float4(worldNormal, 1.0f));
float3 diffuse = (ndotl * _LightColor0.rgb);
float3 color = v.color;
v2f o;
o.pos = mul(UNITY_MATRIX_VP, float4(worldPosition, 1.0f));
o.uv_MainTex = v.texcoord;
o.ambient = ambient;
o.diffuse = diffuse;
o.color = color;
TRANSFER_SHADOW(o)
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed shadow = SHADOW_ATTENUATION(i);
fixed4 albedo = tex2D(_MainTex, i.uv_MainTex);
float3 lighting = i.diffuse * shadow + i.ambient;
fixed4 output = fixed4(albedo.rgb * i.color * lighting, albedo.w);
UNITY_APPLY_FOG(i.fogCoord, output);
return output;
}
ENDCG
}
}
}
//from https://docs.unity3d.com/2021.1/Documentation/ScriptReference/Graphics.DrawMeshInstancedIndirect.html
Shader "Instanced/InstancedSurfaceShader"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model
#pragma surface surf Standard addshadow fullforwardshadows
#pragma multi_compile_instancing
#pragma instancing_options procedural:setup
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
StructuredBuffer<float4> positionBuffer;
#endif
void rotate2D(inout float2 v, float r)
{
float s, c;
sincos(r, s, c);
v = float2(v.x * c - v.y * s, v.x * s + v.y * c);
}
void setup()
{
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
float4 data = positionBuffer[unity_InstanceID];
float rotation = data.w * data.w * _Time.y * 0.5f;
rotate2D(data.xz, rotation);
unity_ObjectToWorld._11_21_31_41 = float4(data.w, 0, 0, 0);
unity_ObjectToWorld._12_22_32_42 = float4(0, data.w, 0, 0);
unity_ObjectToWorld._13_23_33_43 = float4(0, 0, data.w, 0);
unity_ObjectToWorld._14_24_34_44 = float4(data.xyz, 1);
unity_WorldToObject = unity_ObjectToWorld;
unity_WorldToObject._14_24_34 *= -1;
unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
#endif
}
half _Glossiness;
half _Metallic;
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment