언리얼 엔진의 초현실적 휴먼 렌더링 기술 분석 1부 - 개요 및 피부 렌더링
🌐 원문 링크: 剖析Unreal Engine超真实人类的渲染技术Part 1 - 概述和皮肤渲染 (cnblogs.com/timlly)
📅 원문 발행일: 2019-06-27 | ✍️ 저자: Timlly (00 / )💡 시리즈 분류:
Digital Human| 국내 업계 표준 용어 감수 및 수식/도해 복원 적용 완료

'매트릭스'의 주인공의 많은 장면은 컴퓨터로 렌더링된 가상의 디지털 휴먼입니다

영화 '배틀엔젤'의 한 장면. 주인공 알리타도 가상의 인물이다.
최근 몇 년간 컴퓨터 하드웨어 성능과 렌더링 기술의 향상으로 인해 오프라인 렌더링 분야의 영화 및 애니메이션 분야의 폭넓은 응용 외에도 실시간 분야의 응용도 큰 발전을 이루었습니다. 예를 들어 범 엔터테인먼트 분야의 차세대 게임, AAA 명작, VR 게임, 생방송 등이 있습니다.

"Far Cry 5"의 가상 게임 캐릭터

GDC2013에서 R&D와 블리자드가 시연한 차세대 가상 캐릭터

GDC2018에서 언리얼 엔진이 선보인 가상 캐릭터 사이렌은 움직임, 표정, 신체 및 기타 정보를 통해 배우가 실시간으로 구동할 수 있습니다.
1.1.2 디지털 휴먼 생산 프로세스
디지털 휴먼은 여러 단계와 번거로운 프로세스를 가지고 있습니다. 그러나 요약하면 일반적으로 다음 단계가 있습니다.
- 모델 스캔. 일반적으로 광학 스캐너나 SLR 카메라로 구성된 360도 사진 경계 상자를 사용하여 스캔된 개체를 전방위적으로 스캔하여 원본 모델 데이터를 얻습니다.

위 이미지는 많은 사진 및 조명 장비로 구성된 구형 매트릭스인 모델 스캐너를 보여줍니다.
- 모델 조정. 스캐닝 단계에서 얻은 초기 모델은 종종 결함이 있어 렌더링에 직접 넣을 수 없습니다. 아티스트는 3D 모델링 도구(예: Maya, 3DMax 등)를 사용하여 조정, 최적화, 리토폴로지를 거쳐 최종적으로 사용 가능한 적합한 모델로 조정해야 합니다.

왼쪽: 스캔한 초기 모델; 중간: 조정된 중간 모델; 오른쪽: 최적화된 디테일을 갖춘 사용 가능한 모델입니다.
- 스티커를 만들어 보세요. 이 단계에서는 모델링 소프트웨어 또는 재료 제작 소프트웨어(예: Substance)를 사용하여 고정밀 모델을 사용하여 디퓨즈, 노멀, 러프니스, AO, 산란, 하이라이트 및 기타 텍스처를 굽거나 생성하여 최종 렌더링을 준비합니다. 이러한 텍스처의 원래 크기는 일반적으로 가상 인간의 세부 사항을 높은 정밀도로 복원하기 위해 4K, 8K 또는 16K로 매우 큽니다.

확산 맵

노멀맵
- 가져오기 엔진. 이 단계에서는 이전에 제작된 모델과 텍스처를 렌더링 엔진(예: UE4, Unity 등)으로 가져오고 조명, 재료, 장면 및 기타 요소를 추가하고 캐릭터의 포괄적인 PBR 렌더링 기술과 결합하여 최종 이미지를 얻습니다.

Unreal Engine으로 렌더링된 가상 캐릭터
1.2 언리얼 엔진의 디지털 휴먼
1.2.1 언리얼 엔진 디지털 피플의 역사
거대 상업용 렌더링 엔진인 언리얼 엔진은 실시간 분야에서 디지털 휴먼을 렌더링하려는 많은 시도를 해왔습니다. 주요 노드는 다음과 같습니다.
- 2015: "소년과 그의 연". 이는 1인칭 슈팅 게임으로 명성을 떨쳤던 에픽이 이전에 해왔던 것과는 전혀 다른 오픈 월드 컨셉과 자연스러운 캐릭터 애니메이션 스타일을 선보였습니다.

'소년과 연'의 스크린샷
- 2016: "헬블레이드: 세누아의 희생". 이는 디지털 휴먼을 실시간 게임에 도입하려는 언리얼의 시도입니다. 화질면에서는 매우 현실적인 수준에 도달했습니다.

"헬블레이드: 세누아의 희생"에 등장하는 게임 캐릭터화면
- 2017: "마이크를 만나다". Siggraph 2017에서 Epic Game은 이 프로젝트를 통해 디지털 휴먼 기술의 최신 연구를 전 세계에 선보였습니다. 최첨단 화면 캡처 기술, 체성 감각 제어 기술 및 화면 렌더링 기술을 사용하여 컴퓨터에서 인간 아바타를 생성하는 것입니다. 그중 디지털맨 마이크는 유명한 영화 특수효과 대가이자 Fx 가이드 웹사이트 창립자인 마이크 시모어(Mike Seymour)의 화신이다.

Unreal Engine 공식 팀이 제작한 Mike 가상 캐릭터
- 2018: "사이렌". 사이렌(Siren)은 에픽게임즈(Epic Game), 3레터럴(3Lateral), 큐빅모션(Cubic Motion), 바이콘(Vicon), 텐센트의 NEXT 스튜디오 등 다수의 다국적 기업과 협력해 제작한 최고 수준의 실시간 렌더링 가상 캐릭터로, 제작에만 반년 이상의 시간이 걸렸다. 화질면에서는 디지털 사진과 다르지 않습니다.

'사이렌'에 등장하는 가상 캐릭터의 디테일은 디지털 카메라로 촬영한 사진과 동일합니다
1.2.2 "마이크를 만나다" 프로젝트
저자는 원래 "사이렌"이라는 가상 캐릭터를 기반으로 한 연구를 수행하고 싶었지만 관계자는 이 프로젝트를 오픈 소스로 만들지 않았습니다.
그래서 이 글에서는 "Meet Mike" 프로젝트의 캐릭터만을 연구 대상으로 사용할 수 있습니다.
"Meet Mike" 프로젝트의 에셋과 소스 코드는 Unreal Engine의 Epic Games Launcher에서 다운로드할 수 있습니다.

"Meet Mike"의 리소스 및 소스 코드를 다운로드하는 세부 단계
Mike 프로젝트가 성공적으로 다운로드되면 프로젝트의 DigitalHuman.uproject 파일을 열면 다음 화면이 표시됩니다.

Mike의 모델과 모든 재질의 세부 정보를 보려면 오른쪽 상단에 있는 World Outsider 패널에서 "final_mike"를 클릭하세요.

특정 부분(예: 피부)의 재질을 연구하려면 해당 재질을 두 번 클릭하여 재질 노드를 엽니다. 아래 그림은 M_Head 스킨 재질을 두 번 클릭한 후의 인터페이스입니다.

머티리얼 에디터를 연 후 후속 연구를 수행할 수 있습니다. 다음 장에서는 디지털 인간의 피부, 눈알, 머리카락 및 기타 신체 부위의 렌더링 기술에 중점을 둘 것입니다.
Mike의 일부 데이터:
- 570,000개의 삼각형과 690,000개의 꼭지점. 얼굴에는 많은 수의 삼각형이 집중되어 있으며, 특히 머리카락에 약 75%를 차지합니다.

