본문 바로가기

Unreal Engine

Subsurface Profile Shading Model in Unreal Engine

Subsurface Profile 셰이딩 모델은 Subsurface 와 그 방식이 비슷하지만, Screen Space에서 Scattering 처리를 수행한다는 점이 다르다. Screen Space 렌더링은 BackScattering은 귀 같은 부분에서만 나타나고, 전체적으로 미묘한 Scattering을 보이는 피부 같은 질감을 더 효과적으로 표현할 수 있는 방법이다.

대략적인 과정은 아래와 같다.

 

  1. Specular 와 같은 view-dependent 한 라이팅 계산을 제외한 Diffuse 연산 결과물을 렌더타겟이 저장한다.
  2. 결과물을 퍼포먼스를 위해 DownSample 하고, 두 개의 post-process pass 로 필터링한다. 이때 사용되는 kernel은 G-buffer 에 저장된 Subsurface Profile 정보에 따라 달라지는데, 여기에는 색상이 있는 가중치들과 Sample position 상세가 들어 있으며, Profile 에 정의된 Scale 정보에 따라 스케일링된다. 한 장면당 총 255 개의 subsurface profile 을 사용할 수 있다.
  3. 필터링 된 결과를 full-resolution 결과물에 블렌딩한다.

View dependent 한 라이팅 결과물과 non-view dependent 라이팅 결과를 분리할 때, Scene Color 의 알파 채널을 사용하기 때문에 64-bit 렌더러를 사용해야 한다. (이것은 r.SceneColorFormat으로 확인할 수 있다)

SceneColorFormat 에 관한 Help

 

더보기

더 이전에는 Screen Space 대신 Texture Space에서 blurring을 수행하기도 했던 듯 하다. 이것에 관한 자세한 내용은 GPU Gems https://developer.nvidia.cn/gpugems/gpugems/part-iii-materials/chapter-16-real-time-approximations-subsurface-scattering 의 16.4 에서 자세히 다루었다. 이 방법은 GBuffer가 없는 환경에서도 사용할 수 있고, 때에 따라 결과물이 더 안정적이지만 아래와 같은 한계가 있다. - 오브젝트의 UV 모양에 따른 제약이 생기며, - Texture 가 가진 해상도 만큼으로 결과물의 퀄리티가 제한되며, - 경우에 따라 보이지 않는 부분 (backface 등)의 처리를 하는데 비용이 낭비될 수 있다.

Subsurface Profile 셰이딩 모델은 현재 아래와 같은 한계점이 있고, 앞으로도 계속 폴리싱 될 예정이라고 한다.

  • non-deferred (mobile) 렌더링 모드에서는 동작하지 않는다.
  • scatter radius 를 지나치게 크게 설정할 경우, 극단적인 라이팅 환경에서 밴딩 현상이 생길 수 있다.
  • 현재 BackScattering 은 구현되어 있지 않다
  • non-SSS 머터리얼이 SSS Material를 가릴 때, 주변에 회색 그래디언트가 생긴다.

여기까지가 Unreal Docs에 적힌 내용을 일부 요약해 본 것이다. 아래부터는 직접 작업하고 검색하면서 알게 된 내용들을 기록해 둔다.

 

Subsurface Profile

Subsurface Profile 은 Subsurface Profile 셰이딩 모델에서 사용하는, 표면의 scattering 에 관한 정보의 모음이다. 이 정보를 토대로 GBuffer에 각 Profile 마다 SubsurfaceProfileTexture를 저장해 두었다가. 이후 Screen Space 처리에서 사용하게 된다.

