Unity gpu instancing
unity可以自动合并相同的material对象渲染
将对应shader enable instancing的选项勾上
本文说明一下直接调用unity api的方式,实现合并渲染
参考opengl gl_Instance
unity提供了一组api,向gpu批量提交渲染数据
以其中的一个为例Graphics.DrawMeshInstanced
参数为网格数据,shader,以及每个instance的变换矩阵
Material.SetBuffer可以向shader传递数组,在shader可以根据unity_InstanceID相访问对应的实例数据
本文的例子是在urp下实现的,使用了urp中shader的api
运行效果如下:
?
?
shader代码:
Shader "lsc/gpu_instancing"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
//启用gpu instancing
#pragma multi_compile_instancing
//开启主灯光的阴影
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _SHADOWS_SOFT
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
//自定义数组,保存每个实例的颜色
StructuredBuffer<float4> _instancing_color;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD2;
float4 positionCS : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
//给unity_InstanceID赋值,使urp的内部函数会自动调用unity_Builtins0数组中的属性
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
//内部会自动取实例的变幻矩阵,在不同的位置画出实例
VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
o.positionCS = vertexInput.positionCS;
o.positionWS = vertexInput.positionWS;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
half4 frag (v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
half4 col = tex2D(_MainTex, i.uv);
#ifdef UNITY_INSTANCING_ENABLED
col.rgb = _instancing_color[unity_InstanceID];
#else
col.rgb = half4(0, 1, 0, 1);
#endif
//计算主光与阴影
float4 shadow_coord = TransformWorldToShadowCoord(i.positionWS);
Light mainLight = GetMainLight(shadow_coord);
half3 light_col = mainLight.color * mainLight.shadowAttenuation;
col.rgb = light_col * col.rgb;
return col;
}
ENDHLSL
}
Pass //产生阴影
{
Name "ShadowCaster"
Tags{"LightMode" = "ShadowCaster"}
ZWrite On
ZTest LEqual
ColorMask 0
Cull[_Cull]
HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5
// -------------------------------------
// Material Keywords
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
ENDHLSL
}
Pass //写入深度
{
Name "DepthOnly"
Tags{"LightMode" = "DepthOnly"}
ZWrite On
ColorMask 0
Cull[_Cull]
HLSLPROGRAM
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5
#pragma vertex DepthOnlyVertex
#pragma fragment DepthOnlyFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
ENDHLSL
}
}
}
对应的cs代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;
//此脚本挂在任意物体(空物体也可)
public class gpu_instancing : MonoBehaviour
{
public int gobj_line_count_ = 20;
public int gobj_count_perline_ = 20;
//网格数据
public Mesh instance_mesh_;
//gpu instancing shader
public Material mtrl_;
ComputeBuffer cb_color_;
List<float4> lst_color_;
void Start()
{
cb_color_ = new ComputeBuffer(gobj_line_count_ * gobj_count_perline_, sizeof(float) * 4);
lst_color_ = new List<float4>();
}
void Update()
{
//绘制gpu instancing 100个胶囊
lst_color_.Clear();
Vector3 pos = this.transform.position;
var matrices = new Matrix4x4[gobj_line_count_ * gobj_count_perline_];
for (int i = 0; i < gobj_line_count_; i++)
{
for (int j = 0; j < gobj_count_perline_; j++)
{
Vector3 tmppos = pos;
tmppos.z = pos.z + i;
tmppos.x = pos.x + j;
var scale = new Vector3(0.5f, 0.5f, 0.5f);
var matrix = Matrix4x4.TRS(tmppos, Quaternion.identity, scale);
matrices[i * gobj_count_perline_ + j] = matrix;
//每个实例颜色赋值
lst_color_.Add(new float4((float)i/ gobj_line_count_, (float)j / gobj_count_perline_, 0.0f, 1.0f));
}
}
cb_color_.SetData(lst_color_);
//每个实例的颜色数组
mtrl_.SetBuffer(Shader.PropertyToID("_instancing_color"), cb_color_);
if (mtrl_)
Graphics.DrawMeshInstanced(instance_mesh_, 0, mtrl_, matrices, gobj_line_count_ * gobj_count_perline_);//materices是变换矩阵
}
}
|