개발 노트 Programmable Blending 2016/08/17 00:29 by cagetu

HDR 렌더링 파이프라인을 구성하게되면 부딪히게 되는 문제 중 하나가 바로 Float 렌더타겟에 대한 것이다. 모바일에서 지원하지 않는 기기들도 많이 있고, 메모리 문제 때문에 HDR압축(RGBM 압축...)을 처리해야 한다. 일반적인 압축 방법은 알파 채널에 Color 값을 복원할 수 있는 키 값을 저장해서 압축하는 형태를 가지기 때문에, 알파 블랜딩을 처리하기가 어렵다. 
(포프님이 "알파 혼합 가능한 RGBM"라는 하드웨어 알파블랜딩 공식을 해석하셔서 풀어내신 방법도 있다.)

예전에 작업할 때에도 이 문제에 대해서 해결을 하지 못하고 이리 저리 실험만 하다가 답을 얻지 못했었다. 

언리얼4는 과연 이 부분을 어떻게 처리하였을까? 

핵심은 [Programmable Blending] 으로 하드웨어 알파 블랜딩을 사용하지 않고, 셰이더에서 알파블랜딩을 처리하는 것이다.

힌트는 바로 GL_EXT_shader_framebuffer_fetch 라는 확장 함수에 있었다. 이 OpenGL ES 2.0부터 있었다고 하는데, 솔직히 처음보는 함수이지만 이 녀석이 하는 일은 단순하지만 굉장하다. 바로 셰이더에서 현재 background pixel 값을 가지고 올 수 있기 때문이다.

셰이더에서 알파 블랜딩을 처리하기 때문에 "압축된 현재 픽셀 값 -> 복원 -> 알파 블랜딩 -> 결과를 다시 압축" 형태로 셰이더에서 처리할 수 있다.

UE4의 사례를 보면 다음과 같다.

[머터리얼 계산]
1. 라이팅 계산
2. Fog 계산
3. 알파 블랜딩 처리
- gl_LastFragData 값을 HDRDecode
- 알파 블랜딩 연산
4. HDREncode로 최종 결과값 압축

[포스트 프로세스]
1. 렌더 타겟 색상을 합성, 샘플링 하기 위해 HDRDecode로 색상 복원
2. 합성, 샘플링 처리
3. HDREncode로 최종 결과값 압축

주의할 점은 [Programmable Blending]을 사용하게 되면 하드웨어 블랜딩을 혼용해서 사용할 수 없다는 점이다. 당연한 이야기이지만 HDREncode된 결과를 블랜딩할 수 없다.

Encode/Decode 하는 연산량이 조금 추가되기는 하지만, 메모리 절약이나 광범위하게 사용할 수 있다는 측면에서 충분히 시도해볼만한 방법이라고 보인다. DirectX 계열에서도 아마 유사한 기능이 있을 것이로 보이기 때문에 동일한 처리가 가능할 것으로 보인다. 물론 더 좋은 방법이 있을 것 같지만...

HDR 렌더링이 쉽지 않기 때문에 궁금한 부분이 참 많았는데, 그 중에 하나에 대한 대답을 들은 것 같아서 정말 속이 다 시원하다.




개발 노트 glDiscardFramebufferEXT 2016/08/04 01:16 by cagetu

OpenGL ES 2.0 "Rendering a Frame"를 보면 예전에 iOS에 내용을 적어놓기는 했지만, 잘못 이해하고 있었던 부분이 있어서 갱신해본다.

모바일 기기에서는 RenderTarget을 전환하는 비용이 굉장히 비싸다. 그래서 glDiscardFramebufferEXT라는 확장함수를 사용하면 더 이상 사용하지 않는 renderbuffer들 (depth/stencil)을 제외시킬 수 있어서 최적화하는데 도움을 준다.

나는 glDiscardFrameBufferEXT를 사용하면 즉시 depth, stencil 버퍼를 제거해주는 것으로 이해하고 있었고, 항상 present 하기 전에 주로 사용했었다. 

언리얼 코드를 보면 "장면 렌더링 -> DiscardFramebuffer (Depth/Stencil) -> PostProcess -> Present" 순서로 처리가 되는데, PostProcess 처리 하는 중간에 장면 버퍼 (프레임 버퍼)에 메쉬를 덧그리는 작업을 진행했더니, 깊이 버퍼가 갱신되지 않는 현상을 보게 되었다. 

