Unity3D研究院GPU Instancing测试
GPU instancing 很早就支持手机了(Android只支持Opengl ES 3.0),但是我一直不知道将它应用到哪里,刚好最近在调研这个我对它又重新测试了一下。
如果是不动的物体勾选static静态合并批次(40-50帧率)
自定义Shader中勾选Enable GPU Instancing
帧率竟然还不如静态合批次(帧率 30-40)
自定义Shader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
Shader "SimplestInstancedShader1" { Properties { _Color ("Color", Color) = (1, 1, 1, 1) _MainTex ("Texture", 2D) = "white" {}
}
SubShader { Tags { "RenderType"="Opaque" } LOD 100
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #include "UnityCG.cginc"
struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID };
struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID // necessary only if you want to access instanced properties in fragment Shader. };
UNITY_INSTANCING_BUFFER_START(Props) UNITY_DEFINE_INSTANCED_PROP(float4, _Color) UNITY_INSTANCING_BUFFER_END(Props)
sampler2D _MainTex; float4 _MainTex_ST;
v2f vert(appdata v) { v2f o;
UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader.
o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o; }
fixed4 frag(v2f i) : SV_Target { UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
return tex2D (_MainTex, i.uv) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color); } ENDCG } } } |
所以,静态不动的物体就没必要用上面的方法了,于是我又测试了Graphics.DrawMeshInstanced()方法,终于满意了。(稳定60帧)
Graphics.DrawMeshInstanced()方法不需要游戏对象以及游戏组件的额外开销,在Update()方法中一气呵成,不过它也有限制,最多可以画1023个。
还有个方法是Graphics.DrawMeshInstancedIndirect()它没有画多少的限制,而且更加灵活,我也搞了好一会儿才在游戏中跑起来,后来才知道它不能再手机上用,只有PC上可以.
在shader中接收位置 颜色 矩阵
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
Shader "Instanced/InstancedIndirectSelection" {
Properties { _MainTex ("Texture", 2D) = "white" {} }
SubShader { Tags{ "RenderType" = "Opaque" } LOD 100
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 4.5
#include "UnityCG.cginc"
StructuredBuffer<float4> positionBuffer; StructuredBuffer<float4> colorBuffer; StructuredBuffer<float4x4> matrix4x4Buffer;
struct appdata { fixed4 color : COLOR; float4 vertex : POSITION; float4 texcoord : TEXCOORD0; };
struct v2f { float4 color: COLOR; float4 vertex : SV_POSITION; float2 texcoord : TEXCOORD0; };
sampler2D _MainTex; float4 _MainTex_ST;
v2f vert(appdata v, uint instanceID : SV_InstanceID) { float4 data = positionBuffer[instanceID]; float4x4 materix = matrix4x4Buffer[instanceID / matrix4x4Buffer.Length]; float3 worldPosition = data.xyz + mul(materix,v.vertex.xyz * data.w); v2f o; o.vertex = mul(UNITY_MATRIX_VP, float4(worldPosition, 1.0f)); o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); o.color = colorBuffer[instanceID]; return o; }
fixed4 frag(v2f i) : SV_Target { return tex2D (_MainTex, i.texcoord) * i.color;
}
ENDCG } } } |
在代码中将位置 颜色 矩阵传进去就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
using UnityEngine; public class InstancedIndirectExample : MonoBehaviour { public int instanceCount = 100000; public Mesh instanceMesh; public Material instanceMaterial; private ComputeBuffer positionBuffer; private ComputeBuffer argsBuffer; private ComputeBuffer colorBuffer; private ComputeBuffer matrix4x4Buffer;
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() { // instanceMaterial.SetBuffer("positionBuffer", positionBuffer); Graphics.DrawMeshInstancedIndirect(instanceMesh, 0, instanceMaterial, new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f)), argsBuffer); } void UpdateBuffers() { if ( instanceCount < 1 ) instanceCount = 1;
// Positions & Colors if (positionBuffer != null) positionBuffer.Release(); if (colorBuffer != null) colorBuffer.Release(); if (matrix4x4Buffer != null) matrix4x4Buffer.Release();
positionBuffer = new ComputeBuffer(instanceCount, 16); colorBuffer = new ComputeBuffer(instanceCount, 4*4); matrix4x4Buffer = new ComputeBuffer(instanceCount,16);
Vector4[] positions = new Vector4[instanceCount]; Vector4[] colors = new Vector4[instanceCount]; Matrix4x4[] materix4X4 = new Matrix4x4[instanceCount];
for (int i=0; i < instanceCount; i++) { positions [i] = new Vector4 (i, 0f, 0f, 1f); colors[i] = new Vector4( 1f, 1f, 1f, 1f ); materix4X4[i] = Matrix4x4.TRS (positions [i], Quaternion.Euler (0F, 0F, 0F), Vector3.one); }
positionBuffer.SetData(positions); colorBuffer.SetData(colors); matrix4x4Buffer.SetData (materix4X4);
instanceMaterial.SetBuffer("positionBuffer", positionBuffer); instanceMaterial.SetBuffer("colorBuffer", colorBuffer); instanceMaterial.SetBuffer("matrix4x4Buffer", matrix4x4Buffer);
// indirect args uint numIndices = (instanceMesh != null) ? (uint)instanceMesh.GetIndexCount(0) : 0; args[0] = numIndices; args[1] = (uint)instanceCount; argsBuffer.SetData(args); }
void OnDisable() { if (positionBuffer != null) positionBuffer.Release(); positionBuffer = null;
if (colorBuffer != null) colorBuffer.Release(); colorBuffer = null;
if (argsBuffer != null) argsBuffer.Release(); argsBuffer = null;
if (matrix4x4Buffer != null) matrix4x4Buffer.Release(); matrix4x4Buffer = null;
} } |
如果是发生移动的物体顶点数量在900以内会动态合并批次,如果需要支持更多的顶点GPU Instaning的优势就更明显了。 目前来看草、石头、植被、比较合适用它。
- 本文固定链接: https://www.xuanyusong.com/archives/4488
- 转载请注明: 雨松MOMO 2018年09月03日 于 雨松MOMO程序研究院 发表