카테고리 없음

유니티_HLSL_GPU인스턴싱

zelkova 2021. 4. 22. 10:36

<목차로 돌아가기>

 

 

오브젝트 별 재질 속성으로 작동하는 드로콜을 통합하는 또 다른 방법이 있습니다.

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);
	}
}

 

 

반응형