Skip to main content
Far Cry and DirectX 
Carsten Wenzel
CRYTEK의 GDC2005 자료 
• 개인적으로 공부하다 적은 것이므로 영어&이해 부족으로 인한 오역을 감안하세요 
• 틀린 부분이나 혹 문제가 될 부분이 있다면 해강(questionname@naver.com)으로 
알려주시면 감사하겠습니다 
• 붉은 색은 개인적으로 적어놓은 메모입니다 틀릴 수도 있어요 
• 원본은 여기입니다 
• http://www.crytek.com/cryengine/presentations/far-cry-and-dx9
Far Cry는 최신 DX9 기능을 사용한다 
• Shader Models 2.x / 3.0  
- vertex textures와 dynamic flow control은 제외 
• Geometry Instancing  
• Floating-point render targets 
Dynamic flow control* in PS 
• 우리는 궁극적으로 하나의 패스에 여러개의 조명을 
통합하기를 원한다… 
float3 finalCol = 0; 
float3 diffuseCol = tex2D( diffuseMap, IN.diffuseUV.xy ); 
float3 normal = mul( IN.tangentToWorldSpace, 
tex2D( normalMap, IN.bumpUV.xy ).xyz ); 
for( int i = 0; i < cNumLights; i++ ) 
{ 
float3 lightCol = LightColor[ i ]; 
float3 lightVec = normalize( cLightPos[ i ].xyz – 
IN.pos.xyz ); 
// … 
// Attenuation, Specular, etc. calculated via if( 
const_boolean ) 
// … 
float nDotL = saturate( dot( lightVec.xyz, normal ) ); 
final += lightCol.xyz * diffuseCol.xyz * nDotL * atten; 
} 
return( float4( finalCol, 1 ) ); 
* Dynamic flow control는 동적 코드 분기(ex : break) 
Shader3.0에서 24, 4.0부터 완전지원
Dynamic flow control in PS 
• 현실 세계에 온 것을 환영한다… 
– Dynamic indexing은 오직input registers에서만허용; 
상수레지스터와루프안의인덱스를통해라이트 
데이터가 통과하는것을방지 
– 입력레지스터를통해라이트정보가 통과되면, 
그것들은 충분하지않기때문에 제대로실현 
불가능하다(오직 10개* ) 
– 동적분기는무료가아니다 
*3.0 기준 레지스터 제한 10 
http://msdn.microsoft.com/en-us/ 
library/bb172920%28v=VS.85%29.aspx
Loop unrolling* 
• 우리는 동적 분기와 루프를 사용하지 않기로 선택했다 
• 대신 정적 분기를 사용하고, 루프를 풀어서 대신했다 
• Far Cry의 기존 쉐이더 프레임워크와 잘 작동한다 
• 쉐이더는 다른 light masks를 위해 미리 컴파일 한다 
– 0-4개의패스당동적라이트 
– 3개의다른라이트타입(spot, omni(ex:전구), directional) 
– 2개의라이트당수정타입(specular only, occlusion map(물체가빛을막아생김) 
• 4개의 조명을 사용할 때 loop unrolling 후 160지침 
(지나치게 긴 쉐이더)이 발생할 수 있다 
– ps_2_0에서너무길게작성 
– ps_2_a, ps_2_b and ps_3_0에서는괜찮다! 
• 런타임에 뻑나는걸 방지하기 위해, 쉐이더 캐쉬를 미리 확보 
* 루프를 풀어쓰는 기법 (예 : for(int i = 0; i < 100; ++I) -> for(int i = 0; i < 50; i += 2) 
분기를 줄이고, cache hit 비율을 높여주는 효과가 있다 
레지스터 사용량이 증가하는건 단점 
정상적인 코드에 대한 최적화로 고려되는 작업이므로 무분별한 사용은 금물 
http://en.wikipedia.org/wiki/Loop_unrolling
어떻게 쉐이더 캐쉬를 작업했나 
• 특정 쉐이더에 따라 다르다: 
1) 재질 종류 
(e.g. skin, phong, metal) 
2) 재질 사용 항목 
(e.g. bump-mapped, specular) 
3) 특정 환경 
(e.g. light mask, fog)
어떻게 쉐이더 캐쉬를 작업했나 
• Cache access: 
– 객체가 랜더링할때이미 쉐이더핸들을 가지고 있나? 그것들을사용해라! 
– 그렇지 않다면메모리에서쉐이더를찾아봐라 
– 만약 하드디스크에서로드에실패하면,(다음) 
– 만약 하드디스크에저장된백업에서VS/PS 생성에 실패한다면, (다음) 
– 마지막으로객체의 쉐이더핸들을 저장한다 
• 이상적인 해결책은 아니다, 그러나 
– 기존의 하드웨어에서꽤 잘 작동한다 
– assets의 변경 없이 손쉽게 통합시킬수 있었다 
• 캐쉬를 효율적으로 써야… 
– HD에 미리 캐쉬된 파일 같은 것들이 존재해야모든 쉐이더의통합이가능 
• 즉시사용할경우쉐이더컴파일에필요한시간때문에맛이간다! 
– 그러나, 캐쉬를 계속 유지하는것은 번거로운일이다
Loop unrolling – 장점/단점 
• 장점: 
– 속도! 동적으로분기되지않아꽤 많은사이클을절약한다 
– 이때, 우리는동적분기보다더 효율적으로쉐이더를바꿀수 
있다는것을찾아냈다 
• 단점: 
– light mask당쉐이더조합번호를쓰기때문에, 정교한쉐이더 
캐싱이필요하다(조합의사전정렬후 244) 
– 쉐이더를미리컴파일하는것은시간이걸린다 
– Far Cry 1.3의 쉐이더캐쉬는430 MB (압축하면~23 MB인 EXE 
패치)가필요하다
Geometry Instancing 
• 오브젝트의 인스턴스를 n만큼 랜더링할 때 n-1의 드로우콜 
비용이 절약될 가능성이 있다* 
• Far Cry는 식물의 랜더링 속도를 올리기 위해 주로 사용했다 
• 인스턴스 속성당: 
– Position 
– Size 
– Bending info 
– Rotation (필요할때만) 
• 인스턴스 속성의 수를 줄이는! 두가지 방법: 
– 버텍스쉐이더상수 
• 100개 이상의폴리곤을가지고있는 오브젝트에사용 
– 속성을연속된열 (stream) 로 만든다 
• 작은 오브젝트에사용 (sprites, impostors) 
* 인스턴싱은 동일한 데이터를 가지고 여러 사본을 만들어 랜더링 
하는 방법이다. n-1의 비용이 절약된다는 말은 10개의 오브젝트를 
찍는다고 했을 때 10번의 호출이 인스턴싱을 통해 최초 1번만 들 수 
있다는 이야기다
VS 상수안의 인스턴스 속성 
• 많은 숫자의 폴리곤을 가진 오브젝트를 위한 
최고의 방법 
• 인스턴스 인덱스를 연속적인 열(stream)로 넣어 
덧붙인다 
• SetStreamSourceFrequency를 사용하여 다음과 
같이 지오메트리 인스턴싱에 설정한다… 
SetStreamSourceFrequency( geomStream, 
D3DSTREAMSOURCE_INDEXEDDATA | numInstances ); 
SetStreamSourceFrequency( instStream, 
D3DSTREAMSOURCE_INSTANCEDATA | 1 ); 
• SSSF(위함수)가 완료되면 당신은 버텍스 스트림의 
빈도를 재설정 해야 한다 ( strNum, 1 )!
VS 상수로 만들어낸 
matMVP와 변환된 버텍스로부터 
VS 속성 (위치 & 크기) 들을 분석해낸다 
const float4x4 cMatViewProj; 
const float4 cPackedInstanceData[ numInstances ]; 
float4x4 matWorld; 
float4x4 matMVP; 
int i = IN.InstanceIndex; 
matWorld[ 0 ] = float4( cPackedInstanceData[ i ].w, 0, 0, 
cPackedInstanceData[ i ].x ); 
matWorld[ 1 ] = float4( 0, cPackedInstanceData[ i ].w, 0, 
cPackedInstanceData[ i ].y ); 
matWorld[ 2 ] = float4( 0, 0, cPackedInstanceData[ i ].w, 
cPackedInstanceData[ i ].z ); 
matWorld[ 3 ] = float4( 0, 0, 0, 1 ); 
matMVP = mul( cMatViewProj, matWorld ); 
OUT.HPosition = mul( matMVP, IN.Position );
인스턴스 속성 stream(열) 
• 폴리곤이 많지 않은 오브젝트를 위한 최고의 방법 
• 인스턴스 데이터를 stream에 추가해 넣는다 
• 버텍스 스트림 설정이 완료되면 다시 이전으로 
되돌린다
특성 stream으로 만들어낸 
matMVP와 변환된 버텍스로부터 
VS 속성 (위치 & 크기) 들을 분석해낸다 
const float4x4 cMatViewProj; 
float4x4 matWorld; 
float4x4 matMVP; 
matWorld[ 0 ] = float4( IN.PackedInstData.w, 0, 0, 
IN.PackedInstData.x ); 
matWorld[ 1 ] = float4( 0, IN.PackedInstData.w, 0, 
IN.PackedInstData.y ); 
matWorld[ 2 ] = float4( 0, 0, IN.PackedInstData.w, 
IN.PackedInstData.z ); 
matWorld[ 3 ] = float4( 0, 0, 0, 1 ); 
matMVP = mul( cMatViewProj, matWorld ); 
OUT.HPosition = mul( matMVP, IN.Position );
Geometry Instancing – 결과 
• 식물의 양에 따라 랜더링 속도를 40%까지 
증가시킬 수 있다 
( draw call이 심하게 제한될 때) 
• 우리는 스프라이트의 거리비율을 증가시켜, 
좋은 비주얼과 적당한 랜더링 속도를 얻을 
수 있었다
Scene을 일반적으로 그렸을 때
배치 시각화 – 동일한 식물 오브젝트를 채워 
넣는 방법을 통해 하나의 드로우콜로 처리할 
수 있었다!
High Dynamic Range Rendering
High Dynamic Range Rendering* 
• A16B16G16R16F 랜더 타겟 포멧 사용 
• Alpha blending과 filtering은 필수다 
• 후처리를 위한 통합 솔루션 
• 후처리에 큰 영향을 주는 것들의 
전체범위를 대체 
(glare(눈부심), flares(불꽃)) 
* HDR 참고 
http://www.slideshare.net/agebreak/ndc12-hdr
HDR – 구현 
• Far Cry의 HDR 표준 접근 방법 
– Kawase’s bloom filters* 
– Reinhard’s tone mapping operator 
– See DXSDK sample 
• Performance hint 
– 후처리를 위해컬러를RG, BA로 색을나눈뒤, 
G16R16F포멧의 두MRT로읽어들여라. 일부 
카드에서 더 효율적으로캐시를 처리한다 
*GDC2004 - Practical Implementation of High Dynamic Range Rendering 
GDC2003도 있다 뒤에 기록
Bloom from [Kawase03] 
• 반복적으로 작은 블러 필터를 적용한다* 
• 원본 이미지와 bloom을 합성한다 
– 이상적으로는 HDR 공간에서 바로 tone mapping 
*전체를 blur하기엔 부하가 크기에 축소 버퍼를 반복적으로 사용한다 
이 방법으로 넓은 범위를 더 빠르고, 높은 품질로 blur 할 수 있다
필터 크기가 각 패스마다 증가 
픽셀이 랜더링 되는 지점 
텍스처 샘플링 지점 
1st pass 
2nd pass 
3rd pass 
From [Kawase03]
No HDR
HDR (tone mapped scene + 
bloom + stars)
[Reinhard02] – Tone Mapping* 
1) scene 휘도를 계산 
GPU에서 log()값을 샘플링 완료 
1X1 다운스케일링, exp()(지수함수)로 계산 
2) 평균 휘도 α를 목표로 확장 
3) 톤맵핑 연산자 적용 
• 휘도값을 Lumavg 의 값으로 천천히 맞춰가면서 2와 3의 단계에 따라 Lumavg 
으로 대체해 빛의 적응을 시뮬레이션 한다 
• 자세한 내용은 “Tone Reproduction In Interactive Applications” 라는 
Reinhard의 세션에 참석해 봐라 
*HDR값을 LDR 영역 값으로 돌려주는 것이 톤맵핑이다 
LDR의 부족한 출력영역을 보정해 보다 리얼한 표현이 가능해진다 
http://www.cs.utah.edu/~reinhard/cdrom/
HDR – 주의사항 
• 현재 FSAA(Full Screen Anti-Aliasing)는 사용하지 않는다 
• 매우 느린 채우기 속도 
• float buffer blending 지원 필요 
• HDR관련 제작1 : 
– Light maps 
– Skybox 
1) 프로토 타입을 위해 우리는 실제로 라이트맵 생성기 를 
수정해 HDR맵과 HDR 스카이박스를 생성하도록 시도했다. 
그들은 좋아 보인다. 그러나 우리는 패치에 포함하지 않았다 
(아래 이유 때문에)… 
– HDR light map textures 압축에 도전중이다 
– 대역폭(사양) 요구사항이더 커졌을 것이다 
– Far Cry 패치 크기가 거대했을것이다 
– 모든 레벨을 조정하고테스트하기엔시간이없었다
결론 
• Dynamic flow control in ps_3_0 
• Geometry Instancing 
• High Dynamic Range Rendering
References 
• [Kawase03] Masaki Kawase, “Frame 
Buffer Postprocessing Effects in 
DOUBLE-S.T.E.A.L (Wreckless),” Game 
Developer’s Conference 2003 
• [Reinhard02] Erik Reinhard, Michael 
Stark, Peter Shirley and James 
Ferwerda, “Photographic Tone 
Reproduction for Digital Images,” 
SIGGRAPH 2002.
Questions 
???