찾아보니, glDiscardFramebuffer는 드라이버에게 해당 renderbuffer들은 갱신시키기 위해 더 이상 유지시킬 필요가 없다고 알려주는 것이다. 이 후의 해당 렌더타겟이 다시 바인딩 될 때까지는 해당 renderbuffer들은 더 이상 갱신되거나 하지 않는다는 것이다. 

그러니, 내가 저 흐름에서 Discard 이후에 Discard 이전의 깊이버퍼를 이용해서 깊이 테스트를 하려고 했더니 작동이 정상적으로 되지 않았던 것이다. -_-;;;;

반대로 보면 언리얼이 최적화를 위해서 한 작업에 하나를 보게 된 것이라서 무척 기뻤다. 이게 별거 아닌 것 같아 보여도, 알고 보면 엄청난 비법이다!! 이 한 줄이 전체적으로 엄청난 속도 차이를 보여줄 수 있다! 이건 바로 적용해봐야지!!! 혹시 유니티가 이런 처리가 안되어 있다고 한다면, 포스트프로세스 렌더 쪽에서 속도 차이가 나는 건 당연할 듯... (물론 잘 되어 있겠지 뭐!)



더 재밌는 것은 이게 PC로 개발할 때에는 잘 작동하는 것처럼 보이는데, 이유는 glDiscardFramebufferEXT 확장 함수가 애초에 PC 애뮬레이터나 OpenGL에는 지원하지 않고, Android 기기에서는 지원하기 때문이다. 그러니, 기기에서 테스트할 때 문제가 생긴다는 것이지!!! 헐~

그래도 다행히 작동 방식을 잘못 이해하고 있었지만, 저 함수를 자체엔진에서 작업해본 적이 있었기 때문에 그래도 Discard를 봤을 때 의심해볼 수 있었지 아마 해보지 않았다면 지금도 헤매도 있었을지 모른다. 규모의 차이가 있지만 직접 api 레벨로 구현해본 경험이 상용엔진을 작업할 때에도 정말 많은 도움을 주고 있다. 내가 그 동안 한 일들이 헛되지 않았다는 것을 느끼고 있다. (언리얼을 파악하는데 오래 걸리는 건 어쩔 수 없지만.. 흑.. ㅡㅡ;;)

확실히 알고 보면 배울게 많은 코드다!!

개발 노트 GDC16 2016/08/01 23:42 by cagetu

UE4가 빌드 시간이 꽤 긴 편이어서 빌드 타임에 틈틈히 GDC16 문서를 보고 있다.

GDC16 Vault에서 무료로 볼 수 있는 강연도 많고 동영상도 많이 올라와 있다. (물론 영어라.. 크억!)

https://knarkowicz.wordpress.com/2016/03/21/gdc-2016-presentations/ 에 이쁘게 정리한 내용해 준 분도 계심...

작년 같았으면 디자인이랑 인디 세션을 중심으로 봤을텐데, 엔진 작업을 다시 시작하면서 주로 Visual Arts 랑 프로그래밍 세션을 보고 있다. 

컨텐츠 작업을 주로 하느라 관련 기술을 업데이트 하지 못한 것도 있지만, 모바일을 주로 다루다보니, 정체되어 있던 부분들도 상당히 많다. 보고 있으면 너무 기술적인 차이가 느껴져서 좌절스럽기도 한 내용도 있고, 같은 모바일이더라도 내가 생각했던 것들보다 더 깊이 생각하고 고민해서 발전한 것들을 보면 반성하게 되기도 하다.

따라가야 하는 입장이지만, 한 걸음씩 나아가서 손에 닿을 정도까지라도 가보고 싶다.. 아직 가야할 길이 너무나 멀다.. ;;;

비주얼웍스2 2016/07/29 00:38 by cagetu

예전에 집에서 틈틈히 렌더러를 만들었었는데, 그 첫번째 이유가 회사에서 작업한 내용은 집에서 가지고 놀 수 없다는 것이었고, 새로운 기술이 나오거나 할 때에 바로 적용해보려면 뭔가 돌아가는 만들어진 물건(?)이 있어야 하는데, 그럼 하나 만들어두어야겠네?! 라고 해서 시작된 프로젝트였다. (지금 생각해보면 그냥 오픈소스 프로젝트여도 되었으려나?)

