由于工作原因,好久没写博客了,后续慢慢补上这段时间的打杂学习到的内容
之前在 OpenGL 上实现了 GPU Instancing
在 Unity 也是差不多的,只不过 Unity 对 ShaderLab 和 应用层 C++ 封装了黑箱处理
但是原理是差不多的:在 GPU 建立一块缓存,用于存放不同实例的不同属性,便于绘制时的差异效果
那么今天就搬一下砖
Shader
// author : jave.lin
// Unity 中 实现 Instancing 的 Shader
Shader "Game/CoinInstancingShader"
{
Properties
{
[NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#define SELF_ROT // 测试自转
#define TINT_COLOR // 测试着色
#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
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
sampler2D _MainTex;
// SRP 中,可以使用一个 UnityPerMaterial 再裹一层
// 总之这些潜规则要熟悉 Unity 才能知道
// - https://forum.unity.com/threads/materialpropertyblock-with-shader-graph.697868/
// CBUFFER_START(UnityPerMaterial)
UNITY_INSTANCING_BUFFER_START(MyInstancing)
UNITY_DEFINE_INSTANCED_PROP(float4x4, _MMat)
#ifdef TINT_COLOR
UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
#endif
#ifdef SELF_ROT
UNITY_DEFINE_INSTANCED_PROP(float, _AngleOffset)
#endif
UNITY_INSTANCING_BUFFER_END(MyInstancing)
// CBUFFER_END
// unity 的 instancing 参考下面的连接内容
// jave.lin : refer to :
// - https://docs.unity3d.com/Manual/GPUInstancing.html
// UNITY_INSTANCING_BUFFER_START(MyInstancing)
// UNITY_DEFINE_INSTANCED_PROP(float4x4, _MMat)
// UNITY_DEFINE_INSTANCED_PROP(half4, _Color)
// UNITY_INSTANCING_BUFFER_END(MyInstancing)
// 这里只是功能演示,一般尽量不在 VS, FS 中构建矩阵
// 想这个自旋转的矩阵,完全可以合并在 Model Matrix 中来处理
// 下面只是做测试
// 可参考我之前写的软渲染器中的:GenYRotateMat 函数
// https://github.com/javelinlin/3DSoftRenderer/blob/master/SoftRenderer/RendererCoreCommon/Renderer/Common/Mathes/Mathes.cs
#ifdef SELF_ROT
float4x4 GetYRotMat(float y, float offset)
{
float angle = y + offset;
float c = cos(angle);
float s = sin(angle);
return float4x4(
float4( c, 0, s, 0),
float4( 0, 1, 0, 0),
float4(-s, 0, c, 0),
float4( 0, 0, 0, 1)
);
}
#endif
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v)
UNITY_TRANSFER_INSTANCE_ID(v, o);
float4x4 mMat = UNITY_ACCESS_INSTANCED_PROP(MyInstancing, _MMat);
#ifdef SELF_ROT
float angle = UNITY_ACCESS_INSTANCED_PROP(MyInstancing, _AngleOffset);
mMat = mul(mMat, GetYRotMat(_Time.w, angle));
#endif
float4 vertex = mul(mMat, v.vertex);
o.vertex = mul(UNITY_MATRIX_VP, vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
#ifdef TINT_COLOR
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb = UNITY_ACCESS_INSTANCED_PROP(MyInstancing, _Color).rgb * col;
return col;
#else
return tex2D(_MainTex, i.uv);
#endif
}
ENDCG
}
}
}
CSharp 脚本
using UnityEngine;
[ExecuteInEditMode]
public class InstancedScript : MonoBehaviour
{
private static int _MMat = Shader.PropertyToID("_MMat");
private static int _Color = Shader.PropertyToID("_Color");
private static int _AngleOffset = Shader.PropertyToID("_AngleOffset");
private static MaterialPropertyBlock mpb;
public Color col;
public float angle_offset;
private MeshRenderer mesh_renderer;
private void Start()
{
mesh_renderer = GetComponent<MeshRenderer>();
if (mpb == null) mpb = new MaterialPropertyBlock();
}
private void Update()
{
mpb.SetMatrix(_MMat, transform.localToWorldMatrix);
mpb.SetColor(_Color, col);
mpb.SetFloat(_AngleOffset, angle_offset);
mesh_renderer.SetPropertyBlock(mpb);
}
}
材质上勾上:Enable GPU Instanced
运行效果
Project
backup : GPU_Instancing_DEMO
目前上面的方式不是unity比较好的方式,后续有空再搬一砖:在CSharp调用API绘制
加班太狠,生活琐事多,学习计划都打乱了