88.4K Views
March 07, 21
スライド概要
3D描画の仕組みを理解することでGPUを使用せずに3D描画エンジンをフルスクラッチで作成する方法に関するスライド。
理解に重点を置くことを目的をしているため実用性はない。
発表時の動画は
https://youtu.be/Ow-ouwxxfP8
岐阜の山中でヒキコモリ系プログラマー WindowsとiOSの間で生きる何か C/C++/Java/C#/Obj-C/Swift/F#/Haskell/Rustで生きている
作ってわかる レンダリングパイプライン ~CPUで3D描画~ iOSDC Japan 2018
自己紹介 @ta̲ka̲tsu 岐阜県在住
自己紹介 @ta̲ka̲tsu 岐阜県在住 3D CAD系 プログラマー
自己紹介 @ta̲ka̲tsu 岐阜県在住 3D CAD系 プログラマー 3D CAM系 プログラマー
自己紹介 @ta̲ka̲tsu 岐阜県在住 3D CAD系 プログラマー 3D CAM系 プログラマー 会社設立
自己紹介 @ta̲ka̲tsu 岐阜県在住 3D CAD系 プログラマー 3D CAM系 プログラマー 会社設立 フリーランス←Now
自己紹介 @ta̲ka̲tsu 岐阜県在住 3D CAD系 プログラマー 3D CAM系 プログラマー 会社設立 フリーランス←Now
概要 レンダリングパイプラインの 理解 分解 再構築
レンダリング パイプラインの 理解 Understanding
レンダリングパイプラインとは 3Dグラフィックスを計算する 処理方式の1つ レンダリング: データから画像などを得ること パイプライン: 複数の処理を直列に繋いだ一連の処理
レンダリングパイプラインで描けるもの ・点
レンダリングパイプラインで描けるもの ・点 ・線
レンダリングパイプラインで描けるもの ・点 ・線 ・三角形
プリミティブ ・点 ・線 ・三角形(ポリゴン) プリミティブ
プリミティブ ・点…頂点1つ ・線…頂点2つ ・三角形…頂点3つ
頂点属性 頂点属性 ・位置
頂点属性 頂点属性 ・位置 ・色
頂点属性 頂点属性 ・位置 ・色 ・法線ベクトル ・テクスチャ座標 ・…
レンダリングパイプラインの入出力 3Dデータ レンダリング パイプライン フレームバッファ
レンダリングパイプラインの入出力 頂点列 レンダリング パイプライン フレームバッファ
レンダリングパイプラインの入出力 頂点列 レンダリング パイプライン フレームバッファ
レンダリングパイプラインの入出力 頂点列 レンダリング パイプライン フレームバッファ 共通データ (Uniform)
レンダリングパイプラインの入出力 描画命令 (Draw Call) 頂点列 レンダリング パイプライン フレームバッファ 共通データ (Uniform)
レンダリング パイプラインの 分解 Decomposition
レンダリングパイプラインの入出力 頂点列 レンダリング パイプライン フレームバッファ 共通データ (Uniform)
レンダリングパイプラインの入出力 頂点列 頂点処理 プリミティブアセンブリ ラスタライズ フラグメント処理 ピクセル操作 フレームバッファ 共通データ (Uniform)
頂点列 頂点処理 プリミティブアセンブリ ラスタライズ フラグメント処理 頂点処理 ピクセル操作 フレームバッファ
頂点処理 出力: 頂点列 入力: 頂点列 頂点処理
頂点処理:頂点シェーダー 頂点 共通データ 頂点 ・位置 ・位置 ・属性A ・属性B ・属性C 頂点シェーダー ・属性D ・属性E
頂点処理:頂点シェーダー 頂点属性 ・位置 ・色 ・法線ベクトル 頂点シェーダー 頂点属性 ・位置 ・色
頂点処理:クリッピング クリッピング空間 Metalでは −1 ≦ x ≦ 1 −1 ≦ y ≦ 1 0≦z≦1
頂点処理:クリッピング
頂点処理:クリッピング
頂点処理:クリッピング
頂点処理:クリッピング
頂点処理:クリッピング
頂点処理:クリッピング
頂点処理:クリッピング
頂点処理:ビューポート変換 x y ビューポート クリッピング空間 ビュー
頂点処理:ビューポート変換 x y ビューポート クリッピング空間 ビュー
頂点列 頂点処理 プリミティブアセンブリ ラスタライズ プリミティブ アセンブリ フラグメント処理 ピクセル操作 フレームバッファ
プリミティブアセンブリ 入力: 頂点列 出力: プリミティブ列 プリミティブ アセンブリ
プリミティブアセンブリ V[0] V[1] V[3] V[2] V[4]
プリミティブアセンブリ V[0] Point[0] V[1] Point[1] V[3] V[2] Point[2] Point[3] V[4] Point[4] Points
プリミティブアセンブリ V[0] V[1] Line[0] V[3] V[2] Line[1] V[4] Lines
プリミティブアセンブリ V[0] Line[0] V[1] Line[1] V[2] Line[2] V[3] Line[3] V[4] Line Strip
プリミティブアセンブリ V[0] V[1] Triangle[0] V[3] V[2] V[4] Triangles
プリミティブアセンブリ V[0] V[1] Triangle[0] Triangle[1] V[2] Triangle[2] V[4] V[3] Triangle Strip
プリミティブアセンブリ V[0] Line[0] V[1] V[1] Line[1] V[0] V[2] Line[4] Line[2] Line[3] V[4] Line Loop Triangle[0] V[2] Triangle[1] V[3] Triangle[2] V[4] Triangle Fan V[3]
プリミティブアセンブリ:カリング(オプション)
プリミティブアセンブリ:カリング(オプション)
プリミティブアセンブリ:カリング(オプション)
プリミティブアセンブリ:カリング(オプション) 面の向き?
プリミティブアセンブリ:カリング(オプション) 0 0 2 1 1 2 時計回り(CW) or 反時計回り(CCW)
頂点列 頂点処理 プリミティブアセンブリ ラスタライズ フラグメント処理 ラスタライズ ピクセル操作 フレームバッファ
ラスタライズ 入力: プリミティブ列 出力: フラグメント列 ラスタライズ
ラスタライズ
ラスタライズ
ラスタライズ フラグメント ・ピクセル座標 ・Z値 ・属性A ・属性B ・. . .
ラスタライズ
ラスタライズ
ラスタライズ:フラグメントの値 P0 s P t P1 t s P= P0 + P1 s+t s+t = w0P0 + w1P1 (w0 + w1 = 1) AP = w0 AP0 + w1AP1 ( AX :点 X の属性値)
ラスタライズ
ラスタライズ
ラスタライズ:フラグメントの値 t1 s1 Q= P0 + P1 s1 + t1 s1 + t1 t2 s2 P= Q+ P2 s2 + t2 s2 + t2 P0 t1 P1 Q s1 s2 P t 2 = w0P0 + w1P1 + w2P2 P2 (w0 + w1 + w2 = 1) AP = w0 AP0 + w1AP1 + w2 AP2
頂点列 頂点処理 プリミティブアセンブリ ラスタライズ フラグメント 処理 フラグメント処理 ピクセル操作 フレームバッファ
フラグメント処理 入力: フラグメント列 出力: (色)フラグメント列 フラグメント 処理
フラグメント処理 フラグメント 共通データ ・ピクセル座標 ・属性A ・属性B ・. . . ・Z値 フラグメント シェーダー ・色
フラグメント処理 フラグメント 共通データ ・ピクセル座標 (色)フラグメント ・ピクセル座標 ・属性A ・属性B フラグメント シェーダー ・色 ・. . . ・Z値 ・Z値
フラグメント処理:フラグメントシェーダー ・ピクセル座標 ・Z値 ・テクスチャ座標 テクスチャ (共通データ) フラグメントシェーダー ・色
頂点列 頂点処理 プリミティブアセンブリ ラスタライズ ピクセル 操作 フラグメント処理 ピクセル操作 フレームバッファ
ピクセル操作 入力: (色)フラグメント列 出力: フレームバッファ カラーバッファ ピクセル操作 デプスバッファ ステンシルバッファ
ピクセル操作:デプステスト(オプション) 緑を先に書いた場合 水色を先に書いた場合
ピクセル操作:デプステスト(オプション) 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 フラグメント フラグメント 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.4 0.4 0.4 0.4 通過したZ値を 上書き 0.5 0.5 0.4 0.4 0.2 0.2 0.2 0.2 0.5 0.5 0.5 0.2 デプスバッファ デプスバッファ
ピクセル操作:ブレンディング(オプション) α× +(1 − α) × =
レンダリング パイプラインの 再構築 Reconstruction
下準備
混ぜ合わせ可能な型 protocol Addable { static func +(lhs:Self, rhs:Self) -> Self } protocol Scalable { static func *(scale:Float, value:Self) -> Self } protocol Blendable : Addable, Scalable { }
色の準備 struct Color4f : Blendable { let r: Float let g: Float let b: Float let a: Float }
色の準備 struct Color4ui { let r: UInt8 let g: UInt8 let b: UInt8 let a: UInt8 }
頂点の準備 class Vertex3<T:Blendable> { var position : float3 var attribute : T . . . } class var var . . } Vertex4<T:Blendable> { position : float4 attribute : T .
フラグメントの準備 class let let let let . . } Fragment<T:Blendable> { x: Int y: Int z: Float attribute: T .
フレームバッファの準備 protocol BufferPlane { associatedtype CellType var width: Int { get } var height: Int { get } subscript(x: Int, y: Int) -> CellType { get set } }
フレームバッファの準備 class ColorBuffer : BufferPlane { let width: Int let height: Int subscript (x: Int, y: Int) -> Color4ui { . . . } }
フレームバッファの準備 class DepthBuffer : BufferPlane { let width: Int let height: Int subscript (x: Int, y: Int) -> Float { . . . } }
ビューの準備 CALayer
ビューポートの準備 class let let let let . . } Viewport { x: Int y: Int width: Int height: Int .
パイプラインの準備 class RenderPipeline<T:Blendable, U:Blendable> { var vertexBuffer : [Vertex4<T>]! var viewport: Viewport? var vertexShader : ((Vertex4<T>) -> Vertex4<U>)! var fragmentShader : ((Fragment<U>) -> Color4f?)! var colorBuffer : ColorBuffer! var depthBuffer : DepthBuffer? . . . }
頂点列 頂点処理 頂点処理 の 再構築 プリミティブアセンブリ ラスタライズ フラグメント処理 ピクセル操作 フレームバッファ
頂点処理 let convertedVertices = vertexBuffer.map { (vertex:Vertex4<T>) -> Vertex3<U> in let v = vertexShader(vertex) // divide by w let pos = v.position.project() // Viewport Transformation let screenPosition = transform(position: pos, toViewport: vp) return Vertex3<U>(position: screenPosition, attribute: v.attribute) }
プリミティブ アセンブリ の 再構築 頂点列 頂点処理 プリミティブアセンブリ ラスタライズ フラグメント処理 ピクセル操作 フレームバッファ
プリミティブアセンブリ enum Primitive<T:Blendable> { case point(Vertex3<T>) case line(Vertex3<T>, Vertex3<T>) case triangle(Vertex3<T>, Vertex3<T>, Vertex3<T>) }
プリミティブアセンブリ var primitives: [Primitive<U>] . . . let numOfTriangles = convertedVertices.count / 3 primitives = (0..<numOfTriangles).map { Primitive<U>.triangle(convertedVertices[$0*3], convertedVertices[$0*3 + 1], convertedVertices[$0*3 + 2]) }.filter{ (primitive) -> Bool in if cullFace { return primitive.isCCW() } else { return true } }
頂点列 頂点処理 ラスタライズ の 再構築 プリミティブアセンブリ ラスタライズ フラグメント処理 ピクセル操作 フレームバッファ
ラスタライズ var result = [Fragment<U>]() . . .// minX, maxX, minY, maxYを求めておく for py in minY..<maxY { for px in minX..<maxX { let p = float2(Float(px) + 0.5, Float(py) + 0.5) let (w1, w2, w3) = weight(v1: v1, v2: v2, v3: v3, of: p) ?? (-1, -1, -1) if w1 < 0 || w2 < 0 || w3 < 0 { continue } let eachZ = w1 * v1.position.z + w2 * v2.position.z + w3 * v3.position.z let eachAttr = w1 * v1.attribute + w2 * v2.attribute + w3 * v3.attribute result.append(Fragment(x: px, y: py, z: eachZ, attribute: eachAttr)) } }
フラグメント 処理 の 再構築 頂点列 頂点処理 プリミティブアセンブリ ラスタライズ フラグメント処理 ピクセル操作 フレームバッファ
フラグメント処理 // Fragment Processing let fragmentResults = fragments.map{ (fragment) -> Fragment<Color4f>? in if let color = fragmentShader(fragment) { return Fragment<Color4f>( x: fragment.x, y: fragment.y, z: fragment.z, attribute: color) } else { return nil } }.compactMap { $0 }
ピクセル 操作 の 再構築 頂点列 頂点処理 プリミティブアセンブリ ラスタライズ フラグメント処理 ピクセル操作 フレームバッファ
ピクセル操作 // Per Sampling Operation fragmentResults.forEach { (fragment) in if let depthBuffer = depthBuffer { if depthBuffer[fragment.x, fragment.y] > fragment.z { return } depthBuffer[fragment.x, fragment.y] = fragment.z } colorBuffer[fragment.x, fragment.y] = fragment.attribute.toColor4ui() }
完成
DEMO
解説:MatCap(Material Captures)
解説:MatCap(Material Captures) 1.0 1.0 テクスチャ
解説:MatCap(Material Captures) 1.0 1.0 テクスチャ 単位法線ベクトル
解説:MatCap(Material Captures) 1.0 1.0 テクスチャ 半分にして(0.5,0.5)オフセット 単位法線ベクトル
解説:MatCap(Material Captures) 1.0 1.0 テクスチャ 単位法線ベクトル
まとめ
まとめ ・レンダリングパイプラインは作れる
まとめ ・レンダリングパイプラインは作れる ・自分で作ってみると理解が深まる
まとめ ・レンダリングパイプラインは作れる ・自分で作ってみると理解が深まる ・GPUすごい
まとめ ・レンダリングパイプラインは作れる ・自分で作ってみると理解が深まる ・GPUすごい ・今どきのスマートフォンのCPUもすごい
まとめ ・レンダリングパイプラインは作れる ・自分で作ってみると理解が深まる ・GPUすごい ・今どきのスマートフォンのCPUもすごい ・プログラミング楽しい
Enjoy Programming!!
テクスチャマッピング素材: https://www.3dxo.com/textures 顔素材: わたし MatCap素材: https://www.pixelfondue.com/blog/30matcaps