- 각 머리카락은 별도의 삼각형이며 대략 20,000개 이상의 머리카락이 있습니다.

얼굴 뼈대 리그는 약 80개의 관절을 사용하며 주로 머리카락 움직임과 얼굴 털에 사용됩니다.
얼굴 모델은 보다 부드러운 움직임을 위해 턱, 눈, 혀에 사용되는 관절을 10개 정도만 사용합니다.
얼굴은 입체형 적외선 카메라가 장착된 Technoprop의 고급 머리 장착형 얼굴 캡처 장치를 사용하여 캡처되었습니다.
총 750개의 블렌드 셰이프가 사용되었습니다.
이 시스템은 복잡한 기존 소프트웨어와 세 가지 딥 러닝 AI 엔진을 사용합니다.
2. 피부 렌더링
피부 렌더링 기술은 초기 단일 맵 + 렘브란트의 렌더링 방식부터 최근 물리 기반 SSSSS(Screen Space Subsurface Scattering)에 이르기까지 수십 년에 걸쳐 발전해 왔습니다. 이를 바탕으로 파생된 피부 렌더링 기술은 무궁무진하며, 그 중 가장 기본이자 대표적인 것이 SSS(Subsurface Scattering)입니다.
가상 캐릭터 렌더링에서는 피부 렌더링이 특히 중요합니다. 피부는 사람들이 매일 눈으로 보는 매우 친숙한 것이기 때문에 렌더링이 약간 잘못되거나 디테일이 충분히 처리되지 않으면 언캐니 밸리 이론에 빠지게 됩니다. 불쾌한 계곡 이론이 무엇인지는 여기를 참조하세요.

위 사진은 피부 디테일을 제대로 처리하지 못해 불쾌한 골짜기 이론에 해당합니다
2.1 피부 구성 및 이론
2.1.1 피부 구성
인간 피부의 물리적 구성은 매우 복잡합니다. 표면과 내부는 매우 복잡한 물질로 구성되어 있습니다. 단면은 다음과 같습니다.

헤어 샤프트. 피부 표면에 붙어 있는 작은 털.
기름. 피부 표면은 얇은 오일층으로 덮여 있는데, 이는 피부 하이라이트의 주요 원인입니다.
표피. 그리스 층 아래에는 표면 아래 산란을 담당하는 물질 중 하나인 표피가 있습니다.
진피. 표피 아래에는 실제 피부 조직이 있으며, 이는 서브서피스 스캐터링(SSS)을 담당하는 물질 중 하나이기도 합니다.
모낭. 피하 조직과 융모의 기저부.
정맥. 진한 파란색으로 나타나는 정맥.
동맥. 검붉은 혈관.
지방 조직. 지방 조직은 또한 서브서피스 스캐터링(SSS)에 작은 기여를 합니다.
기타 : 피부 표면 질감, 주름, 모공, 주근깨, 여드름, 점, 흉터, 기름 입자 및 기타 세부 사항.

실제 피부에는 모공, 융모, 여드름, 점, 기름 등 많은 세부 사항이 포함되어 있습니다...
2.1.2 피부 모델링
피부 표면의 오일층은 피부 빛의 반사 부분에 주로 기여하고(빛의 약 6% 반사), 오일층 아래의 표피층과 진피층은 서브서피스 스캐터링(SSS) 부분에 주로 기여합니다(빛의 약 94% 산란).
피부의 구성은 매우 복잡하지만 그래픽 렌더링 세계의 현자들은 단순화된 사고 방식을 사용하여 피부를 여러 레이어로 모델링했습니다.

얇은 유분층: 피부의 스페큘러 반사를 시뮬레이션합니다.
표피: 서브서피스 스캐터링(SSS)을 시뮬레이션하는 기여 레이어입니다.
진피: 서브서피스 스캐터링(SSS)을 시뮬레이션하기 위한 기여 레이어입니다.
위에 보이는 것은 피부 표면에만 빛을 반사시키는 BRDF 모델링 방식이지만 실제로는 3-layer 모델링에서는 표피와 진피층의 Subsurface Scattering(BSSRDF)도 고려하고 있습니다. 아래 그림 중앙의 BSSRDF를 참조하세요.

2.1.3 스킨 렌더링 프로세스
피부 렌더링과 관련된 고급 기술은 다음과 같습니다.
선형 공간 조명 워크플로우. 이 부분에 대해서는 "테크니컬 아티스트의 돌아올 수 없는 지점 - 선형 공간 조명"을 참조하세요.
물리 기반 조명(PBR). 이 부분의 이론과 실습은 저자의 다른 기술 기사인 "Learning the Ingredients and Implement of PBR from Simple to Deep"을 참조하세요.
후처리가 많이 필요합니다.
1~5개의 실시간 조명과 1개의 사전 계산된 라이트 프로브.
피부 렌더링 프로세스는 다음 단계로 추상화할 수 있습니다.
- 피부 반사.
직접 반사 부분은 Cook-Torrance의 BRDF 공식을 사용합니다.
구체적인 분석 및 구현에 대해서는 "단순에서 심층까지 PBR의 원리 및 구현 학습"의 3.1.3장 반영 방정식을 참조하세요.
UE의 스킨 렌더링은 Dual Lobe Specular를 사용합니다. Dual Specular Specular는 최종 결과를 형성하기 위해 결합된 두 개의 개별 반사 로브에 대한 러프니스 값을 제공합니다. 두 가지가 결합되면 피부에 탁월한 하위 픽셀 마이크로 주파수 효과를 제공하여 자연스러운 모습을 선사합니다.

UE의 기본 혼합 공식은 다음과 같습니다.
아래 이미지는 UE4 블렌딩 프로세스와 최종 이미징을 보여줍니다.

왼쪽: 더 부드러운 하이라이트 레이어 Lobe1; 중간: 더 강한 하이라이트 레이어 Lobe2; 오른쪽: 최종 혼합 이미지
- 간접반사 부분에서는 preconvolution 큐브맵을 사용합니다.
구체적인 분석 및 구현에 대해서는 "단순에서 심층까지 PBR의 원리 및 구현 학습"의 3.3.2장 Specular IBL을 참조하세요.
- 피부 모공.
피부 모공의 내부 구조는 매우 복잡하여 반사(하이라이트 및 디퓨즈), 그림자, 폐색, 표면 아래 산란 및 기타 효과를 유발합니다.

사람의 모공을 확대한 사진, 내부 구조가 매우 복잡하여 조명 정보가 매우 복잡함
기공 디테일을 렌더링할 때 많은 디테일에 주의를 기울여야 합니다. 그렇지 않으면 렌더링 결과가 불쾌한 계곡 이론에 빠지게 됩니다.
이론적으로 물리적으로 사실적인 렌더링에 가까운 기공의 렌더링 공식은 다음과 같습니다.
그 중에는:

는 하이라이트 항목을 나타냅니다. 은 시각적 각도와 관련된 반사입니다.
그러나 이러한 물리적 현실은 우울증을 너무 뚜렷하고 시각적으로 보기 흉하며 약간 불편하게 만듭니다.

하이라이트와 캐비티의 위치를 미세 조정하면 다음과 같은 렌더링 결과를 얻을 수 있습니다.

위 사진에서 볼 수 있듯이 하이라이트가 너무 강하고, 함몰된 부분의 디테일이 부족하며, 피부 렌더링 결과가 현실적이지 않습니다.
실제로 물리적으로 완전히 실제적인 원리를 버리고 근사치를 사용할 수 있습니다.
마지막으로 현실과 아름다움의 균형을 이루는 그림이 렌더링될 수 있습니다.

