fastskinshading

Report
ShaderX7 2.4.
Fast Skin Shading
John Hable, George Borshukov, Jim Hejl
Shader Study (http://cafe.naver.com/shader)
임용균
Introduction
• 많은 게임들이 일반적인 라이팅 모델을 피부에도 적용함
으로 플라스틱 같은 느낌이 나 피부 같은 느낌이 없다.
• 피부는 판지와 같은 pure diffuse surface와는 매우 다르
다.
– 근본적인 차이는 피부 안에서 빛들의 반사가 매우 다름에 있다.
– 피부는 투명도의 단계가 다른 여러 layer로 구성 되어 있다.
• 피부 밑에서 빛이 어떻게 통과하는지에 대한 빠른 시뮬
레이션의 구현 (PS3, Xbox 360 수준을 목표)
Background
• GPU Gems 3 [d’EonGPUGems07]
• Subsurface scattering
– Step1 : 빛이 피부에 닿음
• diffuse light를 라이트맵에 렌더링
– Step2 : 빛이 피부 아래에서 반사 됨
• 라이트맵을 블러링함으로 시뮬레이션
– Step3 : 빛이 피부 밖으로 빠져나감. 카메라에 의해 보여짐
• 블러링된 빛을 diffuse map에 multiplying함으로 시뮬레이션
Diffuse
• 피부의 한 점의 diffuse를 계산하기 위해서는 그 점 주변의 들어오는
빛의 intensity를 알아야 한다.
• diffusion dipole로 거의 해결되었다. [Donner05]
– 빛과 점의 거리에 따라 red, green, blue의 intensity가 다른 커브를 발견.
• diffusion dipole을 blur들의 합으로 분해할 수 있다.
– real-time으로 실행할 수 있다.
• 5번의 7x7 gaussian blur가 1번의 50x50 gaussian blur보다 빠르다.
• blur 회수를 조절하여 다양한 피부 타입을 나타내는것이 가능하다.
Variance (mm^2)
Red
Green
Blue
0.0064
0.233
0.455
0.649
0.0484
0.100
0.336
0.344
0.187
0.118
0.198
0
0.567
0.113
0.007
0.007
1.99
0.358
0.004
0
7.41
0.078
0
0
Our Contributions
• blur의 samping pattern을 변경함으로 additional error
를 많이 줄일 수 있었다.
– d’Eon과 Leubke의 테크닉을 시뮬레이션 하지만 좀더 적은 tap을
이용한다. (~12 samples)
• 두개의 “링”을 이용하여 블러를 시뮬레이션 함으로 좋은
결과를 얻었다.
– 각각의 링을 6섹션으로 나눔 (총 12섹션)
– 섹션마다 jittered sample을 한다.
– sample에 맞는 weight를 적용한다.
Our Contributions
float3 blurJitteredWeights[13] =
{
{ 0.220441, 0.437000, 0.635000
{ 0.076356, 0.064487, 0.039097
{ 0.116515, 0.103222, 0.064912
{ 0.064844, 0.086388, 0.062272
{ 0.131798, 0.151695, 0.103676
{ 0.025690, 0.042728, 0.033003
{ 0.048593, 0.064740, 0.046131
{ 0.048092, 0.003042, 0.000400
{ 0.048845, 0.005406, 0.001222
{ 0.051322, 0.006034, 0.001420
{ 0.061428, 0.009152, 0.002511
{ 0.030936, 0.002868, 0.000652
{ 0.073580, 0.023239, 0.009703
};
},
},
},
},
},
},
},
},
},
},
},
},
},
float2 blurJitteredSamples[13] =
{
{ 0.000000, 0.000000 },
{ 1.633992, 0.036795 },
{ 0.177801, 1.717593 },
{ -0.194906, 0.091094 },
{ -0.239737, -0.220217 },
{ -0.003530, -0.118219 },
{ 1.320107, -0.181542 },
{ 5.970690, 0.253378 },
{ -1.089250, 4.958349 },
{ -4.015465, 4.156699 },
{ -4.063099, -4.110150 },
{ -0.638605, -6.297663 },
{ 2.542348, -3.245901 },
};
Our Contributions
• blur pass
float3 totalColor = 0;
float2 strectch = tex2D(StretchTextureBlurred, uv.xy).rg;
float shadow = tex2D(LightMap, uv.xy).a;
for (int i=0; i<=12; i++)
totalColor += SubsurfaceJitterSampler(uv.xy, stretch, i);
Our Contributions
• High-Z
– Light map 렌더링 패스에서 depth를 기록한다.
• depth = dot(N, V) * 0.5 + 0.5
– High-Z를 사용하여 오직 앞면의 폴리곤에 blur를 적용한다.
• 위의 공식에서는 depth가 0.5이상인 픽셀만 blur를 적용하면 된다.
• Texture Size
– Light map blur 텍스쳐는 원래의 Diffuse
texture보다 작은것을 이용한다.
(512x512 fp16RGBA buffer)
 Sharpness가 많이 사라지는 문제 발생
 Capture 장비 일 경우 자동 Blur 현상
 최종 합성 단계에서 diffuse map과의 합성
Our Contributions
• Shadow
– Light map 렌더링 패스에서 shadow를 alpha channel에 포함
– lighting을 픽셀마다 두번 해야 되는 단점이 있다.
• Diffuse component를 계산하는 것은 Specular component보다 저렴하므로
큰 문제는 아니다.
– Light map이 블러링 되면서 자연스러운 soft shadow를 얻을 수
있다.
Specular
• 피부는 실제적으로 매우 광택이 있다.
• Phong 모델은 적합하지 않음
– 하나의 퐁 모델이 나타내는 범위로는 비슷하게 표현이 불가능
• 적합한 Specular 모델은?
– 여러 개의 범위를 나타낼수 있는 모델이어야 한다.
– built-in fresnel term이 있어야 한다.
– grazing angle에서 specular highlight가 더 밝아야 한다.
• Kelemen-Szirmay-Kalos [Kelemen01] 모델을 사용
– 그러나 비용이 비싸다.
Variation Across the Face
• 얼굴의 모든 부분에 하나의 라이팅 모델을 적용하는 것은 맞
지 않다.
– 특정 부분은 다른 부분보다 더 밝다.
– 부위마다 subsurface scattering 효과도 다르다.
• Specular Map을 추가하여 Specular를 제어 한다.
– 드라마틱하게 향상되지는 않지만 추천하는 방법
• Subsurfacy Map을 추가하여 subsurface scattering을 제어
– 크게 향상되지는 않지만 다른 느낌을 준다.
– 다양한 느낌을 원한다면 이용 할 만 하다.
(older dark-skinned male VS young white female)
Final Shader
float diffuse = saturate(dot(lightVec, normal));
float finalShadow = tex2D(LightmapCombineBlur, uv.xy).w;
float3 readModelColor = pow(tex2D(HeadDiffuse, uv.xy), 2.2);
float4 outColor = float4(0, 0, 0, 1);
float3 linearLightColor = pow(lightColor, 2.2) * lightBrightness;
float3 diffusePoint = Kd * linearLightColor * diffuse * finalShadow;
float lightmapAmount = tex2D(StretchTexture, uv.xy).b;
float3 diffuseBlurred = Kd * tex2D(LightmapCombinedBllur, float2(uv.x, uv.y)).rgb;
diffuseColor = blurJitteredWeights[0] diffusePoint +
lerp((float3(1, 1, 1) – blurJitteredWeights[0]) *
diffusePoint, diffuseBlurred, lightmapAmount);
specular = KelemenSzirmauKalosSpec(normalize(viewVec), normal, lightVec,
eccentricity, rolloff, weight);
outColor.rgb = Ao * Ka * realModelColor +
(diffuseColor * realModelColor + kS * linearLightColor * specular * finalShadow);
Data Preparation
• 실사적인 결과물을 얻기 위해서는 스캔 데이터를 이용하
는 것을 추천
– 데모에 사용된 머리는 XYZRGB로 스캔되었음
• Gamma correction이 핵심적으로 필요함
– 적절한 gamma correction이 diffuse map들에 적용되어야 한다.
• Normal map이 중요함
– 실제 얼굴의 표면은 매우 울퉁불퉁하다.
– Subsurface scattering은 모습을 부드럽게 한다.
Conclusion
• 기존의 방법들(Doug Jones Demo)과 비슷한 결과물을
내면서 속도는 10배 정도 빠르다.
– Xbox360에서 512x512 buffer blur step을 0.45ms에 실행한다.
• Doug Jones Demo에 비해 품질이 떨어지는 부분이 있다.
– “fleshiness”
• 적은 커널을 사용함으로 red channel의
넓은 blur를 정확히 표현하지 못한다.
– 미묘한 부분의 생략
•
•
•
•
Stretching에 약간의 문제가 있음
low stretch부분이 high stretch부분과 만나는 지점
알아차릴만한 blurring artifacts가 발생함
귀 주변과 같은 부분

similar documents