Slide 1

Slide 1 text

Copyright 2019 DELiGHTWORKS. ディライトワークス株式会社 研究開発部 Real Time Computingユニット リードリサーチャー 鈴木 克史 「シェーダーでかんたんにわかる!」 リアルタイムレイトレーシング入門 DirectX

Slide 2

Slide 2 text

Copyright 2019 DELiGHTWORKS. 2 Final Composition

Slide 3

Slide 3 text

Copyright 2019 DELiGHTWORKS. 3 Phong Shading

Slide 4

Slide 4 text

Copyright 2019 DELiGHTWORKS. 4 Phong + Reflection

Slide 5

Slide 5 text

Copyright 2019 DELiGHTWORKS. 5 Phong + Reflection + Direct Shadow

Slide 6

Slide 6 text

Copyright 2019 DELiGHTWORKS. 6 RTAO without Denoise

Slide 7

Slide 7 text

Copyright 2019 DELiGHTWORKS. 7 RTAO with Denoise

Slide 8

Slide 8 text

Copyright 2019 DELiGHTWORKS. シェーダーと描画結果を照らし合わせながら、 Microsoft DirectX Raytracing(DXR)による レイトレーシング表現の基礎を解説します。

Slide 9

Slide 9 text

Copyright 2019 DELiGHTWORKS. リアルタイムレイトレーシングの 原理をつかむ

Slide 10

Slide 10 text

Copyright 2019 DELiGHTWORKS. 10 Menu レイトレーシングパイプライン 1 フォンシェーディング 2 レイトレースシャドウ 3 レイトレースリフレクション 4 レイトレース アンビエントオクリュージョン 5

Slide 11

Slide 11 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 11 レイトレーシングの原理 Scratchapixel 2.0 Rasterization: a Practical Implementation https://www.scratchapixel.com/lessons/3d-basic-rendering/ rasterization-practical-implementation

Slide 12

Slide 12 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 12 レイトレーシングのパイプライン RayGenerationシェーダー ClosestHitシェーダー Missシェーダー 交点あり 交点なし シーンの走査 レイの生成 レイを飛ばす(TraceRay関数) 三角形に交差した? RayGeneration、ClosestHit、Missの 3つのシェーダーをまずおさえよう はい いいえ

Slide 13

Slide 13 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 13 レイトレーシングのパイプライン RayGenerationシェーダー Intersectionシェーダー Any-hitシェーダー ClosestHitシェーダー Missシェーダー 交点あり 交点なし シーンの走査 半透明に交差 レイの交差判定 レイの生成 レイを飛ばす(TraceRay関数) はい いいえ 三角形に交差した? 独自に交差判定したい → Intersection 半透明を描画したい → Any-hitシェーダー ※Closest-hitシェーダーでも可能

Slide 14

Slide 14 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 14 Phong Shading

Slide 15

