프로그래밍/Spine2020. 4. 3. 18:54

 게임 그래픽 작업을 좀 밑바닥부터 해본 사람이 아니면 알 수 없는 자잘한 상식 같은 것이 있다. 예를 들면 이미지 리소스의 크기가 16픽셀을 넘으면 무조건 256픽셀이 되어야 한다거나, 이미지의 제한 색상 수가 16색을 넘으면 무조건 256색이 되어야 한다거나 뭐 그런 것들이다. 

 

 그 중에서도 이펙트나 애니메이션을 할 때 거의 반드시라고 해도 좋을 정도로 부딪치는 상식 아닌 상식이 있는데, 바로 이미지를 프로그램으로 조작할 때 이미지를 어둡게 (= 검은색 단색으로 밀기) 하는 것은 의외로 간단하지만, 반대로 밝게 (= 흰색 단색으로 밀기) 하는 것은 무척이나 어렵다는 사실이다. 

 

 언뜻 생각하면 이미지의 밝기를 그냥 밝게 올리면 되지 않나? 라고 생각할 지도 모른다. 하지만 컴퓨터 그래픽에서 밝기를 올린다는 것은 곧 그 픽셀의 원래 색깔이 된다는 뜻이다. 즉 명도 100% = 원래 픽셀이 가진 RGB값의 100%라는 의미가 된다. 

 

 이 때문에, 이미지를 원래의 밝기보다 더 올려서 흰색으로 만드는 것은 별도의 처리가 필요하게 된다. 옛날 게임들은 하드웨어나 엔진 레벨에서 별도의 처리가 들어가는게 보통이고, 요즘은 거의 쉐이더를 건드려서 이런 처리를 한다. 쉐이더를 건드릴 정도의 여력이 없는 프로젝트라면 보통은 흰색 단색의 이미지 리소스를 하나 더 만들어서 해결하곤 한다. 어느 쪽이든 간단하게는 구현되지 않는다. 이미 상용화된 게임들에서 별 생각없이 보던 흰색 단색으로 밀어서 밝음, 번쩍거림 등을 표현하는 효과는 사실 굉장히 고급 효과인 것.

 

 이전 포스트에서 언급한 건슈팅 게임에도, 이 단색 효과를 꼭 구현하고 싶었는데 찾아보니 일단 여러가지로 샘플이나 방법 자체는 마련이 되어 있었다. 그러나 막상 실제 게임에 반영해 보니 역시나 간단하게는 되지 않는다. 유니티나 프로그래밍에 대해 전혀 지식이 없던 나는 일단 개념을 잡는 것부터가 어려웠는데, 유니티는 일단 이미지를 표시하려면 매터리얼 설정이 있고 그 매터리얼에서 사용하는 쉐이더가 설정되어야 한다. 보통 이미지를 넣을 때는 이런게 대충 자동으로 되니까 별로 의식을 안하게 되지만 실제의 구조는 그런 식이다.

 

 이 때문에 대부분의 샘플 코드나 예제에서는 이 흰색 단색화를 구현할 때 매터리얼의 쉐이더를 조작한다. 이것저것 샘플과 웹을 뒤지고 나서 겨우 대강 윤곽이 잡히기 시작했는데, 매터리얼에서 사용하고 있는 쉐이더의 각종 속성치를 MaterialPropertyBlock이라는 놈을 통해서 조작할 수 있고 이걸 조작하면 해당 오브젝트의 MeshRenderer를 통해 반영되는 식인 듯.

 

 대충 구현에 꼭 필요한 부분의 코드를 기록해 둔다.

 

MaterialPropertyBlock 변수A;

 

void Start()
{
    변수A = new MaterialPropertyBlock();
}

 

void Update()

{

    변수A.SetColor("_FillColor", 색상); //<- 단색화할 색상을 지정함

    변수A.SetFloat("_FillPhase", 1f); //<- 색상의 농도를 지정함
    스파인이들어있는게임오브젝트.GetComponent<MeshRenderer>().SetPropertyBlock(변수A); //<- 스파인이 자식이나 부모 오브젝트에 있을 때는 게임오브젝트명을 적절하게 변경해줘야 한다

}

 

 주의해야 할 점은 해당 스파인 오브젝트는 매터리얼의 쉐이더를 SkeletonFill로 바꿔줘야 이게 먹힌다는 것. SkeletonFill.shader를 열어서 안을 보면 위쪽에 이하와 같은 구문들이 보일 것이다.

 

Shader "Spine/Skeleton Fill" {

Properties {

_FillColor ("FillColor", Color) = (1,1,1,1)

_FillPhase ("FillPhase", Range(0, 1)) = 0

 

 바로 이 부분의 _FillColor와 _FillPhase가 위 스크립트 코드의 변수A.SetColor("_FillColor", 색상), 변수A.SetFloat("_FillPhase", 1f)에 대응하게 된다. 따라서 쉐이더를 잘못 고르면 이 부분의 프로퍼티 명칭이 안 맞아 원하는 효과를 내지 못하게 된다. 특히 웹에 있는 샘플 코드는(스파인 공식 포럼의 자료 대부분 역시도) _FillPhase가 _FillAlpha로 되어 있는 경우가 많아서 이 부분을 체크하지 않으면 어디가 잘못됐는지 몰라 한참 삽질하게 된다(경험담).

 

 구현된 결과는 다음과 같다.

 

Posted by windship