Last active
March 2, 2021 22:00
-
-
Save MattRix/1535a026eb8de0e32ee79d9612178fb2 to your computer and use it in GitHub Desktop.
Instanced rendering example from https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshInstancedIndirect.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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