UE4는 디퓨즈 + 러프니스 + 하이라이트 + 산란 + 노멀 맵의 조합을 사용하여 피부 디테일을 매우 정밀하게 복원합니다.

왼쪽에서 오른쪽으로: 확산, 러프니스, 반사, 산란, 노멀 맵
구체적인 조명 프로세스는 Cook-Torrance의 BRDF와 거의 동일하므로 여기서는 자세히 설명하지 않겠습니다.
- 전역 조명.
피부의 전역 조명은 이미지 기반 조명(IBL)과 향상된 AO를 결합한 결과입니다.
IBL 기술에 대해서는 3.3 이미지 기반 조명(IBL)을 참조하십시오.

위 사진: 글로벌 일루미네이션이 겹쳐졌으나 AO가 없는 사진
AO 부분은 SSAO(Screen Space Ambient Occlusion)로, AO 맵에 Bleed Color(피부는 일반적으로 빨간색)가 혼합되어 있습니다.

빨간색 Bleed Color AO를 추가하여 피부 렌더링을 더욱 적절하게 만들고, 어두운 피부 영역의 밝기와 색상을 더욱 사실적이고 아름답게 표현합니다.
- 지하 산란(BSSRDF).
이 부분은 2.2에서 자세히 설명한다.
2.2 서브서피스 스캐터링(SSS)
서브서피스 스캐터링(SSS)(Subsurface Scattering)은 피부, 옥, 우유 등 반투명 재질을 시뮬레이션하는 물리적 렌더링 기술입니다.

일반 BRDF와 다른 점은 동일한 입사광이 반투명 물질에 들어간 후 내부에서 여러 번 산란되고 최종적으로 입사 지점 근처에서 여러 광선이 산란된다는 것입니다.
물질 내 R, G, B의 확산 곡선이 다르기 때문에 입사된 빛과 다른 색이 나타납니다.

빨간색 빛은 투과력이 강해 피부 조직에 더 쉽게 침투하여 붉은색 빛을 형성합니다.
2.2.1 BSSRDF
BSSRDF는 입사광이 재료 내부에서 여러 번 산란된 후 다시 반사되는 빛을 충분히 고려한 서브서피스 스캐터링(SSS)(Subsurface Scattering)을 기반으로 하는 조명 모델입니다.

왼쪽: BRDF; 오른쪽: BSSRDF, 입력 빛이 재료에 산란된 후 다시 방출되는 여러 줄의 빛을 고려

위 그림은 BRDF, BTDF 및 BSSRDF 간의 관계를 설명합니다.
BRDF: 양방향 반사 분포 함수로 매질 입사점에서 반사 조명 모델을 표현하는 데 사용됩니다.
BTDF: 빛이 매체를 통과한 후 조명 모델을 설명하는 데 사용되는 양방향 전송 분포 함수입니다.
BSSRDF: 매체 내부 입사광의 조명 모델을 설명하는 데 사용되는 양방향 표면 아래 반사 분포 함수입니다.
BSDF = BRDF + BTDF.
- BSSRDF는 BSDF의 업그레이드 버전입니다.
다음 두 그림은 BRDF와 BSSRDF를 사용한 스킨 렌더링 결과를 보여줍니다.

BRDF 조명 모델로 렌더링된 피부

BSSRDF 조명 모델로 렌더링된 피부
BSSRDF로 렌더링한 피부 효과가 더욱 현실적이고 아름다워 언캐니 밸리 효과에 빠지는 것을 방지할 수 있음을 알 수 있습니다.
BRDF 방정식을 다시 살펴보면 빛 교차점의 노멀 반구에 대한 구면 적분으로 반사 조명을 계산합니다.
BSSRDF의 경우 물체 표면의 각 위치에서 각 반사에 대해 반구 면적 적분이 필요하며 이는 중첩된 적분입니다.
그 중에는:
는 BSSRDF의 정의가 나가는 빛의 휘도와 입사 광속의 비율임을 보여줍니다. 는 프레넬 전송 효과입니다. 는 디퓨즈율로 입사점과 출사점 사이의 거리와 관련이 있습니다.
그 중에는:
는 프레넬 반사 항입니다. 는 지점에서의 서브서피스 스캐터링(SSS) 함수입니다. 다음과 같이 더욱 단순화할 수 있습니다.
즉,
단순화된

위 그림은
해당
는 배율 인수가 있는 프레넬 항이며 공식은 다음과 같습니다.
여기서
BSSRDF 공식의 보다 구체적인 이론, 유도 및 단순화 과정에 대해서는 다음 두 논문을 참조하십시오.
지하 조명 전송을 위한 실제 모델
BSSRDF Explorer: BSSRDF용 렌더링 프레임워크
2.2.2 서브서피스 스캐터링(SSS)의 공간적 흐림
하위 표면 산란은 기본적으로 특수 가우시안 블러와 유사하게 가중치 계산을 위해 주변 픽셀을 샘플링합니다. 즉, 서브서피스 스캐터링(SSS) 계산은 두 부분으로 나눌 수 있습니다.
(1) 먼저 각 픽셀에 대해 일반적인 디퓨즈 계산을 수행합니다.
(2) 그런 다음 특수 함수
위의 (2)에서 언급된
실제로 서브서피스 스캐터링(SSS)은 모든 재질에 존재합니다. 유일한 차이점은 밀도 분포 함수
확산 프로파일 기술로 시뮬레이션된 서브서피스 스캐터링(SSS)을 사용하여 더 부드러운 피부 질감을 얻으려면 이미지에 다양한 매개변수를 사용하여 여러 가우시안 블러를 적용해야 합니다. 퍼지 공간 분할에는 두 가지 방법이 있습니다.
텍스처 공간 흐림(텍스처 공간 흐림). 피부 내 산란의 국지적 특성을 활용하여 3D 메쉬를 텍스처 좌표를 렌더링 좌표로 사용하여 펼쳐 2D 텍스처에서 효율적으로 시뮬레이션합니다.
화면 공간 흐림(화면 공간 흐림). 텍스처 공간과 달리 SSSSS(Screen Space SubSurface Scattering)라고도 알려진 화면 공간에서 흐려집니다.

텍스처 공간과 화면 공간에서 0, 3, 5배 가우시안 블러의 결과

위: 화면 공간 지하 산란 렌더링 프로세스
2.2.3 분리 가능한 지하 산란
서브서피스 스캐터링(SSS) 블러에 대한 컨볼루션 분리(Separable Convolution)의 최적화 방법이 있습니다. 구체적으로, 가로 좌표 U와 세로 좌표 V는 별도로 컨볼루션된 후 합성됩니다.

이로 인해 현재 UE에서 사용하는 인간 피부 렌더링 방법이기도 한 분리 가능한 하위 표면 산란(SSSS 또는 4S라고도 함)이 탄생했습니다.
구체적인 파생 프로세스에 대해서는 분리 가능한 지하 산란을 참조하세요.
또한 이 문서에서는 실시간 렌더링을 가속화하려면 사전 통합된 분리 가능한 커널도 필요하다고 언급했습니다.
SVD(Singular Value Decomposition) 방법을 사용하여 행 벡터와 열 벡터**로 분해하며, 분해된 표현 방법은 기본적으로 에너지 손실이 없음을 보장합니다. 아래 그림은 계산 과정을 보여줍니다.

