Graphics.DrawMeshInstancedIndirect
Declaration
public static void DrawMeshInstancedIndirect(Mesh mesh, int submeshIndex, Material material, Bounds bounds, ComputeBuffer bufferWithArgs, int argsOffset = 0, MaterialPropertyBlock properties = null, Rendering.ShadowCastingMode castShadows = ShadowCastingMode.On, bool receiveShadows = true, int layer = 0, Camera camera = null, Rendering.LightProbeUsage lightProbeUsage = LightProbeUsage.BlendProbes, LightProbeProxyVolume lightProbeProxyVolume = null);public static void DrawMeshInstancedIndirect(Mesh mesh, int submeshIndex, Material material, Bounds bounds, GraphicsBuffer bufferWithArgs, int argsOffset = 0, MaterialPropertyBlock properties = null, Rendering.ShadowCastingMode castShadows = ShadowCastingMode.On, bool receiveShadows = true, int layer = 0, Camera camera = null, Rendering.LightProbeUsage lightProbeUsage = LightProbeUsage.BlendProbes, LightProbeProxyVolume lightProbeProxyVolume = null);Параметры
| Параметр | Описание |
|---|---|
| сетка | Mesh для рисования. |
| submeshIndex | Какое подмножество меши рисовать. Это относится только к сетям, которые состоят из нескольких материалов. |
| материал | Material использовать. |
| границы | Ограничительный том, окружающий экземпляры, которые вы собираетесь нарисовать. |
| bufferWithArgs | Буфер GPU, содержащий аргументы для определения количества экземпляров сетки, которые должны быть нарисованы. |
| argsOffset | Смещение в байтах, по которому в буфере начинаются аргументы отрисовки. |
| свойства | Дополнительные свойства материала, которые должны применяться. См. MaterialPropertyBlock. |
| castShadows | Определяет, может ли сетка создавать тени. |
| receiveShadows | Определяет, может ли сетка получать тени. |
| слой | Слой для использования. |
| камера | Если null (по умолчанию), сетка будет рисоваться во всех камерах. В противном случае она будет рисоваться только в заданной Camera. |
| lightProbeUsage | LightProbeUsage для экземпляров. |
Описание
Эта функция теперь устарела. Используйте Graphics.RenderMeshIndirect вместо этого. Рисует одну и ту же сетку несколько раз с помощью GPU instancing.
Эта функция работает только на платформах, поддерживающих вычислительные шейдеры.
Подобно Graphics.DrawMeshInstanced, эта функция рисует много экземпляров одной и той же меши, но в отличие от этого метода, аргументы для определения количества экземпляров для рисования поступают из bufferWithArgs.
Используйте эту функцию в ситуациях, когда вы хотите нарисовать одну и ту же меш определенное количество раз с помощью инстанционированного шейдера. Меши далее не вычеркиваются view frustum или печеными окклюдерами, не сортируются по прозрачности или z-эффективности.
Буфер с аргументами, bufferWithArgs, должен иметь пять целых чисел на заданной argsOffset смещении: число индексов на экземпляр, число экземпляров, место индекса начала, место вершины базы, место экземпляра начала.
Unity требует аргумент submeshIndex только в том случае, если подсетки внутри меши имеют разные топологии (e.g. Треугольники и Линии). В противном случае вся информация о том, какую подсетку рисовать, поступает из аргумента bufferWithArgs.
Вот скрипт, который может быть использован для рисования многих экземпляров одной и той же меши:
using UnityEngine; using System.Collections;
public class ExampleClass : MonoBehaviour { public int instanceCount = 100000; 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; } }
Вот шейдер поверхности, который может быть использован с примерным скриптом выше:
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"
}
Вот пользовательский шейдер, который может быть использован с примером скрипта выше:
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
}
}
}