아티스트가 직접 작성하는 파라미터라고 되어 있지만, 실제로 에셋을 열어봤을 때 나오는 용어들에 대한 지식이 없는 사람이라면 그냥 눈으로 보면서 제일 좋아보이는 임의의 값을 넣게 된다. 그런데 이렇게 하면 특정 라이팅 환경에서 어색해 보일 수 있다. Unreal Docs에도 속시원한 설명이 없어서 직접 알아보았다.

  • Surface Albedo : 여기에서는 ‘표면 반사율’ 을 의미한다.
    이 단어의 의미를 찾아보면 아래와 같다.
    Albedo (/ælˈbiːdoʊ/ al-BEE-doh; from Latin albedo 'whiteness') is the fraction of sunlight that is diffusely reflected by a body. It is measured on a scale from 0 (corresponding to a black body that absorbs all incident radiation) to 1 (corresponding to a body that reflects all incident radiation). Surface albedo is defined as the ratio of radiosity Je to the irradiance Ee (flux per unit area) received by a surface
    주로 태양에서 온 빛 에너지를 지구 표면에서 얼마나 부드럽게(diffusely), 여러 방향으로 반사하였는지 측정하는 데 사용되는 용어인 듯 하다.
    입사한 빛을 표면에서 얼마나 반사하였는지에 대한 비율로, 받아들인 모든 빛을 흡수하는 흑체(BlackBody) 의 경우 0.0, 모든 빛을 반사하는 물체가 1.0 인것과 대응된다.

Mean Free Path, 우리말로는 ‘평균 자유 경로’ 라고 한다.


설명에 따르면 산란원의 밀도가 높을수록 - 단위부피 당 분자 갯수가 많아 더 빽빽할수록, 또는 분자의 직경이 클수록 분자가 충돌 없이 이동할 수 있는 최대 거리가 짧아지므로, Mean Free Path 거리는 짧아진다. 붐비는 지하철에서는 자유롭게 이동할 수 있는 거리가 (몸집이 큰 사람일수록)짧은 것과 같은 원리인 듯 하다.
https://ps.uci.edu/~cyu/p51A/LectureNotes/Chapter3/Chapter3_Diffusion.pdf

 

기체의 경우 압력이 더 높아질수록 짧아진다고 하는데, 이 부분은 우리가 하려는 렌더링과는 무관해 보인다.

이것을 알고 다시 Unreal Docs를 보면, 파라미터의 의미를 더 쉽게 이해할 수 있다.

  • Mean Free Path Distance : 위에서 살펴본 Mean Free Path 의 개념에 가까운 파라미터이다.
    표면 아래로 빛이 나아갈 수 있는 거리이다.
  • Mean Free Path Color : 표면 아래로 빛 입자가 침투한 이후, 각 R, G, B 색상 요소가 나아갈 수 있는 거리의 비율이다.
    이것은 위의 Mean Free Path Distance에 의해 스케일링된다.
    만약 R 값이 G, B 값보다 더 높다면, 표면 안에서 붉은 계열 빛 입자가 다른 노란, 푸른 계열보다 더 멀리 이동할 수 있다는 뜻이고, 이것은 scattering이 일어난 이후의 빛의 색상과 관련되어 있음을 짐작할 수 있다.

Burley Normalized diffusion model

이 파라미터들의 기원은 아래 문서에서 찾아볼 수 있다.

https://graphics.pixar.com/library/ApproxBSSRDF/approxbssrdfslides.pdf

 