지금도 마찬가지로 언리얼을 가지고 작업을 하다보니, 언리얼에서 어떻게 구현했는지를 보면서 예전에 내가 했던 것들이랑 비교해보고 싶었고, 그럼 이걸 비교해보려고 테스트할만한 물건이 있어야 하는데? 라고 생각하다가 지금은 배틀리그 이후로 사용하지 않는 엔진이 되어버린 소프트네트 시절 VisualWorks라는 엔진을 다시 꺼내서 재구성을 시작하기로 했다.


실행은 되는데, D3D9 / ES2.0 기반인데, 다른 API를 붙이기에는 확장성이 많이 떨어진다. vs2015로 갈아타는 것부터 시작해서 틈틈히 정리해보도록 하려고 한다. 그래도 틈틈히 정리하면 당장 ES 2.0 기반의 모바일 렌더링 테스트를 하는데에는 전혀 문제가 없을 것으로 보인다.

테스트 결과가 괜찮으면 유니티로도 사용가능하도록 붙여보고 할 생각도 있다. 누구 말대로 어차피 해봐야 아무도 관심없을 거라고 하지만, 이건 그냥 나한테 필요해서 하는거니까. 괜찮아!!! ㅎㅎ

궁극적으로는 Vulkan과 DX11 기반도 붙이고 싶은데, 그럴려면 고민을 많이 해야 할 듯... 새로 만드는 것이 좋으려나?? 잘 모르겠네.. 그건 그 때가서 생각해보자!! 장난감이 늘어난다!! 신나~! 헤헤~



개발 노트 협업 2016/07/28 01:32 by cagetu

주로 5명이 넘지 않는 프로그램팀으로 일을 한 경험도 많고, 전체 팀을 합쳐도 소규모로 개발을 한 경험이 많아서 그런지 나에게 저장소는 그냥 내가 만든 코드를 보관하는 용도라는 인식이 강하다. 그래서 코드를 잘못 올리거나 빌드가 깨졌을 때의 패널티에 대한 것은 이야기를 많이 듣기는 했지만, 인식이 크지는 않은 편이다. 

이런 습관이 대규모 프로젝트에 들어가니까 되려 부작용이 생겼다. 너무 부주의하게 코드를 편하게 올리거나 해서 실수가 생겼을 때, 너무 큰 피해를 주게 된다는 것을 오늘 느꼈다. 한번 실수로 빌드가 실패하게 되면 거의 반나절에서 하루의 다른 사람의 작업 시간을 날리게 될 수도 있고, (당연한 이야기 이지만) 다른 팀의 작업물을 건드릴 수가 있다. (사실 이전에도 에러난 코드를 올려서 가끔 폐를 끼치기는 했지만... ;;;)

오늘 너무 큰 충격을 받았다. 왠만하면 그냥 실수해도 넘어갔었지만, 지금은 진짜 큰일 날 수도 있겠구나! 라는 생각이 들었다. 내가 부주의하면 100명의 작업이 마비될 수도 있는 상황이 벌어진다는 것을!!! 꺄~~~ 소~~오~~름!!!

시행착오는 당연히 있을 수 있지만, 반복되면 안된다!!! 그리고 자동화가 정말 이래서 중요한거구나!!! 그렇다고 쫄지말고! 미안하다고 하고, 계속 깨져가면서 익혀보자!!! 뭐! 욕 먹고 말지!!!

큰 프로젝트에서 배우고 싶었던 것들이다. 작업물을 올릴 때 꼭 실행 테스트를 해보고, 확인해보는 습관을 길러야겠다!!! 좋은 가르침이 있던 날이다!!!

1 2 3 4 5 6 7 8 9 10 다음



메모장

내가 먼 훗날에 이 글들을 보았을 때, 좋은 추억이 될 수 있기를...

나를 위해... 나에게 쓰는...

msn: cagetu@hotmail.com
mail: cagetu79@gmail.com
twitter: twitter.com/cagetu
facebook: facebook.com/cagetu