2.3 UE 기본 구현
이 섹션에서는 UE의 C++ 및 셰이더 소스 코드에서 스킨 렌더링 구현을 분석합니다. UE 소스 코드를 다운로드하는 구체적인 단계는 공식 문서인 Unreal Engine 소스 코드 다운로드를 참조하세요.
**완벽한 공유 정신을 보여준 Epic Game에 다시 한번 엄지손가락을 치켜세웁니다!**UE의 오픈 소스를 통해 엔진의 내부 구현을 엿볼 수 있습니다. 더 이상 블랙박스 작업이 아닙니다. 또한 개인, 프로젝트 및 회사에 큰 도움이 되는 그래픽 렌더링에 대한 지식을 배울 수 있는 기회도 제공합니다.
피부 렌더링에는 여러 가지 방법이 있습니다. UE는 분리 가능한 지하 산란(SSSS 또는 4S라고도 함)을 사용합니다. 먼저 Blizzard의 Jorge 등은 GDC2013 연설 "Next-Generation Character Rendering"에서 처음으로 SSSS의 렌더링을 선보였으며, 2015년 논문을 통해 Separable Subsurface Scattering을 공식적으로 제안했습니다. 두 개의 패스를 수평 및 수직으로 컨벌루션하여 근사화하고 효율성이 더욱 향상되었습니다. 현재 게임에 사용되는 주류 기술이다.
UE 소스 코드에서 SSSS와 관련된 주요 파일(저자는 UE 4.22를 사용하고 있으며 버전에 따라 다를 수 있음):
- \Engine\Shaders\Private\SeparableSSS.ush:
SSSS 셰이더의 주요 구현입니다.
- \Engine\Shaders\Private\PostProcessSubsurface.usf:
사후 처리 단계에서는 SeparableSSS.ush에 대한 데이터 및 도구 인터페이스 구현을 제공합니다.
- \Engine\Shaders\Private\SubsurfaceProfileCommon.ush:
SSSS의 상수와 구성을 정의합니다.
- \Engine\Source\Runtime\Engine\Private\Rendering\SeparableSSS.cpp:
오프라인 계산에 사용할 수 있는 확산 프로파일, 가우시안 블러, 전송 프로파일과 같은 로직의 CPU 버전을 구현합니다.
- \Engine\Source\Runtime\Engine\Private\Rendering\SubsurfaceProfile.cpp:
SSS 프로필 관리, 텍스처 생성 및 SSSS와의 상호 작용.
2.3.1 SeparableSSS.ush
SeparableSSS.ush는 SSSS를 구현하는 기본 셰이더 파일입니다. 먼저 픽셀 셰이더 코드를 분석합니다. (다음 인터페이스 중 일부는 다른 파일에 정의되어 있습니다. 이름으로 일반적인 의미를 알 수 있습니다. 내부 구현 세부 사항에 신경 쓸 필요가 없으며 핵심 렌더링 알고리즘 분석을 방해하지 않습니다.)
// BufferUV: 텍스처(Texture)좌표 ,로부터 GBuffer 내에서 데이터 ;
// dir: 블러(Blur)。개 passfloat2(1.0, 0.0),테이블 블러(Blur);개 passfloat2(0.0, 1.0),테이블 블러(Blur)。는 “”의 최적화 。
// initStencil:는 초기화(Initialize)스텐실 버퍼(Stencil Buffer)。개 pass로 true,로써 개 pass최적화 。
float4 SSSSBlurPS(float2 BufferUV, float2 dir, bool initStencil)
{
// Fetch color of current pixel:
// SSSSSampleSceneColorPoint 및 SSSSSampleSceneColor는 페치/가져오기(Fetch)2.2.2단계/스텝 (1) 내에서 까지 의 계산/산출(Calculate)의 디퓨즈(Diffuse)컬러
float4 colorM = SSSSSampleSceneColorPoint(BufferUV);
// we store the depth in alpha
float OutDepth = colorM.a;
colorM.a = ComputeMaskFromDepthInAlpha(colorM.a);
// 에 따라 는 반환(Return),의 회 테이블 산란 계산/산출(Calculate)。
BRANCH if(!colorM.a)
{
// todo: need to check for proper clear
// discard;
return 0.0f;
}
// 0..1
float SSSStrength = GetSubsurfaceStrength(BufferUV);
// Initialize the stencil buffer in case it was not already available:
if (initStencil) // (Checked in compile time, it's optimized away)
if (SSSStrength < 1 / 256.0f) discard;
float SSSScaleX = SSSParams.x;
float scale = SSSScaleX / OutDepth;
// 계산/산출(Calculate)샘플링픽셀(Pixel)의
float2 finalStep = scale * dir;
// ideally this comes from a half res buffer as well - there are some minor artifacts
finalStep *= SSSStrength; // Modulate it using the opacity (0..1 range)
FGBufferData GBufferData = GetGBufferData(BufferUV);
// 0..255, which SubSurface profile to pick
// ideally this comes from a half res buffer as well - there are some minor artifacts
uint SubsurfaceProfileInt = ExtractSubsurfaceProfileInt(GBufferData);
// Accumulate the center sample:
float3 colorAccum = 0;
// 초기화(Initialize)로 ,는 를 위해 。
float3 colorInvDiv = 0.00001f;
// 내에서 포인트 샘플링
colorInvDiv += GetKernel(SSSS_N_KERNELWEIGHTOFFSET, SubsurfaceProfileInt).rgb;
colorAccum = colorM.rgb * GetKernel(SSSS_N_KERNELWEIGHTOFFSET, SubsurfaceProfileInt).rgb;
// 。
float3 BoundaryColorBleed = GetProfileBoundaryColorBleed(GBufferData);
// 픽셀(Pixel)의 샘플링,즉, 회 테이블 산란 의 계산/산출(Calculate),는 및 거리의 의 블러(Blur)
// SSSS_N_KERNELWEIGHTCOUNT는 개수 , 및 구성(Configuration),는 6、9、13。에 의해 r.SSS.SampleSet설정(Set)。
SSSS_UNROLL
for (int i = 1; i < SSSS_N_KERNELWEIGHTCOUNT; i++)
{
// Kernel는 컨볼루션 커널(Convolution Kernel),컨볼루션 커널(Convolution Kernel)의 가중치(Weight)에 의해 (Diffusion Profile),컨볼루션 커널(Convolution Kernel)의 크기 이면 에 따라 현재(Current)픽셀(Pixel)의 뎁스 (d(x,y)) 및 (dFdx(d(x,y)) 및 dFdy(d(x,y)))。는 에 따라 Subsurface Profile파라미터 계산/산출(Calculate)의 。
// Kernel.rgb는 컬러 채널/패스(Pass)의 가중치(Weight);Kernel.a는 샘플링위치 ,범위 는 0~SUBSURFACE_KERNEL_SIZE(즉, 회 테이블 산란 의 )
half4 Kernel = GetKernel(SSSS_N_KERNELWEIGHTOFFSET + i, SubsurfaceProfileInt);
float4 LocalAccum = 0;
float2 UVOffset = Kernel.a * finalStep;
// 에 의해 컨볼루션 커널(Convolution Kernel)는 의 ,따라서 로써 샘플링 내에서 의 포인트 의 컬러 계산/산출(Calculate)。GetKernel호출(Call),가중치(Weight)계산/산출(Calculate)。
SSSS_UNROLL
// Side의 는 -1 및 1,BufferUV + UVOffset * Side,즉, 샘플링 내에서 포인트 의 포인트 처리(Process)。
for (int Side = -1; Side <= 1; Side += 2)
{
// Fetch color and depth for current sample:
float2 LocalUV = BufferUV + UVOffset * Side;
float4 color = SSSSSampleSceneColor(LocalUV);
uint LocalSubsurfaceProfileInt = SSSSSampleProfileId(LocalUV);
float3 ColorTint = LocalSubsurfaceProfileInt == SubsurfaceProfileInt ? 1.0f : BoundaryColorBleed;
float LocalDepth = color.a;
color.a = ComputeMaskFromDepthInAlpha(color.a);
#if SSSS_FOLLOW_SURFACE == 1
// 에 따라 OutDepth 및 LocalDepth의 뎁스 보정(Correct)회 테이블 산란 ,만약 ,회 테이블 산란 。
float s = saturate(12000.0f / 400000 * SSSParams.y *
// float s = saturate(300.0f/400000 * SSSParams.y *
abs(OutDepth - LocalDepth));
color.a *= 1 - s;
#endif
// approximation, ideally we would reconstruct the mask with ComputeMaskFromDepthInAlpha() and do manual bilinear filter
// needed?
color.rgb *= color.a * ColorTint;
// Accumulate left and right
LocalAccum += color;
}
// 에 의해 내에서 샘플링 포인트의 가중치(Weight)는 의 ,colorAccum 및 colorInvDiv*2,그러나 colorAccum / colorInvDiv,따라서 *2로써 。
colorAccum += Kernel.rgb * LocalAccum.rgb;
colorInvDiv += Kernel.rgb * LocalAccum.a;
}
// 컬러 가중치(Weight) 및 뎁스 가중치(Weight),로써 정규화 ,,컬러 。(뎁스 정보 또는 SSS의 머티리얼(Material),샘플링!)
float3 OutColor = colorAccum / colorInvDiv;
// alpha stored the SceneDepth (0 if there is no subsurface scattering)
return float4(OutColor, OutDepth);
}이 파일에도 SSSSTransmittance가 있는데 작성자가 UE 소스코드 프로젝트 전체를 검색해본 결과 사용되지 않은 것으로 보여 당분간 분석은 하지 않을 예정입니다. 아래에는 소스 코드만 게시되어 있습니다.
//-----------------------------------------------------------------------------
// Separable SSS Transmittance Function
// @param translucency This parameter allows to control the transmittance effect. Its range should be 0..1. Higher values translate to a stronger effect.
// @param sssWidth this parameter should be the same as the 'SSSSBlurPS' one. See below for more details.
// @param worldPosition Position in world space.
// @param worldNormal Normal in world space.
// @param light Light vector: lightWorldPosition - worldPosition.
// @param lightViewProjection Regular world to light space matrix.
// @param lightFarPlane Far plane distance used in the light projection matrix.
float3 SSSSTransmittance(float translucency, float sssWidth, float3 worldPosition, float3 worldNormal, float3 light, float4x4 lightViewProjection, float lightFarPlane)
{
/**
* Calculate the scale of the effect.
*/
float scale = 8.25 * (1.0 - translucency) / sssWidth;
/**
* First we shrink the position inwards the surface to avoid artifacts:
* (Note that this can be done once for all the lights)
*/
float4 shrinkedPos = float4(worldPosition - 0.005 * worldNormal, 1.0);
/**
* Now we calculate the thickness from the light point of view:
*/
float4 shadowPosition = SSSSMul(shrinkedPos, lightViewProjection);
float d1 = SSSSSampleShadowmap(shadowPosition.xy / shadowPosition.w).r; // 'd1' has a range of 0..1
float d2 = shadowPosition.z; // 'd2' has a range of 0..'lightFarPlane'
d1 *= lightFarPlane; // So we scale 'd1' accordingly:
float d = scale * abs(d1 - d2);
/**
* Armed with the thickness, we can now calculate the color by means of the
* precalculated transmittance profile.
* (It can be precomputed into a texture, for maximum performance):
*/
float dd = -d * d;
float3 profile = float3(0.233, 0.455, 0.649) * exp(dd / 0.0064) +
float3(0.1, 0.336, 0.344) * exp(dd / 0.0484) +
float3(0.118, 0.198, 0.0) * exp(dd / 0.187) +
float3(0.113, 0.007, 0.007) * exp(dd / 0.567) +
float3(0.358, 0.004, 0.0) * exp(dd / 1.99) +
float3(0.078, 0.0, 0.0) * exp(dd / 7.41);
/**
* Using the profile, we finally approximate the transmitted lighting from
* the back of the object:
*/
return profile * saturate(0.3 + dot(light, -worldNormal));
}2.3.2 SeparableSSS.cpp
SeparableSSS.cpp 테마는 확산 프로필, 전송 프로필, 가우시안 흐림 계산 및 미러 컨볼루션 커널의 사전 계산을 제공합니다.
소스코드를 더 잘 이해하기 위해 먼저 몇 가지 전제 지식을 소개하겠습니다.
2.3.2.1 가우스 합 확산 프로파일
확산 프로파일의 시뮬레이션은 여러 가우스 합 함수로 시뮬레이션할 수 있으며, 여기서 가우스 함수의 공식은 다음과 같습니다.
아래 그림은 단일 가우시안 합계의 확산 프로파일 플롯입니다.

