많은 수의 인스턴스를 효율적으로 렌더링하기 위해 BRG는 DOTS 인스턴싱이라는 새로운 셰이더 인스턴싱 모드를 사용합니다.BRG가 사용하는 모든 셰이더는 DOTS 인스턴싱을 지원해야 합니다.기존의 인스턴스화 셰이더에서 셰이더는 각 인스턴스화 프로퍼티에 대한 배열이 상수 또는 균일 버퍼에 전달하므로 각 배열의 각 요소는 드로우의 단일 인스턴스에 대한 프로퍼티 값을 포함합니다.DOTS 인스턴스화 셰이더에서 Unity는 각 DOTS 인스턴스화 프로퍼티에 대해 하나의 32비트 정수를 셰이더에 전달합니다.이 32비트 정수를 메타데이터 값이라고 합니다.이 정수는 원하는 모든 것을 나타낼 수 있지만, 일반적으로 셰이더가 렌더링 중인 인스턴스에 대한 프로퍼티 데이터를 로드하는 버퍼의 오프셋을 나타냅니다.
DOTS 인스턴싱에는 다음과 같이 기존 인스턴싱에 비해 많은 장점이 있습니다.
DOTS 인스턴싱을 지원하려면 셰이더가 다음과 같아야 합니다.
#pragma target 4.5
이상을 지정해야 합니다.DOTS_INSTANCING_ON
키워드를 지원해야 합니다.#pragma multi_compile _ DOTS_INSTANCING_ON
으로 이를 선언해야 합니다.참고:Unity가 URP와 HDRP에서 제공하는 셰이더 그래프와 셰이더는 DOTS 인스턴싱을 지원합니다.
트랜스폼 매트릭스와 같은 인스턴스 데이터를 로드하려면 셰이더가 DOTS 인스턴스화 프로퍼티를 정의해야 합니다.다음은 간단한 DOTS 인스턴스화 프로퍼티 블록 예시입니다.
UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
UNITY_DOTS_INSTANCED_PROP(float4, Color)
UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)
프로퍼티 블록의 시작과 끝을 표시하려면 블록 이름 앞에 UNITY_DOTS_INSTANCING_START
와 UNITY_DOTS_INSTANCING_END
매크로를 사용합니다.예시에서는 이름을 MaterialPropertyMetadata
로 사용했습니다.블록 이름에는 다음 세 가지가 허용됩니다.
셰이더는 각각 하나씩 선언할 수 있으므로 DOTS 인스턴스화 셰이더는 이러한 블록을 03개 가질 수 있습니다.Unity가 정의한 셰이더 코드는 UserPropertyMetadata를 사용하지 않으므로 이 이름은 자유롭게 사용할 수 있습니다.URP와 HDRP는 제공하는 모든 셰이더에 BuiltinPropertyMetadata를 정의하고 대부분의 셰이더에는 MaterialPropertyMetadata를 정의하므로 UserPropertyMetadata를 사용하는 것이 가장 좋습니다.커스텀 셰이더에는 세 가지 이름을 모두 사용할 수 있으며, 한 번에 모두 사용할 수도 있습니다.
블록에는 다음과 같은 형식의 DOTS 인스턴스화 프로퍼티 정의를 얼마든지 포함할 수 있습니다.
UNITY_DOTS_INSTANCED_PROP(PropertyType, PropertyName)
PropertyType
은 부울 벡터를 제외한 어느 HLSL 빌트인 타입(예: unit, float4, float4x4, int2x4)이든 될 수 있으며, PropertyName
은 DOTS 인스턴스화 프로퍼티의 이름입니다.DOTS 인스턴스화 프로퍼티는 일반 머티리얼 프로퍼티와는 완전히 별개이며, 다른 일반 머티리얼 프로퍼티와 동일한 이름을 지정할 수 있습니다.이는 UNITY_DOTS_INSTANCED_PROP
매크로가 Unity에서 다른 프로퍼티 이름과 상충하지 않는다고 인식하는 특별한 상수 이름을 생성하기 때문에 가능합니다.Unity가 제공하는 셰이더는 DOTS 인스턴스화 프로퍼티에 일반 머티리얼 프로퍼티와 동일한 이름을 부여하지만, 이 규칙을 따를 필요는 없습니다.
Unity는 내부적으로 셰이더가 선언하는 모든 DOTS 인스턴스화 프로퍼티에 대해 32비트 정수 메타데이터 값을 셰이더에 제공합니다.Unity는 코드가 BatchRendererGroup.AddBatch를 호출하여 드로우와 관련된 배치를 생성할 때 메타데이터 값을 설정합니다.Unity가 메타데이터 값을 설정하지 않은 경우 메타데이터의 기본값은 0
입니다.또한 셰이더는 BatchRendererGroup.AddBatch
에 인자로 전달한 GraphicsBuffer에 Unity가 설정하는 ByteAddressBuffer unity_DOTSInstanceData
에도 액세스할 수 있습니다.이 버퍼는 일반적으로 셰이더가 인스턴스 데이터를 로드하는 위치입니다.여러 배치가 하나의 GraphicsBuffer를 공유할 수 있지만, 각 배치는 unity_DOTSInstanceData
에 대해 별도의 GraphicsBuffer를 사용할 수도 있습니다.
참고:Unity는 DOTS 인스턴스화 데이터를 자동으로 제공하지 않습니다.각 배치의 unity_DOTSInstanceData
버퍼에 올바른 데이터가 포함되어 있는지 확인하는 것은 사용자의 책임입니다.인스턴스 데이터에는 트랜스폼 매트릭스, 라이트 프로브 계수, 라이트맵 텍스처 좌표와 같이 일반적으로 Unity가 게임 오브젝트용으로 제공하는 여러 프로퍼티가 포함되어 있어야 합니다.
DOTS 인스턴스화 프로퍼티에 액세스하기 위해 셰이더가 Unity에서 제공하는 액세스 매크로 중 하나를 사용할 수 있습니다.액세스 매크로는 unity_DOTSInstanceData
내의 인스턴스 데이터가 다음 레이아웃을 사용한다고 가정합니다.
unity_DOTSInstanceData
버퍼 내 배치에 있는 첫 번째 인스턴스의 바이트 주소를 포함합니다.0
인 경우 모든 인스턴스는 인스턴스 인덱스 0의 값을 사용합니다.즉, 각 인스턴스는 메타데이터 값의 바이트 주소에서 바로 로드됩니다.이 경우 버퍼는 인스턴스당 하나의 값 대신 단일 값만 저장하면 됩니다.1
인 경우 주소에는 AddressOfInstance0 + sizeof(PropertyType) * instanceID
를 사용하여 인스턴스 인덱스 instanceID
의 값을 찾을 수 있는 배열이 포함되어야 합니다.이 경우 렌더링된 모든 인스턴스 인덱스가 버퍼에 유효한 데이터를 포함했는지 확인해야 합니다.그렇지 않으면 범위를 벗어난 액세스와 정의되지 않은 동작이 발생할 수 있습니다.또한 텍스처와 같이 위의 레이아웃을 사용하지 않는 커스텀 데이터 소스를 사용하려는 경우 유용한 메타데이터 값을 직접 설정할 수도 있습니다.
Unity는 다음과 같은 액세스 매크로를 제공합니다.
액세스 매크로 | 설명 |
---|---|
UNITY_ACCESS_DOTS_INSTANCED_PROP(PropertyType, PropertyName) |
위에 설명한 레이아웃을 사용하여 unity_DOTSInstanceData 에서 로드된 값을 반환합니다.Unity가 제공하는 셰이더는 되돌아갈 기본값이 없는 DOTS 인스턴스화 빌트인 프로퍼티에 이 버전을 사용합니다. |
UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(PropertyType, PropertyName) |
메타데이터 값의 최상위 비트가 0인 경우 기본값을 반환한다는 점을 제외하면 UNITY_ACCESS_DOTS_INSTANCED_PROP 과 동일하게 반환합니다.기본값은 DOTS 인스턴스화 프로퍼티와 동일한 이름을 가진 일반 머티리얼 프로퍼티의 값이며, 따라서 Unity가 제공하는 셰이더는 DOTS 인스턴스화 프로퍼티가 일반 머티리얼 프로퍼티와 동일한 이름을 갖는다는 규칙을 사용합니다.기본값을 사용하면 액세스 매크로는 unity_DOTSInstanceData 에 전혀 액세스하지 않습니다.Unity가 제공하는 셰이더는 DOTS 인스턴스화 머티리얼 프로퍼티에 이 액세스 매크로를 사용하므로 로드는 머티리얼에 설정된 값으로 되돌아갑니다. |
UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_CUSTOM_DEFAULT(PropertyType, PropertyName, DefaultValue) |
메타데이터 값의 최상위 비트가 0이 아니면 UNITY_ACCESS_DOTS_INSTANCED_PROP 과 동일하게 반환합니다. 메타데이터 값의 최상위 비트가 0인 경우 이 매크로는 DefaultValue 를 대신 반환하며 unity_DOTSInstanceData 에 액세스하지 않습니다. |
UNITY_DOTS_INSTANCED_METADATA_NAME(PropertyType, PropertyName) |
어디에도 액세스하지 않고 메타데이터 값을 직접 반환합니다.이는 커스텀 인스턴스 데이터 로딩 체계에 유용합니다. |
이러한 매크로를 사용하는 방법에 대한 예시는 매크로 액세스 예시를 참조하십시오.
Unity는 드로우 커맨드 데이터에서 상수 값을 직접 로드하는 셰이더 함수를 액세스 매크로와 함께 제공합니다.Unity가 제공하는 셰이더는 이러한 함수를 사용합니다.
Unity는 다음과 같은 셰이더 함수를 제공합니다.
셰이더 함수 | 설명 |
---|---|
LoadDOTSInstancedData_RenderingLayer |
드로우 커맨드에 대해 renderingLayerMask를 반환합니다. |
LoadDOTSInstancedData_MotionVectorsParams |
드로우 커맨드에 대해 모션 벡터 생성 모드를 반환합니다.이는 Unity 셰이더가 기대하는 float4 형식으로 지정됩니다. |
LoadDOTSInstancedData_WorldTransformParams |
뒤집힌 삼각형 와인딩으로 인스턴스를 그릴지 여부를 반환합니다.FlipWinding을 참조하십시오. |
LoadDOTSInstancedData_LightData |
인스턴스에 대한 씬의 주요 방향 광원 활성화 여부를 반환합니다.주요 광원은 여러 이유(예: 광원이 라이트맵에 이미 포함된 경우)로 비활성화될 수 있습니다. |
LoadDOTSInstancedData_LODFade |
LODCrossFade flag가 설정된 경우 사용자가 설정한 8비트 크로스페이드 값을 반환합니다.플래그가 설정되지 않은 경우 반환 값은 정의되지 않습니다. |
이 섹션에는 Unity가 제공하는 액세스 매크로 예시와 이러한 매크로를 사용하여 인스턴스당 데이터 및 상수 데이터에 액세스하는 방법에 대한 지침이 포함되어 있습니다.
이 예제에서 다음을 참고하십시오.
Color
의 메타데이터 값은 0x80001000
입니다5
입니다.최상위 비트가 이미 설정되어 있으므로 액세서 매크로는 기본값을 로드하지 않습니다.즉, c0
, c1
, c2
는 모두 unity_DOTSInstanceData
주소 0x1050
에서 로드된 동일한 값을 가집니다.
void ExamplePerInstance()
{
// rawMetadataValue will contain 0x80001000
uint rawMetadataValue = UNITY_DOTS_INSTANCED_METADATA_NAME(float4, Color);
float4 c0 = UNITY_ACCESS_DOTS_INSTANCED_PROP(float4, Color);
float4 c1 = UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float4, Color);
float4 c2 = UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_CUSTOM_DEFAULT(float4, Color, float4(1, 2, 3, 4));
}
이 예제에서 다음을 참고하십시오.
Color
의 메타데이터 값은 0x00001000
입니다5
입니다.최상위 비트가 설정되지 않았으므로 기본값으로 되돌아가는 액세서 매크로는 unity_DOTSInstanceData
에 액세스하지 않습니다.이는 다음을 의미합니다.
c0
는 unity_DOTSInstanceData
주소 0x1000
의 값을 포함합니다.c1
은 일반 머티리얼 프로퍼티 Color의 값을 포함하며, Color 프로퍼티가 없는 경우 컴파일 오류가 발생합니다.c2
는 명시적 기본값으로 전달되었으므로 (1, 2, 3, 4)
를 포함합니다.void ExampleConstant()
{
// rawMetadataValue will contain 0x00001000
uint rawMetadataValue = UNITY_DOTS_INSTANCED_METADATA_NAME(float4, Color);
float4 c0 = UNITY_ACCESS_DOTS_INSTANCED_PROP(float4, Color);
float4 c1 = UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float4, Color);
float4 c2 = UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_CUSTOM_DEFAULT(float4, Color, float4(1, 2, 3, 4));
}
모든 unity_DOTSInstanceData
버퍼의 첫 64바이트를 0으로 초기화하고 사용하지 않는 것이 가장 좋습니다.이는 배치 생성 시 지정되지 않은 모든 메타데이터 값에 대해 Unity가 사용하는 기본 메타데이터 값이 0이기 때문입니다.특히 셰이더가 UNITY_ACCESS_DOTS_INSTANCED_PROP
매크로에서 메타데이터 값 0을 로드할 때 인스턴스 인덱스가 무시되므로 셰이더는 이 값을 주소 zero
에서 로드합니다.가장 큰 값 타입(float4x4 매트릭스)의 크기인 첫 64바이트가 0이인지 확인하면 이러한 로드에서 예측한 대로 0의 결과가 반환됩니다.그렇지 않으면 셰이더는 주소 0에 발생하는 상황에 따라 예측할 수 없는 것을 로드할 수 있습니다.
DOTS 인스턴싱을 사용할 때 Unity가 제공하는 셰이더 그래프와 셰이더는 트랜스폼 매트릭스에 특별한 규칙을 사용합니다.GPU 메모리와 대역폭의 사용을 줄이기 위해 4개의 플로트는 항상 일정하기 때문에 16개 전체가 아닌 12개의 플로트만 사용하여 이러한 매트릭스를 저장합니다.이러한 셰이더는 매트릭스에서 각 열의 x, y, z가 순서대로 저장되는 방식으로 지정된 형식의 플로트를 기대합니다.즉, 첫 3개의 플로트는 첫 번째 열의 x, y, z이고, 그 다음 3개의 플로트는 두 번째 열의 x, y, z인 방식입니다.매트릭스는 각 열의 w
요소를 저장하지 않습니다.이에 영향을 받는 트랜스폼 매트릭스는 다음과 같습니다.
unity_ObjectToWorld
unity_WorldToObject
unity_MatrixPreviousM
unity_MatrixPreviousMI
다음 코드 샘플에는 일반 4x4 매트릭스를 12개의 플로트 규칙으로 변환하는 구조체가 포함되어 있습니다.
struct PackedMatrix
{
public float c0x;
public float c0y;
public float c0z;
public float c1x;
public float c1y;
public float c1z;
public float c2x;
public float c2y;
public float c2z;
public float c3x;
public float c3y;
public float c3z;
public PackedMatrix(Matrix4x4 m)
{
c0x = m.m00;
c0y = m.m10;
c0z = m.m20;
c1x = m.m01;
c1y = m.m11;
c1z = m.m21;
c2x = m.m02;
c2y = m.m12;
c2z = m.m22;
c3x = m.m03;
c3y = m.m13;
c3z = m.m23;
}
}