게임엔진/Unity

[Unity] 셰이더 프로퍼티 값 (예,색상 변경하기)

ShovelingLife 2023. 5. 19. 15:45

매터리얼 프로퍼티 값 변경하기

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

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

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

이런식으로 마테리얼이 개별 인스턴스로 복제되어, 배칭이 깨지게 된다.
(.material에 접근하기만 해도 바로 개별 인스턴스가 생성된다.)


이를 방지할 수 있는 것이 Material Property Block, GPU Instancing이다.
Material Property Block을 이용하여 프로퍼티 값을 수정할 경우,
마테리얼의 복사본을 생성하지 않고 값을 수정할 수 있다.
그리고 GPU Instancing을 적용하면 동일 마테리얼에 대해
드로우콜을 통합하여 동적 배칭을 적용할 수 있다.

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

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 미적용

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

2. GPU Instancing 적용

Batches: 96 일부 배칭된다

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

MaterialPropertyBlock 객체를 생성하고,
MaterialPropertyBlock.Set~ 메소드를 통해 프로퍼티 값을 수정한 뒤
MeshRenderer.SetPropertyBlock() 메소드를 통해 수정된 값을 적용한다.
마테리얼마다 MaterialPropertyBlock 객체를 따로 생성해도 되고, 객체를 재사용해도 된다.

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 미적용

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

2. GPU Instancing 적용

Batches : 52,&nbsp;프로퍼티 블록을 쓰지 않는 것보다는 더 많이 배칭된다.

3. GPU Instancing + Instancing Buffer

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

 

기존 쉐이더 코드 (Surface Shader 예제)

fixed4 _Color;
half _Metallic;

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

    o.Albedo = c.rgb;
}

변경된 쉐이더 코드

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

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

출처 : https://rito15.github.io/posts/unity-material-property-block/