Slide 15 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 15 アルベドカラーの描画 [shader("raygeneration")] void MyRaygenShader() { // 1. レイの生成 Ray ray = レイを生成する(); UINT 繰り返し回数 = 0; // 2. レイを飛ばしてピクセル色を取得する float4 color = TraceRadianceRay(ray, 繰り返し回数); // 3. 取得したカラーをテクスチャーに格納する g_renderTarget[DispatchRaysIndex().xy] = color; } RayGenerationの役目 1. レイの生成 2. レイを飛ばして色を決定 3. テクスチャーに色を出力

Slide 16

Slide 16 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 16 アルベドカラーの描画 // closesthitシェーダーのタグ [shader("closesthit")] // RayPayload:出力されるオブジェクト void MyClosestHitShader (inout RayPayload rayPayload, in MyAttributes attr) { // 出力 rayPayload.color = マテリアルカラー; } Closest-hitシェーダーの引数は、 レイについてまわるオブジェクト「RayPayload」と、 交差情報の属性「MyAttributes」が入る ↓ 事前に定義が必要

Slide 17

Slide 17 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 17 アルベドカラーの描画 // 交差した属性情報 struct MyAttributes { float3 normal; }; // レイについてまわるオブジェクト struct RayPayload { // カラー float4 color; // 再帰回数 uint recursionDepth; };

Slide 18

Slide 18 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 18 フォグの描画 // カラー float4 color = マテリアルカラー; // レイの長さ float t = RayTCurrent(); // フォグの適用 color = lerp(color,背景色, 1.0 - exp(-0.000002 * t * t * t)); // 出力 rayPayload.color = color; RayTCurrent関数によって 深度値がとれる!

Slide 19

Slide 19 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 19 フォンシェーディング // カラー float4 color = マテリアルカラー; // フォンシェーディング color = フォンシェーディングの計算( color, attr.normal, false); // レイの長さ float t = RayTCurrent(); // フォグの適用 color = lerp(color,背景色, 1.0 - exp(-0.000002 * t * t * t)); // 出力 rayPayload.color = color; レイトレーシングのシェーダーでも ラスタライザーで使っていた 従来のテクニックが使える!

Slide 20

Slide 20 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 20 Ray Trace Shadow

Slide 21

Slide 21 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 21 シャドウとセルフシャドウ セルフシャドウのみ セルフシャドウ+ドロップシャドウ

Slide 22

Slide 22 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 22 レイトレースシャドウの原理 視点 スクリーン スクリーンレイ シャドウレイ シャドウレイ スクリーンレイ スクリーンレイを飛ばした後、 さらに光源に向かってレイを飛ばす。 遮蔽されているかどうかを判定する

Slide 23

Slide 23 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. シャドウレイ関数(TraceShadowRay) 23 レイトレースシャドウのアルゴリズム スクリーンレイを飛ばす オブジェクトと交差あり シーンの走査 RayGenerationシェーダー ClosestHitシェーダー 遮蔽情報を使ってシェー ディング シャドウのアルゴリズムは2ステップ

Slide 24

Slide 24 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. シャドウレイ関数(TraceShadowRay) 24 レイトレースシャドウのアルゴリズム スクリーンレイを飛ばす オブジェクトと交差あり シーンの走査 RayGenerationシェーダー ClosestHitシェーダー 他のオブジェクト に交差した? shadowPayload を返す いいえ 遮蔽なし shadowPayload.hit = false Missシェーダー シャドウレイを飛ばす RayGenerationシェーダー はい 遮蔽あり shadowPayload.hit = true ClosestHit シェーダー 遮蔽情報を使ってシェー ディング シャドウのアルゴリズムは2ステップ

Slide 25

Slide 25 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 25 レイトレースシャドウ︰メイン部分 // カラー float4 color = マテリアルカラー; // レイの交差位置 float3 hitPosition = HitWorldPosition(); // シャドウレイの作成 Ray shadowRay = { hitPosition, normalize(光源の位置 - hitPosition) }; // シャドウかどうかの算出 bool shadowRayHit = TraceShadowRay (shadowRay, rayPayload.recursionDepth); // フォンシェーディング color = フォンシェーディングの計算( color, attr.normal, shadowRayHit); // レイの長さ float t = RayTCurrent(); // フォグの適用 color = lerp(color,背景色, 1.0 - exp(-0.000002 * t * t * t)); // 出力 rayPayload.color = color; TraceShadowRay関数の結果を シェーディング計算に適用する

Slide 26

Slide 26 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 26 レイトレースシャドウ︰シャドウかどうかの判定① // シャドウかどうかの判定 bool TraceShadowRay (in Ray ray, in UINT currentRayRecursionDepth) { // 再帰回数の上限を超えていたら、falseを返す if (currentRayRecursionDepth >= MAX_RAY_RECURSION_DEPTH) { return false; } // レイオブジェクトを作成 RayDesc rayDesc; rayDesc.Origin = ray.origin; // レイの原点 rayDesc.Direction = ray.direction; // レイの方向 rayDesc.TMin = 0; // レイの最小範囲 rayDesc.TMax = 10000; // レイの最大範囲 // ShadowRayPayloadの初期化 ShadowRayPayload shadowPayload = { true };

Slide 27

Slide 27 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 27 レイトレースシャドウ︰シャドウかどうかの判定② // レイを飛ばす TraceRay( // シーン構造(AccelerationStructure) g_scene, // 各種フラグ設定 RAY_FLAG_CULL_BACK_FACING_TRIANGLES | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_FORCE_OPAQUE // Any hit shaderをスキップ | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER, // Closest hit shaderをスキップ TraceRayParameters::InstanceMask, TraceRayParameters::HitGroup::Offset[RayType::Shadow], TraceRayParameters::HitGroup::GeometryStride, TraceRayParameters::MissShader::Offset[RayType::Shadow], // レイとshadowPayloadを渡す rayDesc, shadowPayload); // 交差済みかどうかを返す return shadowPayload.hit; }

Slide 28

Slide 28 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 28 Ray Trace Reflection

Slide 29

Slide 29 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 29 レイトレースリフレクション 床リフレクションのみ 床リフレクション+オブジェクトリフレクション

Slide 30

Slide 30 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 30 レイトレースリフレクションの原理 法線 リフレクションマテリアル スクリーンレイ 視点 スクリーン リフレクションレイ スクリーンレイを飛ばした後、 さらに反射角方向にレイを飛ばす

Slide 31

Slide 31 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. リフレクション関数 (TraceRadianceRay) 31 レイトレースリフレクションのアルゴリズム スクリーンレイを飛ばす オブジェクトと交差あり シーンの走査 RayGenerationシェーダー ClosestHitシェーダー payload情報を使って シェーディング リフレクションのアルゴリズムも 2ステップ

Slide 32

Slide 32 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. リフレクション関数 (TraceRadianceRay) 32 レイトレースリフレクションのアルゴリズム スクリーンレイを飛ばす オブジェクトと交差あり シーンの走査 RayGenerationシェーダー ClosestHitシェーダー オブジェクトに交 差した? payload を返す いいえ 遮蔽されている payload.color = 背景色 Missシェーダー レイを飛ばす RayGenerationシェーダー はい payload.color = 描画色 ClosestHit シェーダー payload情報を使って シェーディング リフレクションのアルゴリズムも 2ステップ

Slide 33

Slide 33 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 33 レイトレースリフレクション① // リフレクション float4 reflectedColor = float4(0, 0, 0, 0); // リフレクションレイの作成 Ray reflectionRay = { HitWorldPosition(), reflect(WorldRayDirection(), attr.normal) }; // リフレクションカラーの算出 float4 reflectionColor = TraceRadianceRay(reflectionRay, rayPayload.recursionDepth); // フレネル効果の適用 float3 fresnelR = FresnelReflectanceSchlick(WorldRayDirection(), attr.normal, color.xyz); // リフレクションカラーの計算 reflectedColor = 反射係数 * float4(fresnelR, 1) * reflectionColor;

Slide 34

Slide 34 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 34 レイトレースリフレクション② // フォンシェーディング float4 phongColor = フォンシェーディングの計算( color, attr.normal, shadowRayHit); //フォンシェーディングの結果とリフレクションを合算 color = phongColor + reflectedColor; // レイの長さ float t = RayTCurrent(); // フォグの適用 color = lerp(color,背景色, 1.0 - exp(-0.000002 * t * t * t)); // 出力 rayPayload.color = color;

Slide 35

Slide 35 text

Copyright 2019 DELiGHTWORKS. 35 Ray Traced Ambient Occlusion

Slide 36

Slide 36 text

Copyright 2019 DELiGHTWORKS. 36

Slide 37

Slide 37 text

Copyright 2019 DELiGHTWORKS. 37

Slide 38

Slide 38 text

Copyright 2019 DELiGHTWORKS. 38

Slide 39

Slide 39 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 39 その前に︰レイトレースシャドウのおさらい 視点 スクリーン スクリーンレイ シャドウレイ

Slide 40

Slide 40 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 40 レイトレースシャドウ︰横から見てみる

Slide 41

Slide 41 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 41 アンビエントオクルージョンの原理 レイの衝突点から法線方向を起点にした 半球面上に無数のシャドウレイを作る それらの結果の平均を遮蔽度合いとする

Slide 42

Slide 42 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 42 アンビエントオクルージョンの原理 正確に計算するなら半球状に無数のレイを飛 ばしたい・・・ レイの数を増やすほど計算量が増えてしまう

Slide 43

Slide 43 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 43 アンビエントオクルージョンの原理 ランダムな方向に飛ばすことで近似する

Slide 44

Slide 44 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 44 アンビエントオクルージョンの原理 4本のシャドウレイを飛ばす 2本が遮蔽 明るさ = 2/4 = 0.5

Slide 45

Slide 45 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 45 アンビエントオクルージョンの原理 4本のシャドウレイを飛ばす 遮蔽なし 明るさ = 4/4 = 1

Slide 46

Slide 46 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 46 アンビエントオクルージョンの原理 4本のシャドウレイを飛ばす 1本が遮蔽 明るさ = 3/4 = 0.75

Slide 47

Slide 47 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 47 アンビエントオクルージョンの計算結果

Slide 48

Slide 48 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 48 アンビエントオクルージョンの計算結果(デノイズ後)

Slide 49

Slide 49 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 49 アンビエントオクルージョン︰デノイズ前後

Slide 50

Slide 50 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 50 アンビエントオクルージョンのシェーダー float evaluateAO(float3 position, float3 normal) { uint2 pixIdx = DispatchRaysIndex().xy; // レイインデックス x=0~1920, y=0~1080 uint2 numPix = DispatchRaysDimensions().xy; // ステージサイズ x=1920, y=1080 // ランダムなシードを計算 uint randSeed = initRand(pixIdx.x + pixIdx.y * numPix.x, 100); // 遮蔽度合い float visibility = 0.0f; // 飛ばすレイの回数 const int aoRayCount = 4; for (int i = 0; i < aoRayCount; ++i) { // 法線を中心とした半球上のランダムなベクトルのサンプリング(コサイン重み付き分布) float3 sampleDir = getCosHemisphereSample(randSeed, normal); // シャドウレイを飛ばす float sampleVisibility = shootShadowRay(position, sampleDir, RAY_EPSILON, 10.0, 1); //遮蔽度合い += サンプリングした値 × コサイン項 / 確率密度関数 float NoL = saturate(dot(normal, sampleDir)); float pdf = NoL / PI; visibility += sampleVisibility * NoL / pdf; } // 平均を取る return (1 / PI) * (1 / float(aoRayCount)) * visibility; } 複数レイを飛ばして 平均を取る 数式の詳しい解説はP. 59参照

Slide 51

Slide 51 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 51 アンビエントオクルージョンのシェーダー float evaluateAO(float3 position, float3 normal) { uint2 pixIdx = DispatchRaysIndex().xy; // レイインデックス x=0~1920, y=0~1080 uint2 numPix = DispatchRaysDimensions().xy; // ステージサイズ x=1920, y=1080 // ランダムなシードを計算 uint randSeed = initRand(pixIdx.x + pixIdx.y * numPix.x, 100); // 遮蔽度合い float visibility = 0.0f; // 飛ばすレイの回数 const int aoRayCount = 4; for (int i = 0; i < aoRayCount; ++i) { // 法線を中心とした半球上のランダムなベクトルのサンプリング(一様分布) float3 sampleDir = getUniformHemisphereSample(randSeed, normal); // シャドウレイを飛ばす float sampleVisibility = shootShadowRay(position, sampleDir, RAY_EPSILON, 10.0, 1); //遮蔽度合い += サンプリングした値 × コサイン項 / 確率密度関数 float NoL = saturate(dot(normal, sampleDir)); float pdf = 1.0 / (2.0 * PI); visibility += sampleVisibility * NoL / pdf; } // 平均を取る return (1 / PI) * (1 / float(aoRayCount)) * visibility; } 複数レイを飛ばして 平均を取る 数式の詳しい解説はP. 59参照

Slide 52

Slide 52 text

Copyright 2019 DELiGHTWORKS. 52 AAなし デノイズなし AAあり デノイズなし AAあり デノイズ1回 AAあり デノイズ2回 AAあり デノイズ3回 AAあり デノイズ3回 サンプル2回 AAあり デノイズ3回 サンプル3回

Slide 53

Slide 53 text

Copyright 2019 DELiGHTWORKS. 53 AAなし デノイズなし AAあり デノイズなし AAあり デノイズ1回 AAあり デノイズ2回 AAあり デノイズ3回 AAあり デノイズ3回 サンプル2回 AAあり デノイズ3回 サンプル3回

Slide 54

Slide 54 text

Copyright 2019 DELiGHTWORKS. 54

Slide 55

Slide 55 text

Copyright 2019 DELiGHTWORKS. 55 レイトレーシングパイプライン 1 フォンシェーディング 2 レイトレースシャドウ 3 レイトレースリフレクション 4 レイトレース アンビエントオクリュージョン 5 まとめ

Slide 56

Slide 56 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 読む 初のDXR本:Ray Tracing Gems Microsoft DirectX仕様:DirectX-Specs SIGGRAPH 2018 - Full Rays Ahead! From Raster to Real-Time Raytracing 教わる SIGGRAPHコース Introduction to DirectX RayTracing 手を動かす microsoft/DirectX-Graphics-Samples TheRealMJP/DXRPathTracer acmarrs/IntroToDXR 学習リソースの紹介 Ray Tracing Gems http://www.realtimerendering.com/raytracinggems/ Introduction to DirectX RayTracing http://intro-to-dxr.cwyman.org/

Slide 57

Slide 57 text

Copyright 2019 DELiGHTWORKS. 58 Let’s Ray tracing!

Slide 58

Slide 58 text

Copyright 2019 DELiGHTWORKS.

Slide 59

Slide 59 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 60 点PにおけるAmbient Occlusionの関数を下記と定義すると、 AO(P) = 1 ∫ cos d (1.1) AO(P) = 1 � 1 ∑=1 (ω ) cos / (1.2) 下記のように近似できる(※モテカルロサンプリングによる積分の近似) ∫ d :微小領域dωの半球面Ω上での積分 ︓Visibility項(可視項・遮蔽されているかどうか。0 or 1を返す) cos ︓コサイン項(法線とシャドウレイの内積) ︓確率密度関数(Probability Density Function) k:サンプリング数(ここではシャドウレイの数) 補足:アンビエントオクルージョンの計算式 http://www.realtimerendering.com/raytracinggems/ 𝐶𝐶𝐶𝐶𝐶𝐶𝐶𝐶 15 "𝑂𝑂 𝑡𝑡𝑡𝑡𝑡 𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼 𝑜𝑜𝑜𝑜 𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆“ . 209

Slide 60

Slide 60 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 61 コサイン重み付き分布(※p.59)によりサンプリングした場合、pdfは = cosθ/ (1.3) AO(P) = 1 ∑=1 (ω ) (1.4) よって、(1.3)より(1.2)は(1.4)のようにも書ける 補足:アンビエントオクルージョンの計算式 AO(P) = 1 � 1 ∑=1 (ω ) cos / (1.2) http://www.realtimerendering.com/raytracinggems/ 𝐶𝐶𝐶𝐶𝐶𝐶𝐶𝐶 15 "𝑂𝑂 𝑡𝑡𝑡𝑡𝑡 𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼𝐼 𝑜𝑜𝑜𝑜 𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆𝑆“ . 209

Slide 61

Slide 61 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 62 サンプリング方式: Cosine-weighted sampling float3 getCosHemisphereSample(uint randSeed, float3 hitNorm) { // 2つのランダムな数値を取得 float2 randVal = float2(nextRand(randSeed), nextRand(randSeed)); // 法線に垂直なベクトルを取る(最後に利用する) float3 bitangent = getPerpendicularVector(hitNorm); float3 tangent = cross(bitangent, hitNorm); // ディスク上に一様にサンプリング float r = sqrt(randVal.x); float phi = 2.0f * PI * randVal.y; // 半球に射影する float x = r * cos(phi); float z = r * sin(phi); float y = sqrt(1.0 - randVal.x); // 1- r2 // 法線ベクトルの座標系に射影 return x * tangent + y * hitNorm.xyz + z * bitangent; } r Φ x z 半径r、角度Φのディスク上に一様に分布 y 1 r 半球に射影

Slide 62

Slide 62 text

Copyright 2019 DELiGHTWORKS Inc. All Rights Reserved. 63 サンプリング方式: Uniform sampling float3 getUniformHemisphereSample(uint randSeed, float3 hitNorm) { // 2つのランダムな数値を取得 float2 randVal = float2(nextRand(randSeed), nextRand(randSeed)); // 法線に垂直なベクトルを取る(最後に利用する) float3 bitangent = getPerpendicularVector(hitNorm); float3 tangent = cross(bitangent, hitNorm); // θ、Φを決める float cosTheta = randVal.x; float sinTheta = sqrt(1.0f - cosTheta * cosTheta); float phi = 2.0f * PI * randVal.y; // 半球に射影する float x = sinTheta * cos(phi); float z = sinTheta * sin(phi); float y = cosTheta; // 法線ベクトルの座標系に射影 return x * tangent + y * hitNorm.xyz + z * bitangent; } r, θ, Φを元に半球面上にランダムな関するを取得する Φ θ x z y x = r sinθ ・ cosΦ z = r sinθ ・ sinΦ y = cosθ r rsinθ

Slide 63

Slide 63 text

Copyright 2019 DELiGHTWORKS. 64

Slide 64

Slide 64 text

Copyright 2019 DELiGHTWORKS. 65

Slide 65

Slide 65 text

Copyright 2019 DELiGHTWORKS. 66