SetPixel 대신 Compute Shader 사용하기
프로젝트/Color Lim 개발일지

SetPixel 대신 Compute Shader 사용하기

 

게임에 물감이 튀는 효과가 있다

페인트 효과

Ground에 물감이 칠해지는 효과이고 동적으로 보이기 위해

Texture2D.SetPixel을 통해 매번 다른 크기, 개수만큼 원형을 그려주고 있었다.

 

작은 액체의 이펙트는 호출 횟수도 적고 크기도 작아서 그나마 괜찮았는데

풍선을 터뜨릴 땐 큰 범위를 SetPixel 해주니 프레임드랍이 생겼다.

BaseBranch.Update() 23.41ms

안그래도 거슬렸었는데

이참에 바꿔야겠다는 생각이 들었다.

 


 

원하는 구현 내용

  • 개수나 크기, 모양이 동적으로 그려질 수 있어야함
  • 해당 텍스쳐의 색상에 자주 접근해서 가져와야함
  • 자주 호출돼도 괜찮은 성능.

그리는건 역시 GPU를 사용하는게 제일 나으니까 최대한 셰이더를 사용하고싶었는데

유니티 셰이더는 아직 잘 다루지 못해

내가 원하는 위치에 원하는 크기만큼 그리는 기능을 구현하는게 어려워보였다.

 

좀 더 원하는대로 다룰 수 있어보이는 Compute Shader를 사용해보기로 했다.

컴퓨트 셰이더도 처음 접하다보니 지금 당장 필요한 내용만 쉽게 구현하는걸 목표로 했다.

원형을 그리는 Compute Shader
이펙트를 칠할때 쓰는 코드

 

결과적으로 프레임 드랍이 사라지고 만족스러운 결과가 나왔다.

BaseBranch.Update() 0.1ms

 


 

또다른 문제점

기존 코드는 저장된 Texture2D에서 GetColors를 사용해 색상을 얻어왔는데

RenderTexture에 저장한걸 Shader에서 바로 표현되게 하다보니

특정 위치의 Color 정보를 알아오기 위해 결국 GPU에서 CPU로 Texture를 가져와야했다.

이것도 성능이 많이 들기때문에 해결이 필요했다

 

떠오르는 해결 방법은 2가지

  1. Compute Shader에서 색상 정보를 저장해두고 가져와서 사용하기
  2. 원하는 색상 범위만큼만 작은 Texture2D로 가져오기

렌더텍스쳐의 크기가 크고 여러개가 존재하는 상황이라

1번을 사용할 경우 큰 공간할당이 필요해

원하는 부분(테두리) 만큼만 저장한다던지 하는 또다른 해결법이 필요했다.

 

2번은 결국 new Texture2D를 한 후 GetColors를 사용해야하는데

작은 범위만 가져온다고 성능이 많이 개선될까? 라는 의문이 있었다.

 

결과적으론 확실히 효과가 있었다.

지금처럼 작은 범위의 색상만 자주 얻어오는 경우 문제가 없을 정도였다.

 

성능을 더 개선하고자 한다면 얻어온 텍스쳐를 캐싱해두는 법도 있겠지만

텍스쳐의 정보가 자주 바뀌며 결국 업데이트를 해야하기도 하고

중요한건, 지금 사용함에 있어서 성능상 문제가 없다 라는 것이기에 여기까지 구현하기로 했다.

 


후기

Compute Shader를 처음 접하는 거다보니

내가 생각한대로 사용하는게 맞는지, 정상적으로 작동할지 걱정이 많았다

간단한 내용이었지만 기본 지식들을 공부하면서 진행해야해서

생각보다 시간이 오래걸렸다. (4일정도)

 

더 성능 좋게, 확장성 있게, 여러 기능을 추가해두고 싶은 마음도 있었다.

 

하지만 개발을 계속하면서 깨달은건

시간을 오래 들이면 완벽하게 만들 수 있을지 모르지만

시간은 무한하지 않다..!

내가 아는 한에서 최대한의 성능, 코드의 유연성을 만들 수 있을만큼만 만들고

잘 정리해둔 뒤 마무리하고 다음으로 넘어가는게 소규모 팀에선 중요한 것 같다.


 

+

생각했던 것과 다른 색상을 얻어와서 도대체 왜이러나 싶었다

RectTransform을 사용하는 오브젝트에서 RenderTexture를 사용하다보니 생기는 문제같다

 

왼쪽 아래가 (0, 0) 오른쪽 위가 (1, 1)인 대부분의 좌표공간과 달리

 

Rect는 왼쪽 위가 (0, 0) 오른쪽 아래가 (1, 1)이다..

 

원하는 world position의 색상을 가져오기 위해

(getColorPos.x, renderTexture.height - getColorPos.y) 에서 색상을 얻어오도록 수정해야했다.