R, G, B의 확산 거리가 다르며 단일 가우스 함수로는 복잡한 인간 피부 확산 프로파일을 정확하게 시뮬레이션할 수 없음을 알 수 있습니다.
실습을 통해 함께 분포된 여러 가우스가 확산 프로파일에 대한 탁월한 근사치를 제공할 수 있음이 나타났습니다. 그리고 가우시안 함수는 분리 가능하면서도 동시에 방사상 대칭을 이루고, 서로 컨볼루션되어 새로운 가우스 함수를 생성할 수 있다는 점에서 독특합니다.
각 확산 분포
그리고 가우스 함수의 분산
대부분의 투명한 물체(우유, 대리석 등)의 경우 하나의 쌍극자 프로필이면 충분합니다. 그러나 피부와 같은 다층 구조의 재료의 경우 하나의 Dipole Profile로는 이상적인 효과를 얻을 수 없습니다. 3개의 쌍극자를 사용하여 Jensen의 논문 측정에서 얻은 피부 프로필 데이터를 근사화할 수 있습니다.
실험에 따르면 다음 6개의 가우스 함수를 피팅하여 3개의 쌍극자 곡선을 얻을 수 있는 것으로 나타났습니다(구체적인 피팅 파생 프로세스에 대해서는 "GPU Gems 3": 사실적인 피부 렌더링 기술 요약 참조).
위의 공식은 빨간색 채널 Red의 시뮬레이션입니다. 녹색 채널 Green과 파란색 채널 Blue의 매개변수는 다릅니다. 아래 표를 참조하십시오.

R, G, B 채널에 맞춰진 곡선은 서로 다릅니다(아래 그림). R 채널 곡선의 확산 범위가 가장 넓어 피부가 붉게 나타나는 것을 볼 수 있습니다.