문서 내용을 요약하면 아래와 같다.

  • Burley 의 Reflectance Model은 기존 Monte-Carlo 모델을 적절하게 근사한 반사 모델(BSSRDF)이다.주로 아래와 같은 구조로 표현된다.
  • (BSSRDF - Bidirectional subface scattering reflectance distribution function 는 빛이 물체에 들어가서 물체 안에서 산란하여, , 다시 나오는 모습을 수학적으로 묘사한 것이다.
  • 여기에서 다루려는 것은 R, 즉 반사모델 (Reflectance Model) 임
  • Monte-Carlo Simulation 에서 표면이 가진 Surface Albedo와 Reflectance Profile을 아래와 같이 그래프로 나타내었다.
더보기

Monte-Carlo Simulation?
SubSurface Scattering을 계산하는 가장 보편적인 방식이다. 오브젝트를 Volume ㅇ리가ㅗ가정하고, brute-force Monte Carlo Simulation을 수행한다. 그러나, 이것은 매우 느리다 - 특히 복잡한 씬에서 더 느리다. Donner et al[2009] 는 반 무한대의 볼륨의 평평한 표면에서의 입자 움직임을 Monte-Carlo 를 사용해서 추적해 보았다. 이것을 토대로 빛의 입사각, 잔존하는 빛의 상대적인 위치, 그리고 volume albedo, mean free path length 등의 물리적인 요소들과 빛이 표면을 떠날 때의 분포를 표로 만들었다. 이 데이터를 계산하는 데 수 개월이 걸렸고, 총 데이터는 250MB 정도가 되었다.

이 커브들을 근사하기 위한 여러 시도들이 있었다. Burley는 이들을 두 개의 exponential(지수) 들의 합을 Distance 로 나눈 것으로 보았다. 그리고 Reflectance R을 아래의 식으로 표한하였다.

이 결과에 Surface Albedo A 가 곱해진다.

d 라는 파라미터가 커브의 너비와 높이를 조절하는 역할을 한다.. 그렇다면 d는 뭘까?

→ 여기서는 아티스트가 조절할 수 있는, 표면의 부드러운 정도(softness) 를 나타낸다.

그렇다면 d와 실제 물리적인 파라미터 들 간에는 무슨 관계가 있는걸까?

이것을 찾는 것이 Burley 가 고안한 scattering 근사식의 핵심이다.

https://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf

 

표면에서 빛이 Scattering 되는 거리는 주로 앞에서 살펴본 Mean Free Path 또는 Diffuse Mean Free Path로 표현된다. (Mean Free Path 는 Volume에서, Diffuse Mean Free Path는 표면에서의 산란 거리를 말한다)

RenderMan 이 아닌 다른 렌더러들에서 셰이딩 모델을 살펴봐도, 같은 파라미터를 발견할 수 있다.

https://help.autodesk.com/view/ARNOL/ENU/?guid=arnold_user_guide_ac_standard_surface_ac_standard_subsurface_html

 

Arnold 에서는 Radius 라는 이름으로 되어 있지만, 실제로는 이것이 Mean Free Path이다.

UE에서의 Tint 파라미터는 Weight 파라미터에 대응된다. Diffuse와 SSS가 블렌딩 되는 강도이다.

 

mfp 와 d 사이의 관계식은 아래와 같다.

d = mfp / s

 

여기에서 s는 A (Surface Albedo) 에 따라 달라진다. 각 Albedo 에 가장 알맞는 s값을 찾으면 되는데, A와 s의 이상적인 관계식은 아래와 같다.

위 식을 통해 A = 0.01, 0.02, 0.03 …. , 0.99 까지의 Surface Albedo 값에 알맞는 S 값을 구한다. 그 중간 값은 각 Point 사이를 보간하였다.

이런 변환식을 통해 물리적인 모델들과 Burley모델을 비교해볼 수 있다.

 

그렇다면 이 값들에 대체 어떤 값을 넣어야 하는 걸까? 사람 피부를 표현할 때 알맞은 값이 정해져 있지 않을까?

일상적인 재질에 대한 표준값을 RenderMan 문서에서는 아래와 같이 제시하고 있다.

https://rmanwiki.pixar.com/display/REN22/Subsurface+Scattering+Parameters

 

위 표의 Skin1 값을 토대로, Subsurface Profile 의 파라미터들을 다시 설정해 보았다.

  • 나머지 파라미터 값은 MetaHuman 의 Taro 모델이 사용한 값을 참고하였다.
    표의 값 단위가 mm 이므로, World Unit Scale 에 0.1을 넣어주었다.
    언리얼에서 사용하는 단위는 cm이다.
  • Mean Free Path Distance 는 일반적인 피부 값을 참고해 0.15 cm 정도로 넣어주었다.https://renderman.pixar.com/photorealistic-head
    (얼굴의 각 부분별로 약간씩 다른 값을 페인팅하긴 했지만) 아래 문서를 참고하였다.

Mobile 에서의 Subsurface Scattering

모바일에서의 Subsurface Profile 셰이딩 모델 동작에 관한 내용을 구글링해서도 얻을 수 없었기 때문에, 이 글을 작성하게 되었다.

Subsurface Profile 셰이딩 모델의 BRDF Code 살펴보면, 아래와 같다.

in ShadingModels.ush

FDirectLighting SubsurfaceProfileBxDF( FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, half NoL, FAreaLight AreaLight, FShadowTerms Shadow )
{
	BxDFContext Context;
#if SHADING_PATH_MOBILE
	InitMobile(Context, N, V, L, NoL);
#else
	Init( Context, N, V, L );
#endif
	SphereMaxNoH( Context, AreaLight.SphereSinAlpha, true );
	Context.NoV = saturate( abs( Context.NoV ) + 1e-5 );

	uint SubsurfaceProfileId = ExtractSubsurfaceProfileInt(GBuffer);
	half Opacity = GBuffer.CustomData.a;
	half Roughness = GBuffer.Roughness;

	half Lobe0Roughness = 0;
	half Lobe1Roughness = 0;
	half LobeMix = 0;

	GetProfileDualSpecular(SubsurfaceProfileId, Roughness, Opacity, Lobe0Roughness, Lobe1Roughness, LobeMix);
	half AverageRoughness = lerp(Lobe0Roughness, Lobe1Roughness, LobeMix);

	// Take the average roughness instead of compute a separate energy term for each roughness
	const FBxDFEnergyTerms EnergyTerms = ComputeGGXSpecEnergyTerms(AverageRoughness, Context.NoV, GBuffer.SpecularColor);

	FDirectLighting Lighting;
#if SHADING_PATH_MOBILE
	half Curvature = GBuffer.Curvature;
	half UnClampedNoL = dot(GBuffer.WorldNormal, L);
	half ShadowFactor = 1.0f - sqrt(Shadow.SurfaceShadow);
	// Rotate the world normal based on the shadow value, it's just a experimental value 
	half UnClampedRotatedNoL = max(UnClampedNoL - max(2.0f * UnClampedNoL, 0.4f) * ShadowFactor, -1.0f);
	half4 BurleyDiffuse = GetSSProfilePreIntegratedValue(SubsurfaceProfileId, UnClampedRotatedNoL, Curvature);
	// asset specific color
	half3 Tint = GetSubsurfaceProfileTexture(SSSS_TINT_SCALE_OFFSET, SubsurfaceProfileId).rgb;
	
	// Needs to apply shadow at here, since the preintegrated value has already counted it, and we need to skip applying shadow outside.
	Lighting.Diffuse = lerp(AreaLight.FalloffColor * (Falloff * NoL) * Shadow.SurfaceShadow, BurleyDiffuse.rgb, Tint) * Diffuse_Lambert(GBuffer.DiffuseColor);
#else

#if MATERIAL_ROUGHDIFFUSE
	// Use Chan's diffuse model. It reduces flatness look and has better match, e.g., at the edge of human face skin when compared to GT.
	const float3 DiffuseReflection = Diffuse_Chan(GBuffer.DiffuseColor, Pow4(GBuffer.Roughness), Context.NoV, NoL, Context.VoH, Context.NoH, GetAreaLightDiffuseMicroReflWeight(AreaLight));
#else
	const float3 DiffuseReflection = Diffuse_Burley(GBuffer.DiffuseColor, GBuffer.Roughness, Context.NoV, NoL, Context.VoH);
#endif

	Lighting.Diffuse  = AreaLight.FalloffColor * (Falloff * NoL) * DiffuseReflection;
#endif

	if (IsRectLight(AreaLight))
	{
		float3 Lobe0Specular = RectGGXApproxLTC(Lobe0Roughness, GBuffer.SpecularColor, N, V, AreaLight.Rect, AreaLight.Texture);
		float3 Lobe1Specular = RectGGXApproxLTC(Lobe1Roughness, GBuffer.SpecularColor, N, V, AreaLight.Rect, AreaLight.Texture);
		Lighting.Specular = lerp(Lobe0Specular, Lobe1Specular, LobeMix);
	}
	else
	{
		Lighting.Specular = AreaLight.FalloffColor * (Falloff * NoL) * DualSpecularGGX(AverageRoughness, Lobe0Roughness, Lobe1Roughness, LobeMix, GBuffer.SpecularColor, Context, NoL, AreaLight);
	}

	Lighting.Diffuse  *= ComputeEnergyPreservation(EnergyTerms);
	Lighting.Specular *= ComputeEnergyConservation(EnergyTerms);

#if USE_TRANSMISSION

	const uint ProfileId = ExtractSubsurfaceProfileInt(GBuffer);
	FTransmissionProfileParams TransmissionParams = GetTransmissionProfileParams(ProfileId);

	float Thickness = Shadow.TransmissionThickness;
	Thickness = DecodeThickness(Thickness);
	Thickness *= SSSS_MAX_TRANSMISSION_PROFILE_DISTANCE;
	float3 Profile = GetTransmissionProfile(ProfileId, Thickness).rgb;

	float3 RefracV = refract(V, -N, TransmissionParams.OneOverIOR);
	float PhaseFunction = ApproximateHG( dot(-L, RefracV), TransmissionParams.ScatteringDistribution );
	Lighting.Transmission = AreaLight.FalloffColor * Profile * (Falloff * PhaseFunction); // TODO: This probably should also include cosine term (NoL)

#else // USE_TRANSMISSION

	Lighting.Transmission = 0;

#endif // USE_TRANSMISSION

	return Lighting;
}

 

이 중 SHADING_PATH_MOBILE 부분이 모바일 렌더링 시 Diffuse 계산에 관련된 부분이다.

half Curvature = GBuffer.Curvature;
	half UnClampedNoL = dot(GBuffer.WorldNormal, L);
	half ShadowFactor = 1.0f - sqrt(Shadow.SurfaceShadow);
	// Rotate the world normal based on the shadow value, it's just a experimental value 
	half UnClampedRotatedNoL = max(UnClampedNoL - max(2.0f * UnClampedNoL, 0.4f) * ShadowFactor, -1.0f);
	half4 BurleyDiffuse = GetSSProfilePreIntegratedValue(SubsurfaceProfileId, UnClampedRotatedNoL, Curvature);
	// asset specific color
	half3 Tint = GetSubsurfaceProfileTexture(SSSS_TINT_SCALE_OFFSET, SubsurfaceProfileId).rgb;
	
	// Needs to apply shadow at here, since the preintegrated value has already counted it, and we need to skip applying shadow outside.
	Lighting.Diffuse = lerp(AreaLight.FalloffColor * (Falloff * NoL) * Shadow.SurfaceShadow, BurleyDiffuse.rgb, Tint) * Diffuse_Lambert(GBuffer.DiffuseColor);

 

이 부분은 2022년경에 Cloth 나 Eye 와 같은 다른 셰이딩 모델을 모바일 환경에서도 지원하려는 의도로 추가된 것으로 보인다.

  • Forward Rendering일 때는 맨 앞에서 살펴본 Screen Space Filtering 등을 사용할 수 없다.
  • 따라서 이것과 유사한 다른 방법으로 표면 Scattering을 구현해야 한다.

 

계산 과정을 요약해보면 아래와 같다.

  1. GBuffer에서 Curvature 데이터를 가져온다. 이것은 Material의 Curvature pin의 입력값이다.
    표면의 큰 굴곡을 나타내는 값으로 사용되는데, 이 값이 필요한 이유는 SIGGRAPH 발표자료에서 찾을 수 있다.
    https://advances.realtimerendering.com/s2011/Penner - Pre-Integrated Skin Rendering (Siggraph 2011 Advances in Real-Time Rendering Course).pptx
    문서에 따르면, Curvature 값이 큰 코 끝, 입술 등의 부분이 비교적 평평한 부분인 이마보다 Scattering이 더 크게 일어난다.

 

2.  NdotL 값을 ShadowFactor 를 토대로 Rotate 해서, UnClampedRotatedNoL 값을 얻는다.

3. 1과 2 의 값으로 Texture에 기록된 Scattering값을 얻어온다. 
텍스처에 저장된 값은 아래와 같다.

(텍스처가 아래에서 위로 올라갈수록 변하는 모습을 봤을 때, 1/r = Curvature가 1에 가까울수록 Scattering이 많이 일어날 것이라고 예상할 수 있다)

현재 사용중인 Subsurface Profile 파라미터들 - Surface Albedo, Mean Free Path, Mean Free Path Distance 등으부터 계산한, Sphere에서 모든 방향으로부터 Scattering 될 수 있는 값을 모두 저장해 둔 것이다.

  • 텍스처를 만드는 데 사용되는 함수들은 SSProfilePreIntegratedMobile.usf 와BurleyNormalizedSSSCommon.ush 에 모여 있다.
    실제 UE에서 만들어진 Texture는 아래와 같은 모양이다.
    PF_A2B10G10R10 포맷이다. 별다른 설정이 없었다면 64x64 크기이다.
    이 텍스처가 각 Subsurface Profile 1종마다 1개씩 생성되어, Texture Array 에 저장되고 사용된다.
    index 에 8-bit 가 사용되기 때문에, 한 장면에 최대 256종의 Subsurface Profile 만 사용할 수 있는 것이다.
    • Surface Albedo는 아래 식을 통해 Curve의 Scaling Factor S로 변환된다. 주석에 따르면, Monte Carlo Reference와 비교했을 때 평균 3.9% 정도의 오차를 가진다.
      in BurleyNormalizedSSSCommon.ush
float3 GetDiffuseSurfaceScalingFactor3D(float3 SurfaceAlbedo)
{
	float3 Value = SurfaceAlbedo - 0.8;
	return 1.9 - SurfaceAlbedo + 3.5 * Value * Value;
}

SSProfilePreintegratedMobile.usf를 살펴보면, 핵심 함수인 GetIrredianceFromBurleyDiffusionOnRIng 에 위에서 살펴본 Burley Diffusion 에 관련된 내용을 확인할 수 있다.

 

3-1. 세로 축은 Curvature, 가로 축 값은 UnClampedRotatedNdotL 값을 사용해서 해당 부분의 색상을 샘플링해 온다.
(in ShadingModels.ush)

half4 GetSSProfilePreIntegratedValue(uint SubsurfaceProfileInt, half NoL, half Curvature)
{
	float3 UV = float3((NoL * .5 + .5), Curvature, SubsurfaceProfileInt);

	return Texture2DArraySampleLevel(View.SSProfilesPreIntegratedTexture, View.SSProfilesPreIntegratedSampler, UV, 0);
}

이 값이 BurleyDiffuse 가 된다.

 

3-2. Tint (Subsurface Profile의 파라미터 중 하나) 값에 따라 AreaLight.FalloffColor * (Falloff * NoL) * Shadow.SurfaceShadow 와 BurleyDiffuse 를 혼합한다.

	Lighting.Diffuse = lerp(AreaLight.FalloffColor * (Falloff * NoL) * Shadow.SurfaceShadow, BurleyDiffuse.rgb, Tint) * Diffuse_Lambert(GBuffer.DiffuseColor);

 

여기에서 AreaLight.FalloffColor * (Falloff * NoL) 이라는 부분은 Default Lit 같은 다른 셰이딩 모델에서도 사용되는 Diffuse 항의 기본형이다.

Tint 값이 0에 가까울수록 Diffuse 항 계산 결과를, 1.0에 가까울수록 앞서 계산한 BurleyDiffuse 값을 반환한다.

  • Tint 파라미터가 0에 가까울수록 Scattering 이 없는 것 같이 렌더링 되는것을 볼 수 있다.
    Tint가 float3이기 때문에, 섞이는 강도를 R, G ,B 각 색상 요소에 따라 다르게 할 수 있다.
    (아마도? 의도은 그렇게 보이는데 lerp 가 실제로 이렇게 동작하는지는 정확하지 않음..)

4. 마지막으로 여기에 DiffuseColor 색상이 곱해지면, 이 결과가 Mobile Path의 최종 Diffuse 계산 결과가 된다.
표면이 특별히 Metalic 하지 않다면, 여기에서는 DiffuseColor = BaseColor 이다.
참고 : GBuffer.DiffuseColor = GBuffer.BaseColor - GBuffer.BaseColor * GBuffer.Metallic;

 

Mobile에서 Subsurface Profile 셰이딩 모델 사용하기

위 내용을 알면, 이제 Subsurface Profile 을 모바일에서 사용할 때 필요한 것들을 예상할 수 있다.

 

1. Material 에서 적절한 Curvature 텍스처를 생성해서 넣어준다.
여기에는 모델이 가진 큰 굴곡에 대한 데이터만 포함되어야 한다.

(주름, 모공 등의 디테일은 노멀에서 표현할 것이므로 없어도 된다)

이 텍스처가 메시 각 부분의 Scattering 강도를 결정할 것이다.

아래는 MetaHuman 모델링에서 추출한 Curvature 데이터이다.

(Subsurface Painter에 내장된 baker를 사용했다. Low-Poly Mesh를 High-Poly Mesh 로 사용하였다)

실제로는 이 텍스처를 그대로 사용하는 것이 아니라, Curve를 통해 필요한 영역의 데이터만 사용하도록 적절히 조정해 주었다.

 

2. 되도록이면 물리적인 값을 참조한 Subsurface Profile 파라미터를 사용한다.

Pixar 의 테이블, 또는 MetaHuman 의 Subsurface Profile 값을 참고하는 것이 좋다.

아래는 파라미터를 조절해 보면서 든 생각이다.

  • Surface Albedo 값은 (툴팁에 쓰여있는 것 처럼..) Albedo 텍스처의 색상 값과 비슷하게 넣어 주는 것이 낫다.
  • Tint 값은 되도록 1.0 에 가까운 값을 사용하는 것이 낫다. 이 파라미터의 R, G, B 값 중 어느 하나가 지나치게 높거나 낮을수록 실사 셰이딩과는 점점 거리가 먼 결과가 된다.

3. 기타 알게 된 내용들

  1. Opacity 는 0.1 이하이거나 이상인 값만 유효하다.
    (이것은 SubsurfaceProfileCommon.ush 의 #define SSSS_OPACITY_THRESHOLD_EPS 가 0.10 으로 정의되어 있기 때문이다)
    PC에서는 Scattering 되는 강도로 Opacity를 사용하지만, 모바일에서는 0.1 이하이면 Scattering 하지 않음, 0.1 이상이면 Scattering 함 으로 보면 된다.
    0.1 이하인 부분에서는 Default Lit 셰이딩 모델과 똑같이 동작한다.
    메시의 각 부분별로 Scattering 되는 값을 조정하고 싶으면, 위의 Curvature 텍스처에서 해당 부분을 0으로 주면 된다.
  2. MakeMaterialAttributes 노드를 사용할 경우, 핀 이름이 ClearCoat 가 되는 것에 주의한다.

    이 핀에 들어가는 내용이 Curvature 이다.
  3. Mobile 과 PC 간의 Specular 연산이 다르고, Screen-space에서의 Blur 효과도 모바일에서는 없기 때문에, Normal Intensity나 Specular Intensity 같은 값에 Mobile 전용 Multiplier 파라미터를 추가해서 강도를 조절해 주는 것이 좋아 보인다. (MetaHuman 샘플 참조)

  

 

렌더링 결과

좌 : PC SM5 (Without Dynamic Shadow & Lumen) / 우 : Android Vulkan Preview