확장팩 FFF 모음 정리 링크


오늘도 기술적인 이야기 고봉밥

디맥 라이브본다고 좀 늦었음 ㅈㅅ



https://www.factorio.com/blog/post/fff-411


FFF #411 - 소행성에 관한 모든 것

게시자 Fearghall, Earendel

2024-05-17



안녕하세요!

Fearghall의 팩토리오 프라이데이 팩트에 오신 것을 환영합니다! (그러면 FFFF네요.)


지난 몇 달 동안 소행성은 다른 FFF의 배경으로 몇 번 보셨겠지만, 그 절제된 장엄함에도 불구하고 실제로는 꽤나 복잡한 트릭을 사용해야 했습니다! 저와 함께 3720 대 1의 확률로 소행성 지대를 통과하는 여정을 떠나보세요.






소행성이 걱정되는 부분


처음에는 다른 스프라이트와 동일한 플레이스홀더 소행성, 즉 2D 사전 렌더링된 조명과 그림자가 포함된 이미지로 시작했습니다. 이 방법은 효과가 있었지만 소행성들이 모두 같은 방향을 향하고 회전하지 않고 XY 평면에서만 움직이는 것이 꽤 이상하게 보였습니다.



v453000의 초기 플레이스홀더 소행성



따라서 우리는 소행성 설계 과정에서 바로 회전에 대한걸 떠올렸죠.








소행성 디자인 - Earendel


세 가지 소행성 유형으로 결정한 후 실제 소행성 디자인을 시작할 수 있었습니다. 이 설정을 고려할 때 소행성들은 가능한 한 시각적으로 서로 구별되어야 한다는 것이 분명했습니다.


모든 소행성 유형에 동일한 기존 소행성 모양을 사용하고 플레이스홀더 이미지와 같이 색상으로만 구분할 수도 있었습니다. 그렇게 하려면 우리가 원하는 것보다 더 생동감 있게 만들어야 하지만, 가장 큰 문제는 전체적으로 보기 좋지 않다는 것입니다.


대신 모든 면에서 각기 다른 소행성을 디자인하기로 결정했습니다.





먼저, 전체적인 형태입니다. 이것은 소행성의 조명 때문에 중요합니다. 소행성을 식별하는 데 있어 가장 큰 부분은 왼쪽에서 라이트가 비추는 면의 모양입니다. 금속 소행성은 심하게 변형된 오목한 모양의 움푹 패인 금속 덩어리입니다. 탄화 소행성은 자갈과 먼지로 이루어진 볼록한 덩어리로, 마치 여러 개의 구가 합쳐진 것과 비슷합니다. 산화물 소행성은 직선과 날카로운 각도가 더 많은 딱딱한 면이 있는 형태입니다.


표면 재질을 비교하자면, 금속 소행성은 금속 광택이 있습니다. 탄화 소행성은 매우 거칠고 자갈과 같은 표면 스크래치가 더 많을 것입니다. 산화물 소행성은 더 반짝이고 유리처럼 보이지만 깨끗한 보석보다는 먼지가 쌓이거나 서리가 내린 것처럼 보입니다.


이렇게 하면 멋지고 뚜렷한 소행성을 만들 수 있었지만, 소행성이 매우 뚜렷하기 때문에 특정 소행성이 반복되면 반복되는걸 쉽게 알아볼 수 있다는 또 다른 문제가 발생했습니다. 눈에 띄는 반복을 피하려면 많은 바리에이션이 필요했고, 스프라이트가 상당히 크기 때문에 비용이 많이 들었습니다.


또 다른 옵션은 소행성을 회전시켜 일부 유사성을 숨기는 것이지만...





회전시키지 말아야 하는 이유 7680개


일반적으로 캐릭터나 차량처럼 회전이 필요한 스프라이트의 경우 조명이 일관적으로 보이게끔 회전할 때마다 별도의 이미지를 렌더링합니다. 이 경우에도 이 접근 방식이 효과가 있었겠지만, 소행성 종류마다 크기가 5가지이고 각 종류마다 어느 정도의 변형이 필요하며 그럴듯하게 회전하려면 최소 64 프레임이 필요하므로 총 무려 7680프레임이 넘는 애니메이션이 필요한 성능상 최적화의 악몽이 되었을 것입니다.


분명 다른 솔루션이 필요했습니다.






셰이더 적용


소행성에 라이팅 정보를 동적으로 적용할 수 있는 방법이 필요해 보였습니다. 게임 아티스트의 가장 친한 친구인 노멀 맵이 등장합니다. 잘 모르는 분들을 위해 설명하자면 노멀 맵은 기본적으로 표현하고자 하는 대상의 기울기를 저장하는 이미지입니다.



