Posts 유니티 - Material Property Block을 통해 프로퍼티 값 변경하기
Post
Cancel

유니티 - Material Property Block을 통해 프로퍼티 값 변경하기

드로우 콜과 배칭의 개념



마테리얼 프로퍼티 값 변경하기


MeshRenderer.material.Set~ 메소드를 통해

스크립트에서 마테리얼 특정 프로퍼티의 값을 실시간으로 변경할 수 있다.

하지만 이렇게 .material에 접근하여 프로퍼티를 수정하면

image

이런식으로 마테리얼이 개별 인스턴스로 복제되어, 배칭이 깨지게 된다.

(.material에 접근하기만 해도 바로 개별 인스턴스가 생성된다.)


이를 방지할 수 있는 것이 Material Property Block, GPU Instancing이다.

Material Property Block을 이용하여 프로퍼티 값을 수정할 경우,

마테리얼의 복사본을 생성하지 않고 값을 수정할 수 있다.

그리고 GPU Instancing을 적용하면 동일 마테리얼에 대해

드로우콜을 통합하여 동적 배칭을 적용할 수 있다.


1. 프로퍼티 값을 그냥 수정하는 경우


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private MeshRenderer[] renderers;

private void Start()
{
    renderers = GetComponentsInChildren<MeshRenderer>();
    SetRandomProperty();
}

private void SetRandomProperty()
{
    foreach (var r in renderers)
    {
        r.material.SetColor("_Color", UnityEngine.Random.ColorHSV());
        r.material.SetFloat("_Metallic", UnityEngine.Random.Range(0f, 1f));
    }
}


[1] GPU Instancing 미적용

image

  • Batches : 172
  • 배칭이 전혀 되지 않는다.


[2] GPU Instncing 적용

image

  • Batches : 96
  • 일부 배칭된다.


2. Material Property Block을 통해 수정하는 경우


MaterialPropertyBlock 객체를 생성하고,

MaterialPropertyBlock.Set~ 메소드를 통해 프로퍼티 값을 수정한 뒤

MeshRenderer.SetPropertyBlock() 메소드를 통해 수정된 값을 적용한다.

마테리얼마다 MaterialPropertyBlock 객체를 따로 생성해도 되고, 객체를 재사용해도 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private MeshRenderer[] renderers;
private MaterialPropertyBlock mpb;

private void Start()
{
    renderers = GetComponentsInChildren<MeshRenderer>();
    mpb = new MaterialPropertyBlock();
    SetRandomPropertyWithMPB();
}

private void SetRandomPropertyWithMPB()
{
    foreach (var r in renderers)
    {
        mpb.SetColor("_Color", UnityEngine.Random.ColorHSV());
        mpb.SetFloat("_Metallic", UnityEngine.Random.Range(0f, 1f));
        r.SetPropertyBlock(mpb);
    }
}


[1] GPU Instancing 미적용

image

  • Batches : 172
  • Material Property Block을 통해 프로퍼티를 수정해도
    GPU Instancing을 적용하지 않으면 배칭이 되지 않는다.


[2] GPU Instancing 적용

image

  • Batchs : 52
  • 프로퍼티 블록을 쓰지 않는 것보다는 더 많이 배칭된다.


[3] GPU Instancing + Instancing Buffer

스크립트를 통해 수정할 프로퍼티들을 쉐이더에서 Instancing Buffer 내에 선언해준다.


  • 기존 쉐이더 코드(Surface Shader 예제)
1
2
3
4
5
6
7
8
9
10
fixed4 _Color;
half _Metallic;

void surf (Input IN, inout SurfaceOutputStandard o)
{
    fixed4 c = _Color;
    o.Metallic = _Metallic;

    o.Albedo = c.rgb;
}
  • 변경된 쉐이더 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

UNITY_INSTANCING_BUFFER_START(Props)

    UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
    UNITY_DEFINE_INSTANCED_PROP(half, _Metallic)

UNITY_INSTANCING_BUFFER_END(Props)

void surf (Input IN, inout SurfaceOutputStandard o)
{
    fixed4 c   = UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
    o.Metallic = UNITY_ACCESS_INSTANCED_PROP(Props, _Metallic);

    o.Albedo = c.rgb;
}


image

  • Batches : 8
  • 이제 완벽히 배칭되는 것을 확인할 수 있다.


결론


프로퍼티 값을 스크립트에서 수정하면서 동적 배칭을 적용하기 위해서는

  1. 수정할 프로퍼티를 쉐이더에서 Instancing Buffer 내에 선언한다.
  2. 마테리얼에서 Enable GPU Instancing에 체크한다.
  3. 스크립트에서 Material Property Block을 통해 값을 수정한다.


주의사항

  • 런타임에 정적 배칭 대상의 프로퍼티 값을 수정하면 배칭이 완전히 깨져버린다.
  • 따라서 런타임에 프로퍼티 값을 수정할 대상에는 동적 배칭을 적용해야 한다.


References


This post is licensed under CC BY 4.0 by the author.