오브젝트 별 재질 속성으로 작동하는 드로콜을 통합하는 또 다른 방법이 있습니다.
GPU 인스텅싱이라고 하며 한번에 동일한 메시를 사용하는 여러 개체에 대해 단일 그리기호출을 실행하여 작동합니다.
CPU는 모든 객체 별 변환 및 재질 속성을 수집하여 GPU로 보내는 배열에 넣습니다. 그런다음 GPU는 모든 항목을 반복하고 제공된순서대로 렌더링 합니다.
첫번째 단계로는 아래의 문구를 추가하고
#pragma multi_compile_instancing
아래처럼 UnityInstancing.hlsl 를 추가하여접근방식을 변경해야 합니다.
UNITY_MATRIX_M 및 기타 매크로를 정의한 후 포함하기 전에 수행해야 합니다.
#define UNITY_MATRIX_P glstate_matrix_projection
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
아래와 같이 INSTANCE_ID를 추가합니다.
struct Attributes {
float3 positionOS : POSITION;
//GPU인스턴싱을 사용하는 경우 격체 인덱스는 정점 속성으로 사용 가능
UNITY_VERTEX_INPUT_INSTANCE_ID
};
float4 UnlitPassVertex (Attributes input) : SV_POSITION {
//인덱스를 추출하여 다른 인스턴스 매크로가 의존하는 정적변수에 저장됨.
UNITY_SETUP_INSTANCE_ID(input);
float3 positionWS = TransformObjectToWorld(input.positionOS);
return TransformWorldToHClip(positionWS);
}
이렇게만 해도 동작은 하지만 SRPBatcher가 우선적으로 처리되면서 결과를 얻지 못할 수 있습니다.
따라서 Instance를 참조하는 방식으로 교체해야 합니다.
더보기
Shader "StudySRP/Catlike1"
{
Properties{
_BaseColor("Color", Color) = (1.0, 1.0, 1.0, 1.0)
}
SubShader{
Pass {
HLSLPROGRAM
#include "Catlike1Pass.hlsl"
#include "UnityInput.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#define UNITY_MATRIX_M unity_ObjectToWorld
#define UNITY_MATRIX_I_M unity_WorldToObject
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_MATRIX_P glstate_matrix_projection
#ifndef CUSTOM_UNITY_INPUT_INCLUDED
#define CUSTOM_UNITY_INPUT_INCLUDED
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
float4 unity_LODFade;
real4 unity_WorldTransformParams;
CBUFFER_END
float4x4 unity_MatrixVP;
float4x4 unity_MatrixV;
float4x4 glstate_matrix_projection;
#endif
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl" //GPU인스턴싱 한다고 선언
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial) //bacher에 먹히지 않기위하여 Instance Buffer로 변환하기.
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor) //인스턴트별 재료 데이터를 지원하려면 배열 참조로 교체해야함.
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
#pragma multi_compile_instancing //배열을 통해 데이터를 제공해야 하므로 지시문 추가
#pragma vertex vert
#pragma fragment frag
#ifndef CUSTOM_UNLIT_PASS_INCLUDED
#define CUSTOM_UNLIT_PASS_INCLUDED
#endif
struct Attributes {
float3 positionOS : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID // 아이디부여
};
struct Varyings {
float4 positionCS : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID // 아이디부여
};
Varyings vert(Attributes input) {
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);//아이디할당.
UNITY_TRANSFER_INSTANCE_ID(input, output); //위치와 인덱스를 모두 출력하는 구조체 이를통해엑세스
float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
output.positionCS = TransformWorldToHClip(positionWS);
return output;
}
float4 frag(Varyings input) : SV_TARGET{
UNITY_SETUP_INSTANCE_ID(input);
return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor); // 재질 속성은 이를 통하여 엑세스함.
}
ENDHLSL
}
}
}
인스턴트화된 메시 그리기 |
너무 많은 오브젝트를 손으로 편집하는것은 힘드니 아래와 같이 인스턴스를 생성할 수 있습니다.
using UnityEngine;
public class MeshBall : MonoBehaviour {
static int baseColorId = Shader.PropertyToID("_BaseColor");
[SerializeField]
Mesh mesh = default;
[SerializeField]
Material material = default;
Matrix4x4[] matrices = new Matrix4x4[1023];
Vector4[] baseColors = new Vector4[1023];
MaterialPropertyBlock block;
void Awake() {
for (int i = 0; i < matrices.Length; i++) {
matrices[i] = Matrix4x4.TRS(
Random.insideUnitSphere * 10f, Quaternion.identity, Vector3.one
);
baseColors[i] =
new Vector4(Random.value, Random.value, Random.value, 1f);
}
}
void Update() {
if (block == null) {
block = new MaterialPropertyBlock();
block.SetVectorArray(baseColorId, baseColors);
}
Graphics.DrawMeshInstanced(mesh, 0, material, matrices, 1023, block);
}
}
반응형