왼쪽/오른쪽 경사면은 빨간색 채널에, 위쪽/아래쪽 경사면은 녹색 채널에 저장됩니다.



이 경사면의 각도 정보를 사용하여 라이트를 적용하려면 셰이더에서 몇 가지 영리한 수학적 트릭을 이용하여 고정된 빛의 지점에서 경사 방향을 참조해야 합니다.




회전에 따른 라이팅 업데이트



이렇게 하면 '확산(Diffuse)' 라이트는 제공되지만 색상 정보는 없습니다! 1950년대 스타일의 스페이스 오페라를 만들고 싶지 않으므로 소행성이 흑백이 아니어야 합니다. 따라서 소행성을 렌더링할 때 색상 정보와 라이팅 정보를 분리해야 합니다.


노멀 맵에서 생성된 라이팅을 원시 컬러 이미지에 블렌딩하면 좋은 결과를 얻을 수 있습니다.




디퓨즈 색상 | 라이트가 적용된 디퓨즈 색상



그러나 몇 가지 눈에 띄는 점은 라이트가 매우 "평평"하고 금속 소행성의 금속과 얼음의 광택이 잘 드러나지 않는다는 것입니다. 이는 셰이더가 아직 재질의 광택을 고려하지 않았기 때문입니다. 그러나 우리는 광택이 균일하게 증가하는 것이 아니라 영역마다 달라야 합니다! 그래서 이번에는 반사(Specular) 라이팅을 위해 또 다른 패스를 렌더링합니다.


디퓨즈 색상을 사용하여 반사 색상을 처리하고 소행성을 올바른 영역에서 더 밝게 만드는 것으로 간단히 적용합니다.





반사만 | 반사 전 단계



하지만 얼음은 여전히 이상하게 보입니다! 얼음은 너무 단단해서 빛이 일부 통과하고 산란되어 아름다운 푸른 빛을 나타내야 합니다. 이를 Subsurface scattering 또는 SSS라고 합니다. 따라서 빛이 간접적으로 닿는 영역에만 블렌딩된 이미지와 렌더링 패스를 한 번 더 추가합니다.


그러나 모든 소행성에는 여전히 그림자가 너무 많이 있습니다. 우주에는 빛을 반사할 것이 없기 때문에 기술적으로는 정확하지만, 지구에서 진화한 우리 눈에는 부자연스러워 보입니다. 따라서 그림자의 강도를 낮추고 첫 번째 광원과 상보적인 각도로 보조 광원을 추가할 수 있습니다.





SSS, 반사광 없음 | SSS, 반사광 적용



이제 끝났습니다! 라이팅 계산과 관련된 몇 가지 문제를 해결하는 데 도움을 주신 Earendel과 Posila에게 감사드립니다.





회전에 따른 라이트 업데이트









우주 먼지 셰이더 - Fearghall


Jerzy의 스타필드 셰이더와 소행성의 움직임은 플레이어에게 플랫폼이 움직이고 있음을 보여주는 데 어느 정도 도움이 되었지만, 전체적으로 그 효과를 보기 어려웠기 때문에 모든 곳에서 분명하지만 과하지 않은 무언가가 필요했습니다.


한 가지 해결책은 스타트렉이나 닥터 후 같은 고전적인 워프 속도 효과를 사용하는 것이었지만, 이것은 약간 만화 같고 팩토리오의 현실적인 스타일과는 약간 거리가 멀어 보였습니다. 그래서 저는 폭발한 소행성에서 나온 것으로 추정되는 미세 운석과 행성 간 먼지를 모션 블러 처리하는 것으로 타협점을 찾았습니다.



지나가는 우주 먼지



이 효과를 만드는 한 가지 방법은 모션 블러를 사용하여 엄청난 양의 연기와 파티클을 스폰하는 것이지만, 파티클은 많은 리소스를 대량으로 사용하며, 이 효과는 플랫폼 크기에 따라 확장해야 하므로 최적화 비용이 기하급수적으로 증가하게 됩니다.


대신 모든 '파티클'이 포함된 단일 텍스처를 만들기로 했습니다. 최적화를 위해 이 텍스처를 셰이더에 사용된 다른 여러 텍스처와 '패킹'하여 샘플러 수를 줄이면서도 많은 디테일을 표현할 수 있도록 했습니다.





4개의 이미지를 모두 하나의 RGBA 이미지로 결합



텍스처 좌표에 대한 약간의 수학을 통해 이 이미지를 움직일 수 있으며 빨강, 녹색, 파랑 및 알파 채널을 마스킹하여 필요한 "패킹된" 데이터에 액세스할 수 있습니다.




