mesh | 要绘制的 Mesh。 |
submeshIndex | 要绘制网格的哪个子集。这只适用于由若干种材质构成的网格。 |
material | 要使用的 Material。 |
bounds | 围绕要绘制的实例的包围体。 |
bufferWithArgs | GPU 缓冲区包含相应的参数,指示要绘制此网格的实例数。 |
argsOffset | 缓冲区中的字节偏移,绘制参数起始位置。 |
properties | 要应用的其他材质属性。请参阅 MaterialPropertyBlock。 |
castShadows | 网格是否应投射阴影? |
receiveShadows | 网格是否应接受阴影? |
layer | 要使用的 Layer。 |
camera | 如果为 /null/(默认值),将在所有摄像机中绘制网格。否则,仅在给定摄像机中绘制网格。 |
使用 GPU 实例化多次绘制同一网格。
类似于 Graphics.DrawMeshInstanced,此函数用于绘制同一网格的多个实例,但与之不同的是,此函数中代表要绘制实例数的参数来自 /bufferWithArgs/。
如果您需要使用实例化的着色器多次绘制同一网格,则可以使用该函数。这些网格不会被视锥体或烘焙遮挡物做进一步的剔除处理,也不进行排序以提高透明度或 Z 效率。
带参数的缓冲区 bufferWithArgs
必须在给定的 argsOffset
偏移处具有五个整数:
每个实例的索引数、实例数、起始索引位置、基顶点位置、起始实例位置。
下面是一个可用于绘制同一网格的多个实例的脚本:
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour {
public int instanceCount = 100000;
public Mesh instanceMesh;
public Material instanceMaterial;
private int cachedInstanceCount = -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)
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, 0, 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() {
// 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
uint numIndices = (instanceMesh != null) ? (uint)instanceMesh.GetIndexCount(0) : 0;
args[0] = numIndices;
args[1] = (uint)instanceCount;
argsBuffer.SetData(args);
cachedInstanceCount = instanceCount;
}
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
}
}
}