정답이 파이프라인에 따라 뒤집힌다
적이 맞으면 잠깐 빨개지는 연출을 넣습니다. 코드는 한 줄이면 됩니다.
void Flash(SpriteRenderer sr, Color c)
{
sr.material.color = c;
}
이 한 줄이 배치를 전부 끊습니다.
.material 은 접근하는 순간 복제한다
renderer.material 은 읽기만 해도 그 렌더러 전용 머티리얼 인스턴스를 새로 만듭니다.
원본을 공유하던 상태에서 자기만의 사본을 갖게 되는 것입니다.
적이 100마리면 머티리얼 인스턴스가 100개 생깁니다.
전부 값이 같아도 서로 다른 머티리얼이라 배치가 100조각이 납니다.
그리고 이건 Frame Debugger 에 Objects have different materials 로 뜹니다.
텍스처를 아무리 아틀라스로 합쳐도 안 사라지는 종류입니다.
공유 머티리얼을 그대로 고치고 싶으면
sharedMaterial입니다. 다만 그건 그 머티리얼을 쓰는 전부를 바꿉니다. 에디터에서 고치면 에셋이 바뀝니다.
그래서 나온 정석
MaterialPropertyBlock 은 머티리얼을 복제하지 않고 값만 덮어씁니다.
검색하면 나오는 답이 이것이고, 오래 정답이었습니다.
static readonly int ColorId = Shader.PropertyToID("_Color");
static readonly MaterialPropertyBlock s_Block = new MaterialPropertyBlock();
void Flash(SpriteRenderer sr, Color c)
{
sr.GetPropertyBlock(s_Block);
s_Block.SetColor(ColorId, c);
sr.SetPropertyBlock(s_Block);
}
머티리얼 인스턴스가 안 늘어나고, 다이나믹 배칭도 계속 걸립니다. Built-in 렌더 파이프라인에서는 이게 맞습니다.
그런데 SRP Batcher 에서는 뒤집힌다
SRP Batcher 는 머티리얼별 상수 버퍼를 GPU 에 상주시켜 두고 오프셋만 바꿔 가며 그립니다. 그게 전제입니다.
MaterialPropertyBlock 은 그 경로를 우회합니다. 버퍼에 없는 값을
드로우 콜마다 따로 밀어 넣는 방식이라, SRP Batcher 가 이어 붙일 수가 없습니다.
그래서 PropertyBlock 을 쓴 오브젝트는 배치에서 빠집니다.
코드가 한 글자도 안 바뀌었는데 결론이 반대입니다. 인터넷의 최적화 조언에 유통기한이 있는 이유가 이런 것입니다. 글이 쓰인 시점의 파이프라인이 어느 쪽이었는지가 안 적혀 있으면 판단할 수가 없습니다.
그럼 어떻게 하나
Unity 6 의 URP 2D 에서는 세 가지 중에 고릅니다.
하나. 머티리얼 에셋을 여러 개 만든다. 색이 세 종류뿐이면 머티리얼을 세 개 만들어 두고 갈아 끼웁니다. SRP Batcher 의 배치 조건은 머티리얼이 아니라 셰이더 변형이라, 셰이더만 같으면 머티리얼이 셋이어도 배치가 안 끊깁니다. 런타임에 값을 계산해서 넣어야 하는 게 아니라면 이게 제일 간단합니다.
둘. 정점 컬러를 쓴다.
SpriteRenderer.color 는 머티리얼이 아니라 정점에 실려 갑니다.
머티리얼 상태가 안 바뀌니 배치도 안 끊깁니다.
피격 시 빨개지는 정도는 대개 이걸로 끝납니다.
연출을 위해 셰이더까지 갈 이유가 없는 경우가 생각보다 많습니다.
셋. 인스턴스드 프로퍼티로 옮긴다. 값이 오브젝트마다 다 달라야 하면 셰이더에서 인스턴싱을 켜고 그 프로퍼티를 인스턴스드 버퍼에 선언합니다. 손이 제일 많이 가지만 값이 진짜로 전부 다를 때는 이 길뿐입니다.
어느 쪽인지 확인하는 법
내 프로젝트가 지금 어느 경로로 도는지부터 알아야 위의 표를 읽을 수 있습니다.
- 파이프라인:
Edit > Project Settings > Graphics에서 렌더 파이프라인 에셋이 지정돼 있으면 URP 이고, 비어 있으면 Built-in 입니다 - SRP Batcher 켜짐 여부: URP 에셋 인스펙터의 Advanced 항목에 있습니다. Unity 6 은 기본으로 켜져 있습니다
- 셰이더 호환 여부: 셰이더 인스펙터를 열면 SRP Batcher 호환인지가 한 줄로 나오고, 아니면 그 이유까지 적혀 있습니다
- 실제로 이어졌는지: Frame Debugger 에서
Non-instanced properties set for instanced shader가 보이면 PropertyBlock 이 배치를 깨고 있는 것입니다