2.3.2.2 소스 코드 분석
먼저 SeparableSSS_Gaussian을 분석합니다.
// 개 는 까지 의 G(v,r)의 가우시안(Gaussian)함수 ,추가(Add)FalloffColor컬러 ,컬러 채널/패스(Pass)의 。
inline FVector SeparableSSS_Gaussian(float variance, float r, FLinearColor FalloffColor)
{
FVector Ret;
// 개 컬러 채널/패스(Pass)회 가우시안(Gaussian)함수
for (int i = 0; i < 3; i++)
{
float rr = r / (0.001f + FalloffColor.Component(i));
Ret[i] = exp((-(rr * rr)) / (2.0f * variance)) / (2.0f * 3.14f * variance);
}
return Ret;
}SeparableSSS_Profile을 다시 분석합니다.
// ,는 까지 의 6개 가우시안(Gaussian)함수 까지 3개 dipole의 공식 ?파라미터 ?
// 내에서 r는 회 테이블 산란 의 거리,는 mm,에 의해 UE의 Subsurface Profile설정(Set)。
inline FVector SeparableSSS_Profile(float r, FLinearColor FalloffColor)
{
// ※ 주의: 의 는 ,UE4R、G、B채널/패스(Pass)의 파라미터 을(를) 활용하여 R채널/패스(Pass)의 파라미터 ,의 에 의해 는 FalloffColor의 ,의 머티리얼(Material)。
return // 0.233f * SeparableSSS_Gaussian(0.0064f, r, FalloffColor) + // UE4개 가우시안(Gaussian)함수 ,에 의해 는 개 는 반사(Reflection),strength파라미터 。(We consider this one to be directly bounced light, accounted by the strength parameter)
0.100f * SeparableSSS_Gaussian(0.0484f, r, FalloffColor) +
0.118f * SeparableSSS_Gaussian(0.187f, r, FalloffColor) +
0.113f * SeparableSSS_Gaussian(0.567f, r, FalloffColor) +
0.358f * SeparableSSS_Gaussian(1.99f, r, FalloffColor) +
0.078f * SeparableSSS_Gaussian(7.41f, r, FalloffColor);
}다음으로 위 인터페이스를 사용하여 커널 오프라인 가중치를 계산하는 방법을 분석합니다.
// 에 의해 가우시안(Gaussian)함수 、 내에서 ,따라서 및 ,의 데이터 계산/산출(Calculate)。
void ComputeMirroredSSSKernel(FLinearColor* TargetBuffer, uint32 TargetBufferSize, FLinearColor SubsurfaceColor, FLinearColor FalloffColor)
{
check(TargetBuffer);
check(TargetBufferSize > 0);
uint32 nNonMirroredSamples = TargetBufferSize;
int32 nTotalSamples = nNonMirroredSamples * 2 - 1;
// we could generate Out directly but the original code form SeparableSSS wasn't done like that so we convert it later
// .A is in mm
check(nTotalSamples < 64);
FLinearColor kernel[64];
{
// 컨볼루션 커널(Convolution Kernel)개 기본값(Default)의 범위 ,,에 따라 nTotalSamples개수 조정(Adjust)Range는 의 。(는 mm)
const float Range = nTotalSamples > 20 ? 3.0f : 2.0f;
// tweak constant
const float Exponent = 2.0f;
// Calculate the offsets:
float step = 2.0f * Range / (nTotalSamples - 1);
for (int i = 0; i < nTotalSamples; i++)
{
float o = -Range + float(i) * step;
float sign = o < 0.0f ? -1.0f : 1.0f;
// 현재(Current)의 range 및 의 Range의 alpha채널/패스(Pass),로써 shader 내에서 적용 。
kernel[i].A = Range * sign * FMath::Abs(FMath::Pow(o, Exponent)) / FMath::Pow(Range, Exponent);
}
// 계산/산출(Calculate)Kernel가중치(Weight)
for (int32 i = 0; i < nTotalSamples; i++)
{
// i의 .A블러(Blur),area
float w0 = i > 0 ? FMath::Abs(kernel[i].A - kernel[i - 1].A) : 0.0f;
float w1 = i < nTotalSamples - 1 ? FMath::Abs(kernel[i].A - kernel[i + 1].A) : 0.0f;
float area = (w0 + w1) / 2.0f;
// 블러(Blur)의 가중치(Weight) 및 6개 가우시안(Gaussian)함수 의 결과 ,RGB의 가중치(Weight)。
FVector t = area * SeparableSSS_Profile(kernel[i].A, FalloffColor);
kernel[i].R = t.X;
kernel[i].G = t.Y;
kernel[i].B = t.Z;
}
// offset로 0.0(즉, 내에서 샘플링 포인트)의 까지 위치 0.
FLinearColor t = kernel[nTotalSamples / 2];
for (int i = nTotalSamples / 2; i > 0; i--)
{
kernel[i] = kernel[i - 1];
}
kernel[0] = t;
// 정규화 가중치(Weight),가중치(Weight) 및 로 1,컬러 .
{
FVector sum = FVector(0, 0, 0);
for (int i = 0; i < nTotalSamples; i++)
{
sum.X += kernel[i].R;
sum.Y += kernel[i].G;
sum.Z += kernel[i].B;
}
for (int i = 0; i < nTotalSamples; i++)
{
kernel[i].R /= sum.X;
kernel[i].G /= sum.Y;
kernel[i].B /= sum.Z;
}
}
/* we do that in the shader for better quality with half res
// Tweak them using the desired strength. The first one is:
// lerp(1.0, kernel[0].rgb, strength)
kernel[0].R = FMath::Lerp(1.0f, kernel[0].R, SubsurfaceColor.R);
kernel[0].G = FMath::Lerp(1.0f, kernel[0].G, SubsurfaceColor.G);
kernel[0].B = FMath::Lerp(1.0f, kernel[0].B, SubsurfaceColor.B);
for (int i = 1; i < nTotalSamples; i++)
{
kernel[i].R *= SubsurfaceColor.R;
kernel[i].G *= SubsurfaceColor.G;
kernel[i].B *= SubsurfaceColor.B;
}*/
}
// 가중치(Weight)결과 출력까지 TargetBuffer,결과 。
{
check(kernel[0].A == 0.0f);
// center sample
TargetBuffer[0] = kernel[0];
// all positive samples
for (uint32 i = 0; i < nNonMirroredSamples - 1; i++)
{
TargetBuffer[i + 1] = kernel[nNonMirroredSamples + i];
}
}
}이 파일은 ComputeTransmissionProfile도 구현합니다.
void ComputeTransmissionProfile(FLinearColor* TargetBuffer, uint32 TargetBufferSize, FLinearColor SubsurfaceColor, FLinearColor FalloffColor, float ExtinctionScale)
{
check(TargetBuffer);
check(TargetBufferSize > 0);
static float MaxTransmissionProfileDistance = 5.0f; // See MAX_TRANSMISSION_PROFILE_DISTANCE in TransmissionCommon.ush
for (uint32 i = 0; i < TargetBufferSize; ++i)
{
//10 mm
const float InvSize = 1.0f / TargetBufferSize;
float Distance = i * InvSize * MaxTransmissionProfileDistance;
FVector TransmissionProfile = SeparableSSS_Profile(Distance, FalloffColor);
TargetBuffer[i] = TransmissionProfile;
//Use Luminance of scattering as SSSS shadow.
TargetBuffer[i].A = exp(-Distance * ExtinctionScale);
}
// Do this is because 5mm is not enough cool down the scattering to zero, although which is small number but after tone mapping still noticeable
// so just Let last pixel be 0 which make sure thickness great than MaxRadius have no scattering
static bool bMakeLastPixelBlack = true;
if (bMakeLastPixelBlack)
{
TargetBuffer[TargetBufferSize - 1] = FLinearColor::Black;
}
}ComputeMirroredSSSKernel 및 ComputeTransmissionProfile은 FSubsurfaceProfileTexture::CreateTexture에서 트리거되며, 후자는 레벨이 로드되거나 편집기가 작동될 때(즉, 사전 계산된 비런타임 계산) 트리거됩니다.
void FSubsurfaceProfileTexture::CreateTexture(FRHICommandListImmediate& RHICmdList)
{
// ... (의 처리(Process)코드 )
for (uint32 y = 0; y < Height; ++y)
{
// ... (의 처리(Process)코드 )
// 에 따라 r.SSS.SampleSet의 (0、1、2),3개 해상도/크기 의 가중치(Weight)。
ComputeMirroredSSSKernel(&TextureRow[SSSS_KERNEL0_OFFSET], SSSS_KERNEL0_SIZE, Data.SubsurfaceColor, Data.FalloffColor);
ComputeMirroredSSSKernel(&TextureRow[SSSS_KERNEL1_OFFSET], SSSS_KERNEL1_SIZE, Data.SubsurfaceColor, Data.FalloffColor);
ComputeMirroredSSSKernel(&TextureRow[SSSS_KERNEL2_OFFSET], SSSS_KERNEL2_SIZE, Data.SubsurfaceColor, Data.FalloffColor);
// 계산/산출(Calculate)투과 。
ComputeTransmissionProfile(&TextureRow[SSSS_TRANSMISSION_PROFILE_OFFSET], SSSS_TRANSMISSION_PROFILE_SIZE, Data.SubsurfaceColor, Data.FalloffColor, Data.ExtinctionScale);
// ...(의 처리(Process)코드 )
}
}2.3.3 PostProcessSubsurface.ush
이 파일은 SeparableSSS.ush에 대한 많은 수의 인터페이스와 변수를 정의하며 SeparableSSS를 호출하는 사용자입니다.
// .... (코드 )
#include "SeparableSSS.ush"
// .... (코드 )
// input0 is created by the SetupPS shader
void MainPS(noperspective float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0)
{
float2 BufferUV = UVAndScreenPos.xy;
#if SSS_DIRECTION == 0
// horizontal
float2 ViewportDirectionUV = float2(1, 0) * SUBSURFACE_RADIUS_SCALE;
#else
// vertical
float2 ViewportDirectionUV = float2(0, 1) * SUBSURFACE_RADIUS_SCALE * (View.ViewSizeAndInvSize.x * View.ViewSizeAndInvSize.w);
#endif
#if MANUALLY_CLAMP_UV
ViewportDirectionUV *= (View.ViewSizeAndInvSize.x * View.BufferSizeAndInvSize.z);
#endif
// 회 테이블 산란 컬러
OutColor = SSSSBlurPS(BufferUV, ViewportDirectionUV, false);
#if SSS_DIRECTION == 1
// second pass prepares the setup from the recombine pass which doesn't need depth but wants to reconstruct the color
OutColor.a = ComputeMaskFromDepthInAlpha(OutColor.a);
#endif
}그리고 MainPS를 호출하기 전에 디퓨즈 색상을 다른 코드로 계산하고 나중에 하이라이트 혼합을 수행합니다. 컨볼루션 커널을 미리 계산하기 전에 하이라이트를 혼합하면 렌더링 결과가 좋지 않습니다.