단순히 모든 텍스처를 늘어놓았을 때



그러나 먼지는 여전히 모두 거의 같은 속도로 움직이고 있어 이상하게 보입니다. 따라서 속도를 수정하여 파편과 먼지가 다른 속도로 움직이도록 할 것입니다. 하지만 모든 파편이 같은 속도로 움직이면 너무 하나의 응집된 구조처럼 보일 수 있습니다. 이때 마스킹 텍스처가 유용합니다. 이미지 #1을 사용하여 개별 파티클을 서로 다른 시간에 선택적으로 마스킹하면 많은 작은 물체가 독립적으로 움직이는 것처럼 보이게 할 수 있습니다. 이 작업은 약간의 수학을 통해 값 주변의 피크 그래프를 그린 다음 시간 변수에 목표 값을 할당하고 다시 반복하는 방식으로 수행됩니다.




표시할 파편을 '무작위' 선택



현재 파티클은 항상 트레일 이미지를 100%로 사용하고 있지만, 느리게 움직이는 파티클의 경우 흐릿함을 줄여야 합니다. 이미지를 만들 때 미리 계획한 덕분에 임계값 이하로 픽셀을 잘라내면 이 작업을 수행할 수 있습니다. 트레일 이미지는 선형 그라데이션이므로 이미지 값에서 목표 값을 빼기만 하면 길이를 쉽게 제어할 수 있습니다.



플랫폼이 가속을 받으면 파편의 트레일은 더 길어집니다.



이 과정을 다른 속도와 배율로 몇 번 반복하면 강제 시차 효과가 생성됩니다. 즉, 카메라에 가까운 먼지는 더 크고 더 빠르게 움직입니다.


이제 이 모든 것을 결합하고 강도를 줌 레벨에 맞춰서 보기에 너무 산만하지 않도록 하기만 하면 완료됩니다!




움직이는 플랫폼 위를 지나가는 우주 먼지



이로써 배경 요소는 끝났습니다.


먼지가 플랫폼의 움직임을 전달하는 데 도움이 되지만, 공간은 여전히 매우 2D적으로 느껴집니다. 플랫폼에서 모든 소행성이 완벽하게 평면인 이유는 무엇인가요?


따라서 배경 요소와 더 많은 소행성이 필요합니다! 다시 말하지만, 플랫폼 뒤에 렌더링되지만 충돌하지 않는 소행성을 더 많이 만들 수도 있었지만 이것이 최선이 아닌 것 같았습니다.


대신 "텍스처 스플래팅"이라는 기술을 사용하여 몇 개의 이미지만 포함된 단일 텍스처 아틀라스에서 소행성을 무한으로 만들었습니다. 이 (F)FFF는 이미 상당히 길기 때문에 자세히 설명하지 않겠지만 기본적으로 화면을 격자로 분할하고 각각에 소행성 하나를 무작위 오프셋으로 배치하는 것을 목표로 합니다.


이 기술에 관심이 있는 분들을 위해 Daniel Elliot이 이 구현에 대한 환상적으로 심층적인 유튜브 시리즈를 만들었는데, 여기서 그것과 거의 동일한 구현을 사용했습니다.





모든 배경 소행성의 텍스처 아틀라스



셰이더는 텍스처 샘플의 UV 좌표를 수정하여 이 이미지를 단일 소행성으로 분할합니다. 무작위 UV 그리드를 만들면 그 안에 소행성을 배치할 수 있습니다. 그러나 소행성이 그리드에서 두 셀의 경계를 가로질러 배치되면 소행성이 잘립니다. 따라서 인접한 셀을 참조하여 현재 셀과 겹치는지 확인해야 합니다. 다행히도 이 문제는 주어진 방향에서 동일한 함수 +/-1을 사용하면 간단히 관리할 수 있습니다.




여기에서 생성한 셀 그리드와 겹치는 샘플을 볼 수 있습니다.(빨간색으로 표시됨)



각 소행성에 임의의 회전 속도, 이동 속도, 스케일을 부여하고 시차를 위해 다른 스케일로 전체 작업을 다시 수행합니다. 그런 다음 이전과 동일한 방식으로 동적 라이팅에 동일한 계산을 사용하면 소행성의 필드가 무한대로 늘어납니다!





계속 또 계속 영원히



이 기나긴 기술적인 여정을 함께 해주셔서 대단히 감사합니다. 우리 함선은 온전하게 살아남은 것 같습니다.



항상 그렇듯이, 일반적인 장소에서는 절대 확률을 알려주지 마세요.