2.3.4 UE 지하 산란의 한계
UE4의 서브서피스 스캐터링(SSS)은 매우 사실적인 피부 렌더링을 향상시킬 수 있지만 다음과 같은 제한 사항도 있습니다(공식 문서: 서브서피스 윤곽 셰이딩 모델에서 추출):
이 기능은 지연되지 않은(모바일) 렌더링 모드에서는 작동하지 않습니다.
대형 화면을 분산 반경으로 설정하면 극단적인 조명 조건에서 밴딩 아티팩트가 표시됩니다.

현재는 조명 후방 산란이 없습니다.
현재 SSS가 아닌 재질이 SSS 재질을 차단하면 회색 윤곽선이 나타납니다. (작성자 테스트 결과 4.22.1은 나타나지 않습니다. 아래 그림 참조)

2.4 피부 물질 분석
이 섹션에서는 Mike의 피부 재질을 분석하기 시작합니다. 스킨 소재는 주로 M_Head 입니다.

스킨 머티리얼 노드 개요
여기에는 표면 아래 산란을 가능하게 하는 셰이딩 모델이 있으며, 추가로 다음과 같이 골격 동작 및 정적 조명이 있는 마커를 사용할 수 있습니다.

2.4.1 기본 색상

Base Color는 4개의 Diffuse Reflection Map(아래)을 입력으로 사용하고 혼합된 결과는 MF_AnimatedMapsMike를 통해 출력된 후 Subsurface Scattering Mask Map(T_head_sss_ao_mask)에서 제어하는 계수로 나누어 최종적으로 Base Color 핀에 입력됩니다.

4개의 디퓨즈 맵은 각각 다른 동작 상태의 맵을 나타냅니다.
그 중 MF_AnimatedMapsMike는 다양한 액션에 따라 텍스처 혼합 가중치를 내부적으로 제어하는 일반적인 머티리얼 함수입니다. 서로 다른 액션 매개변수를 혼합하는 세 가지 머티리얼 함수는 m_headMask_01, m_headMask_02, m_headMask_03입니다:

세 가지 재질 함수 m_headMask_01, m_headMask_02 및 m_headMask_03은 각각 얼굴 Blend Shape 동작 세트를 제어하며, 그 중 m_headMask_01이 연구 개체입니다.

위 그림에서 볼 수 있듯이 m_headMask_01에는 5개의 텍스처(head_wm1_msk_01 ~ head_wm1_msk_04, head_wm13_msk_03)가 있으며 총 19개의 채널(head_wm1_msk_04의 알파 채널은 사용되지 않음)을 사용하여 19개의 블렌드 셰이프 마스크 그룹을 제공하고 해당 매개변수와 상호 작용합니다.
또한 m_headMask_02에는 10개의 Blend Shape 작업을 제어하는 3개의 텍스처가 있습니다. m_headMask_03에는 12개의 Blend Shape 동작을 제어하는 3개의 텍스처가 있습니다.
마스크 데이터와 블렌드 셰이프 매개변수를 계산하는 방법은 fn_maskDelta_xx를 입력하여 알아내야 합니다. 다음은 fn_maskDelta_01을 예로 사용합니다.

수많은 재료 노드로 인해 혼동하지 마십시오. 실제로 각 블렌드 셰이프 마스크에 해당 매개변수를 곱한 다음 결과를 다른 매개변수에 추가하고 마지막으로 결과를 출력합니다. 공식으로 요약하면 다음과 같습니다.
그 중
2.4.2 반사광

하이라이트는 주로 Mike_head_cavity_map_001의 R 채널에서 제공됩니다. Power 및 Lerp를 통해 강도와 범위를 조정한 후 Fresnel 노드를 통해 캐릭터 가장자리의 스페큘러 반사가 향상됩니다(아래 그림).

위 결과는 하이라이트와 BaseSpecularValue를 제어하기 위해 T_head_sss_ao_mask 맵의 알파 채널에 의해 조정된 후 최종적으로 Specular 핀에 출력됩니다. (아래 사진)

코 부분의 하이라이트는 T_RGB_roughness_02 맵의 R 채널을 통해 원래 값과 0.8 사이에서 보간됩니다.
2.4.3 러프니스
러프니스 계산은 더 복잡하며 여러 부분에서 분석해야 합니다.
2.4.3.1 모션 블렌딩의 러프니스

이 부분은 기본 색상과 비슷합니다. 서로 다른 동작 상태(Toksvig_mesoNormal, Toksvig_mesoNormal1, Toksvig_mesoNormal2, Toksvig_mesoNormal3)의 4개 러프니스 맵을 통해 초기 러프니스 값에 혼합됩니다.
2.4.3.2 마이크로패싯 기반 러프니스

위 그림과 같이 기본 러프니스 BaseRoughness에 Toksvig_mesoNormal의 G 채널을 추가한 후 머티리얼 함수 MF_RoughnessRegionMult를 처리하여 결과를 출력합니다.
그 중 MF_RoughnessRegionMult의 내부 계산은 다음과 같습니다.

즉, 3개의 마스크 맵(head_skin_mask4, T_siren_head_roughmask_02, T_siren_head_roughmask_01)의 10개 채널을 통해 10개 부품의 러프니스를 제어하고, 각 부품의 러프니스는 매개변수 조정을 제공하여 각 부품이
2.4.3.3 러프니스 조정 및 가장자리 러프니스

위 그림에 표시된 것처럼 RoughnessVariation은 Mike_T_specular_neutral의 R 채널을 통해 Rough0과 Rough1 사이를 보간합니다. EdgeRoughness는 프레넬 노드를 통해 캐릭터 관점의 가장자리 거칠기를 향상시킵니다. 그런 다음 이전 두 섹션의 결과에 각각 곱해지고 추가됩니다.
2.4.3.4 마이크로패싯 디테일 향상

위 그림과 같이 텍스처 좌표를 오프셋한 후 microsurface 디테일 맵 skin_h를 사용한 다음 대비를 강화하고 값을
마이크로패싯 세부 맵 skin_h는 다음과 같습니다.

2.4.4 서브서피스 스캐터링(SSS)(불투명도)
우선 머티리얼 셰이딩 모델이 Subsurface Profile인 경우 머티리얼 핀 Opacity의 역할은 더 이상 오브젝트의 투명도를 제어하는 것이 아니라 서브서피스 스캐터링(SSS)을 제어하는 계수가 된다는 점을 설명해야 합니다.

주요 지하 산란 데이터는 T_head_sss_ao_mask 맵(아래)의 G 채널에 의해 제공되며 [ThinScatter, ThickScatter]로 제한됩니다.

지하 산란 마스크 맵. 귀와 코가 가장 강하고, 코와 입이 그 뒤를 잇는다는 것을 알 수 있습니다.
또한, 위쪽 눈꺼풀(UpperLidScatter)과 눈꺼풀(LidScatter)의 서브서피스 스캐터링(SSS) 계수는 T_RGB_roughness_02 맵의 B 채널과 A 채널을 통해 각각 제어됩니다.
2.4.5 정상(정상)

디퓨즈 및 거칠기와 유사하게 법선의 주요 공급자도 4개의 맵으로 제어됩니다.
또한 카메라가 매우 가까울 때 피부 디테일을 높이기 위해 미세 법선이 제공됩니다.

메인 노멀과 마이크로 노멀이 각각 NormalStrength 및 MicroNormalStrength에 의해 크기가 조정된 후(노멀의 z 채널 데이터는 변경되지 않은 상태로 유지됨) 머티리얼 노드 BlendAngleCorretedNormals를 통해 겹쳐지고 최종적으로 표준화되어 노멀 핀에 입력됩니다. (아래 그림 참조)

일반 블렌딩 프로세스를 분석하기 위해 재질 노드 BlendAngleCordictedNormals를 입력할 수도 있습니다.

머티리얼 노드 관점에서 계산 과정은 복잡하지 않습니다. 이를 함수로 변환합니다.
Vector3 BlendAngleCorrectedNormals(Vector3 BaseNormal, Vector3 AdditionalNormal)
{
BaseNormal.b += 1.0;
AdditionalNormal.rg *= -1.0;
float dot = Dot(BaseNormal, AdditionalNormal);
Vector3 result = BaseNormal * dot - AdditionalNormal * BaseNormal.b;
return result;
}게다가 언리얼 엔진 4의 노멀 맵 블렌딩(Normal Map Blending) 기사에서는 더 간단한 블렌딩 방법을 제안합니다:

두 법선의 XY 및 Z 곱셈을 추가하여 혼합된 결과를 얻습니다.
2.4.6 앰비언트 오클루전(AO)

AO 제어는 매우 간단합니다. 텍스처 T_head_sss_ao_mask의 R 채널을 AO 핀에 직접 입력합니다. T_head_sss_ao_mask의 R 채널은 다음과 같습니다.

얼굴 특징 안쪽, 턱, 목, 머리카락이 주변광을 더 많이 차단하는 것을 볼 수 있습니다.
2.5 피부결 연출
앞에서 볼 수 있듯이 피부 렌더링에는 수십 개에 달하는 많은 텍스처가 포함됩니다.

일반적으로 다음 소스에서 생산됩니다.
스캔된 초고화질 텍스처. 디퓨즈, 스페큘러 반사, SSS, 러프니스, 일반 등.
지도를 바꿉니다. 러프니스, 바이노멀, 미세법선 등

러프니스 맵은 노멀 맵에서 전치된 것입니다.
- 마스크 이미지. 다양한 특성을 정밀하게 제어할 수 있도록 신체의 다양한 영역을 표시하는 다이어그램이 많이 있습니다. 출처는 다음과 같습니다:
PS 및 기타 소프트웨어 제작. 이 방법은 가장 전통적이고 이해하기 쉽습니다.
- 플러그인 생성. Blend Shape, 뼈 등의 가중치 정보를 활용하여 자동으로 마스크 맵을 생성합니다.

Blend Shape는 정점의 가중치를 기록하고 해당 UV 영역에서 마스크 맵을 생성할 수 있습니다.
이 기사 시리즈의 다른 부분
언리얼 엔진의 초현실적 휴먼 렌더링 기술 분석 2부 - 안구 렌더링
언리얼 엔진의 초현실적 휴먼 렌더링 기술 분석 3부 - 헤어 렌더링 등
특별 지침
이 시리즈에는 눈알, 머리카락, 기타 신체 부위에 대한 분석도 포함되어 있습니다. 계속.
참고문헌을 작성해주신 분들께 감사드립니다.
📚 참고 자료 및 공식 링크 (References)
- Next-Generation-Character-Rendering (ACM Transactions on Graphics, Vol. 29(5), SIGGRAPH Asia 2010)
- Separable Subsurface Scattering
- Real-Time Realistic Skin Translucency
- 『GPU Gems 3』:렌더링기술요약 및 정리
- 렌더링기술——
- ! 실시간 렌더링(Real-Time Rendering)기술()
- ! 실시간 렌더링(Real-Time Rendering)기술()
- 📖 얕은 것부터 깊은 것까지 PBR의 원리와 구현을 배워보세요.
- Fast subsurface scattering
- BRDF representation and acquisition
- A BSSRDF Model for Efficient Rendering of Fur with Global Illumination
- Parameter Estimation of BSSRDF for Heterogeneous Translucent Materials
- NVIDIAHairWorks“”(1.1 by Tarkan Sarim)
- Q132:PBRT-V3,BSSRDF()(5.6.2、11.4)
- BSSRDF Explorer: A Rendering Framework for the BSSRDF
- BSSRDF Importance Sampling
- A Practical Model for Subsurface Light Transport
- Digital Mike렌더링
- SIGGRAPH 2017|
- NEXT Story S02.03 - (1)
- 언리얼 엔진(Unreal Engine)GDC
- Gaussian Models
- 『Technical Artist —— 라이팅/조명』
- Kim Libreri、 및 Epic Games
- SirenFMX 2018:『』
- Star Wars
- Facial Action Coding System
- 『: 』(Hellblade: Senua's Sacrifice)
- EPIC win: previs to final in five minutes
- Normal Map Blending in Unreal Engine 4