Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

ゲームの物理 剛体編

Avatar for Fadis Fadis
December 06, 2025

ゲームの物理 剛体編

ビデオゲームで用いられる物理シミュレーションの手法 Position Based Dynamicsを剛体を扱えるように拡張したDetailed Rigid Body Simulation
with Extended Position Based Dynamicsを解説します
これは2025年12月6日に行われた Kernel/VM探検隊@北陸 Part 8 での発表動画です
発表動画 : https://www.youtube.com/watch?v=3fUvEXuhRnc
ソースコード : https://github.com/Fadis/gct/

Avatar for Fadis

Fadis

December 06, 2025
Tweet

More Decks by Fadis

Other Decks in Programming

Transcript

  1. v0 (t + Δt) − k m f0 (p0 (t

    + Δt), p1 (t + Δt), p2 (t + Δt), p3 (t + Δt), n)Δt = v0 (t) + Fext m Δt v1 (t + Δt) − k m f1 (p0 (t + Δt), p1 (t + Δt), p2 (t + Δt), p3 (t + Δt), n)Δt = v1 (t) v2 (t + Δt) − k m f2 (p0 (t + Δt), p1 (t + Δt), p2 (t + Δt), p3 (t + Δt), n)Δt = v2 (t) v3 (t + Δt) − k m f3 (p0 (t + Δt), p1 (t + Δt), p2 (t + Δt), p3 (t + Δt), n)Δt = v3 (t) p0 (t + Δt) − v0 (t + Δt)Δt = p0 (t) p1 (t + Δt) − v1 (t + Δt)Δt = p1 (t) p2 (t + Δt) − v2 (t + Δt)Δt = p2 (t) p3 (t + Δt) − v3 (t + Δt)Δt = p3 (t) ӡಈํఔࣜΛ௚઀ղ͘ํ๏͸ ඇઢܗ࿈ཱํఔࣜΛ ղ͘ඞཁ͕͋Γ ͕͔͔࣌ؒΓ͗͢Δ
  2. Müller, M., Heidelberger, B., Hennix, M., & Ratcliff, J. (2006).

    Position Based Dynamics. In Vriphys: 3rd Workshop in Virtual Realitiy, Interactions, and Physical Simulation. The Eurographics Association. https://doi.org/10.2312/PE/vriphys/vriphys06/071-080 https://matthias-research.github.io/pages/publications/posBasedDyn.pdf Position Based Dynamics
  3. ଎౓Λ࢖ͬͯ ཻࢠͷ ҐஔΛߋ৽ น Position Based Dynamics ෺ཧతʹ͓͔͍͠౓ ΛٻΊΔ ͓͔͍͠౓͕

    খ͘͞ͳΔํ΁ ҐஔΛमਖ਼ मਖ਼͞ΕͨҐஔʹ Ҡಈ͢ΔΑ͏ʹ ଎౓Λमਖ਼
  4. ଎౓Λ࢖ͬͯ ཻࢠͷ ҐஔΛߋ৽ น Position Based Dynamics ෺ཧతʹ͓͔͍͠౓ ΛٻΊΔ ͓͔͍͠౓͕

    খ͘͞ͳΔํ΁ ҐஔΛमਖ਼ मਖ਼͞ΕͨҐஔʹ Ҡಈ͢ΔΑ͏ʹ ଎౓Λमਖ਼
  5. ଎౓Λ࢖ͬͯ ཻࢠͷ ҐஔΛߋ৽ น Position Based Dynamics ෺ཧతʹ͓͔͍͠౓ ΛٻΊΔ ͓͔͍͠౓͕

    খ͘͞ͳΔํ΁ ҐஔΛमਖ਼ मਖ਼͞ΕͨҐஔʹ Ҡಈ͢ΔΑ͏ʹ ଎౓Λमਖ਼
  6. ଎౓Λ࢖ͬͯ ཻࢠͷ ҐஔΛߋ৽ น Position Based Dynamics ෺ཧతʹ͓͔͍͠౓ ΛٻΊΔ ͓͔͍͠౓͕

    খ͘͞ͳΔํ΁ ҐஔΛमਖ਼ मਖ਼͞ΕͨҐஔʹ Ҡಈ͢ΔΑ͏ʹ ଎౓Λमਖ਼
  7. ଎౓Λ࢖ͬͯ ཻࢠͷ ҐஔΛߋ৽ น Position Based Dynamics ෺ཧతʹ͓͔͍͠౓ ΛٻΊΔ ͓͔͍͠౓͕

    খ͘͞ͳΔํ΁ ҐஔΛमਖ਼ मਖ਼͞ΕͨҐஔʹ Ҡಈ͢ΔΑ͏ʹ ଎౓Λमਖ਼ ӡಈํఔ͕ࣜඇઢܗͰ΋ ͜Ε͕Ґஔʹରͯ͠ઢܗͳΒ ઢܗͷ࿈ཱํఔࣜΛղ͘໰୊ʹͳΔ ߴ଎ʹղ͚Δ
  8. ஄ੑମ͸ݩͷେ͖͞Λ ҡ࣋͠Α͏ͱ͢Δ ݩͷେ͖͞d Co1 (p0 , p1) = p0 −

    p1 − d p0 p1 p0 p1 p0 p1 ڑ཭੍໿ ෍͸ཧ༝΋ͳ͘৳ͼͯ͸͍͚ͳ͍
  9. ଞͷ෺ମ͕஄ੑମΛ ؏௨ͯ͠Ҡಈͯ͠͸͍͚ͳ͍ C01 (p0 , p1) = (p1 − p0)

    ⋅ n0 িಥ੍໿ ؏௨͠Α͏ͱͨ͠෺ମ͸ ؏௨͢ΔલͷҐஔʹ໭͞ΕΔ ؏௨͞ΕΔଆͷ෺ମ͸ԡ͞ΕΔ n0 p0 p1 p1
  10. Matthias Müller, Miles Macklin, Nuttapong Chentanez, Stefan Jeschke, and Tae-Yong

    Kim. 2020. Detailed rigid body simulation with extended position based dynamics. In Proceedings of the ACM SIGGRAPH/Eurographics Symposium on Computer Animation (SCA '20). Eurographics Association, Goslar, DEU, Article 10, 1–12. Detailed Rigid Body Simulation with Extended Position Based Dynamics https://doi.org/10.1111/cgf.14105
  11. Δλij = −Cij (pi,t+Δt) − 1 kij Δt2 λij,t+Δt ∇p

    C (pi,t+Δt) M−1 ∇p C (pi,t+Δt) T + 1 kij Δt2 Δpi = 1 mi ∇p C (pi,t+Δt) T Δλij Miles Macklin, Matthias Müller, and Nuttapong Chentanez. 2016. XPBD: position-based simulation of compliant constrained dynamics. In Proceedings of the 9th International Conference on Motion in Games (MIG '16). Association for Computing Machinery, New York, NY, USA, 49–54. https://doi.org/10.1145/2994258.2994272 XPBD- Position-Based Simulation of Compliant Constrained Dynamics
  12. Δλij = −Cij (pi,t+Δt) − 1 kij Δt2 λij,t+Δt ∇p

    C (pi,t+Δt) M−1 ∇p C (pi,t+Δt) T + 1 kij Δt2 Δpi = 1 mi ∇p C (pi,t+Δt) T Δλij Miles Macklin, Matthias Müller, and Nuttapong Chentanez. 2016. XPBD: position-based simulation of compliant constrained dynamics. In Proceedings of the 9th International Conference on Motion in Games (MIG '16). Association for Computing Machinery, New York, NY, USA, 49–54. https://doi.org/10.1145/2994258.2994272 XPBD- Position-Based Simulation of Compliant Constrained Dynamics Lagrange Multiplier
  13. Δλij = −Cij (pi,t+Δt) − 1 kij Δt2 λij,t+Δt ∇p

    C (pi,t+Δt) M−1 ∇p C (pi,t+Δt) T + 1 kij Δt2 Δpi = 1 mi ∇p C (pi,t+Δt) T Δλij :߶ੑ஋ k :λΠϜεςοϓ t :࣌ࠁ ʹ͓͚Δ ൪໨ͷཻࢠͷҐஔ pi,t t i :ཻࢠͷ࣭ྔΛ ର֯੒෼ͱ͢Δߦྻͷٯߦྻ M−1 : ൪໨ͷཻࢠͷ࣭ྔ mi i : ൪໨ͷཻࢠͷҐஔͷमਖ਼ྔ Δpi i :શͯͷ੍໿ C :ཻࢠ ͱཻࢠ ͷؒͷ੍໿ Cij i j
  14. Δλij = −Cij (pi,t+Δt) − 1 kij Δt2 λij,t+Δt ∇p

    C (pi,t+Δt) M−1 ∇p C (pi,t+Δt) T + 1 kij Δt2 ੍໿ ʹؔ༩͍ͯ͠Δ࣭ྔͷٯ਺ͷ૯࿨ Cij িಥ੍໿ͳΒিಥ͍ͯ͠Δ2ͭͷ෺ମ͚͕ͩ ؔ༩͍ͯ͠Δҝ ∇p C (pi,t+Δt) M−1 ∇p C (pi,t+Δt) T = 1 mi + 1 mj ෺ମAͷ࣭ྔ ෺ମBͷ࣭ྔ
  15. Δλij = −Cij (pi,t+Δt) − 1 kij Δt2 λij,t+Δt 1

    mi + 1 mj + 1 kij Δt2 িಥ੍໿ ͸ ΊΓࠐΈͷਂ͞ ͕࢖͑Δ Cij (pi,t+Δt) c ෺ମAͷ ๏ઢ ෺ମAͷද໘ ෺ମBͷද໘ c n
  16. Δλij = −c − 1 kij Δt2 λij,t+Δt 1 mi

    + 1 mj + 1 kij Δt2 มܗ͠ͳ͍߶ମ͸ ߶ੑ஋ ͕ඇৗʹେ͖͍෺ମͱݟ၏ͤΔ ͸΄΅ ʹͳΔͷͰແࢹͰ͖Δ k 1 kij Δt2 0 ෺ମAͷ ๏ઢ ෺ମAͷද໘ ෺ମBͷද໘ c n
  17. Δpi = 1 mi ∇p C (pi,t+Δt) T Δλij Δλij

    = −c 1 mi + 1 mj Ґஔͷमਖ਼ํ޲ ͸ ΊΓࠐΈͷղফํ޲ ͕࢖͑Δ C (pi,t+Δt) n ෺ମAͷ ๏ઢ ෺ମAͷද໘ ෺ମBͷද໘ c n
  18. Δλij = −c 1 mi + 1 mj Δpi =

    1 mi nΔλij ? ߶ମಉ࢜ͷিಥ͸ ௚ਐͱճసͷ ૊Έ߹ΘͤʹΑͬͯ ղফ͞ΕΔ ޲͖ͷमਖ਼ྔ Λ ٻΊΔඞཁ͕͋Δ Δqi Δqi =
  19. Δλij = −c 1 mi + 1 mj Δpi =

    1 mi nΔλij ? ͱ ͷҐஔͷमਖ਼ྔͷൺ͕ pi pj 1 mi 1 mi + 1 mj : 1 mj 1 mi + 1 mj Δqi =
  20. Δλij = −c 1 mi + 1 mj Δpi =

    1 mi nΔλij ? ͱ ͷҐஔͷमਖ਼ྔͷൺ͕ pi pj 1 mi 1 mi + 1 mj : 1 mj 1 mi + 1 mj িಥΛղফ͢Δࡍʹ ΑΓಈ͖΍͍͢෺ͷํ͕ΑΓେ͖͘ಈ͘ ॏ Δqi =
  21. Δλij = −c 1 mi + 1 mj Δpi =

    1 mi nΔλij ? ͱ ͷҐஔͷमਖ਼ྔͷൺ͕ pi pj 1 mi 1 mi + 1 mj : 1 mj 1 mi + 1 mj িಥΛղফ͢Δࡍʹ ΑΓಈ͖΍͍͢෺ͷํ͕ΑΓେ͖͘ಈ͘ ͜ͷΞΠσΞΛճసʹ֦ு͢Δ ॏ Δqi =
  22. Δλij = −c 1 mi + 1 mj + (ri

    × n) T I−1 i (ri × n) + (rj × n) T I−1 j (rj × n) Δpi = 1 mi nΔλij Δθi = ? ෺ମAͷද໘ͷ෺ମBͱͿ͔͍ͭͬͯΔҐஔͱ ෺ମAͷॏ৺ͷࠩ ׳ੑϞʔϝϯτςϯιϧ (Ͳͷ޲͖ʹͲͷ͘Β͍ճΓ΍͍͔͢)ͷٯ਺ Ͱ෺ମAΛ Bͱিಥͨ͠ҐஔͰ ํ޲ʹԡͨ͠৔߹ʹ Ͳͷఔ౓ճΓ΍͍͔͕͢εΧϥͰಘΒΕΔ (ri × n) T I−1 i (ri × n) n
  23. Δλij = −c 1 mi + 1 mj + (ri

    × n) T I−1 i (ri × n) + (rj × n) T I−1 j (rj × n) Δpi = 1 mi nΔλij Δθi = ? ෺ମAͷද໘ͷ෺ମBͱͿ͔͍ͭͬͯΔҐஔͱ ෺ମAͷॏ৺ͷࠩ Ͱ෺ମAΛ Bͱিಥͨ͠ҐஔͰ ํ޲ʹԡͨ͠৔߹ʹ Ͳͷఔ౓ճΓ΍͍͔͕͢εΧϥͰಘΒΕΔ (ri × n) T I−1 i (ri × n) n ճΓ΍͍͢ఔ ͕খ͘͞ͳΓ௚ਐͷׂ߹͕খ͘͞ͳΔ Δλij
  24. F = m d2x dt2 ྗ ࣭ྔ Ґஔ N =

    I d2θ dt2 τϧΫ ׳ੑϞʔϝϯτςϯιϧ ޲͖ (ྗ ॏ৺͔Βͷڑ཭) × ҟͳΔ෺ཧྔͷؒʹಉ͡Α͏ͳؔ܎͕੒Γཱ͍ͬͯΔ
  25. q = a + bi + cj + dk i2

    = j2 = k2 = ijk = − 1 jk = − kj = i ki = − ik = j ij = − ji = k ͨͩ͠ ෳૉ਺Λ4ཁૉʹ֦ுͨ͠෺ ࢛ݩ਺
  26. q = a + bi + cj + dk i2

    = j2 = k2 = ijk = − 1 jk = − kj = i ki = − ik = j ij = − ji = k ͨͩ͠ ෳૉ਺Λ4ཁૉʹ֦ுͨ͠෺ ࢛ݩ਺ ෳૉ਺͸2࣍ݩͷճసΛදݱͰ͖Δ͕ ࢛ݩ਺͸3࣍ݩͷճసΛදݱͰ͖Δ
  27. Δλij = −c 1 mi + 1 mj + (ri

    × n) T I−1 i (ri × n) + (rj × n) T I−1 j (rj × n) Δpi = 1 mi nΔλij Δqi = 1 2 [ωi ,0] q1,s ωi = I−1 i (ri × (nΔλij)) qi,s+1 = qi,s + 1 2 [ωi ,0] q1,s
  28. E (x) = 1(x ≠ [0,0,0]) 0(x = [0,0,0]) p0

    = p0 + 1 ∑ j E (Δp0j) ∑ j Δp0j p1 = p1 + 1 ∑ j E (Δp1j) ∑ j Δp1j p2 = p2 + 1 ∑ j E (Δp2j) ∑ j Δp2j p3 = p3 + 1 ∑ j E (Δp3j) ∑ j Δp3j ⋮ ಉ͡෺ମͷҐஔΛ ॻ͖׵͑Δશͯͷ ͷฏۉΛऔΔ Δp ͜ͷ࿈ཱํఔࣜΛ ϠίϏ๏Ͱղ͘ ࣜຖʹฒྻͰܭࢉ Symmetric Successive Over Relaxation(SSOR)
  29. ΋ಉ༷ͷํ๏Ͱܭࢉ͍ͨ͠ q Symmetric Successive Over Relaxation(SSOR) E (x) = 1(x

    ≠ [0,0,0,0]) 0(x = [0,0,0,0]) q0 = q0 + 1 ∑ j E (Δq0j) ∑ j Δq0j q1 = q1 + 1 ∑ j E (Δq1j) ∑ j Δq1j q2 = q2 + 1 ∑ j E (Δq2j) ∑ j Δq2j q3 = q3 + 1 ∑ j E (Δq3j) ∑ j Δq3j ⋮ Δqi = 1 2 [ωi ,0] q1,s ճసͷ߹੒͸ճసલͷ ͱͷੵͳͷͰ ෳ਺ͷಠཱʹٻΊΒΕͨ ͷ ૯࿨ͱ౳͘͠ͳΒͳ͍ q Δq ߶ମͷ޲͖ΛSSORͰղ͜͏ͱ͢Δͱ ਖ਼͘͠ͳ͍ܭࢉʹͳΔ
  30. Symmetric Successive Over Relaxation(SSOR) E (x) = 1(x ≠ [0,0,0,0])

    0(x = [0,0,0,0]) q0 = q0 + 1 ∑ j E (Δq0j) ∑ j Δq0j q1 = q1 + 1 ∑ j E (Δq1j) ∑ j Δq1j q2 = q2 + 1 ∑ j E (Δq2j) ∑ j Δq2j q3 = q3 + 1 ∑ j E (Δq3j) ∑ j Δq3j ⋮ ઢܗۙࣅͰ͖Δ͘Β͍খ͍͞ճసͷ৔߹ ෳ਺ͷճసͷ߹੒͸ ͷ࿨ͰۙࣅͰ͖Δ q িಥΛղফ͢ΔͨΊͷճస͸ িಥͷਂ͕͞ઙ͚Ε͹͔ᷮ े෼খ͍͞λΠϜεςοϓͰ ਂ͍ΊΓࠐΈΛڐ͞ͳ͚Ε͹ ߶ମͷ޲͖ΛSSORͰܭࢉͰ͖Δ
  31. ෺ମBͷিಥϦετ ෺ମ"ͷ͜͜ ෺ମ"ͷ͜͜ Δpi = 1 mi nΔλij Δqi =

    1 2 [ωi ,0] q1,s Δpi = 1 mi nΔλij Δqi = 1 2 [ωi ,0] q1,s Δp Δq
  32. rigid_collision_dx_dq sum = rigid_collision_dx_dq( vec3( 0, 0, 0 ), vec4(

    0, 0, 0, 0 ), 0u, 0u ); for( uint constraint_block_id = 0u; constraint_block_id != 16u; constraint_block_id++ ) { rigid_collision_dx_dq dx_dq = rigid_collision_constraint_dx( rigid.collision_constraint_offset, constraint_block_id * gl_SubgroupSize + constraint_index, h ); dx_dq.dx_count = subgroupAdd( dx_dq.dx_count ); dx_dq.dq_count = subgroupAdd( dx_dq.dq_count ); dx_dq.dx = subgroupAdd( dx_dq.dx ); dx_dq.dq = subgroupAdd( dx_dq.dq ); sum.dx += dx_dq.dx; sum.dq += dx_dq.dq; sum.dx_count += dx_dq.dx_count; sum.dq_count += dx_dq.dq_count; } 1ͭͷ߶ମ=1Subgroup 1ͭͷ߶ମͷিಥϦετͷαΠζ : 512 GPUͷSubgroupͷαΠζ: 32 ϧʔϓͷճ਺: 16ճ
  33. rigid_collision_dx_dq rigid_collision_constraint_dx( uint constraint_offset, uint constraint_index, float dt ) {

    const uint iter = rigid_collision_constraint_next( rigid_collision_constraint_begin( constraint_offset ), constraint_index ); bool is_end = rigid_collision_constraint_is_end( iter ); const uvec2 dc = is_end ? uvec2( 0, 0 ) : uvec2( rigid_collision_constraint_get_from( iter + constraint_index ), rigid_collision_constraint_get_to( iter + constraint_index ) ); const uint rigid0 = particle_pool[ dc.x ].rigid; const vec3 p0 = particle_pool[ dc.x ].position; const vec3 p_prev0 = particle_pool[ dc.x ].previous_position; const vec4 q0 = matrix_pool[ rigid_pool[ rigid0 ].trs ][ 1 ]; const vec3 n0 = particle_pool[ dc.x ].normal; const vec3 r0 = particle_pool[ dc.x ].local_r; const float inv_m0 = 1.0/rigid_pool[ rigid0 ].mass; const mat3 inv_i0 = mat3( matrix_pool[ rigid_pool[ rigid0 ].inversed_momentum_inertia_tensor ] ); const uint rigid1 = particle_pool[ dc.y ].rigid; িಥ͍ͯ͠Δ2ͭͷཻࢠͷIDΛಘΔ
  34. const uint rigid0 = particle_pool[ dc.x ].rigid; const vec3 p0

    = particle_pool[ dc.x ].position; const vec3 p_prev0 = particle_pool[ dc.x ].previous_position; const vec4 q0 = matrix_pool[ rigid_pool[ rigid0 ].trs ][ 1 ]; const vec3 n0 = particle_pool[ dc.x ].normal; const vec3 r0 = particle_pool[ dc.x ].local_r; const float inv_m0 = 1.0/rigid_pool[ rigid0 ].mass; const mat3 inv_i0 = mat3( matrix_pool[ rigid_pool[ rigid0 ].inversed_momentum_inertia_tensor ] ); const uint rigid1 = particle_pool[ dc.y ].rigid; const vec3 p1 = particle_pool[ dc.y ].position; const vec3 p_prev1 = particle_pool[ dc.y ].previous_position; const vec3 n1 = particle_pool[ dc.y ].normal; const vec3 r1 = particle_pool[ dc.y ].local_r; const float inv_m1 = 1.0/rigid_pool[ rigid1 ].mass; const mat3 inv_i1 = mat3( matrix_pool[ rigid_pool[ rigid1 ].inversed_momentum_inertia_tensor ] ); if( dot( n0, n1 ) >= 0.0 ) is_end == true; const float scale = 0.05; const float friction_scale = 0.02; const float c = dot( n1, p1 - p0 ) * scale; if( c < 0.0 ) is_end = true; const vec3 n = n1; const float w0 = inv_m0 + dot( cross( r0, n1 ), inv_i0 * cross( r0, n1 ) ); const float w1 = inv_m1 + dot( cross( r1, n0 ), inv_i1 * cross( r1, n0 ) ); const float dl = ( -c ) / ( w0 + w1 ); vec3 p = inv_m0 * n * dl; ཻࢠͷID͔Β ཻࢠͷৄࡉΛಘΔ rigid0 : ཻࢠ͕ଐ͍ͯ͠Δ߶ମͷID p0 : ཻࢠͷҐஔ p_prev0 : 1εςοϓલͷཻࢠͷҐஔ q0 : ߶ମͷ޲͖ n0 : p0ͷҐஔʹ͓͚Δ߶ମͷද໘ͷ๏ઢ r0 : ߶ମͷॏ৺͔Βp0·ͰͷϕΫτϧ inv_m0 : ߶ମͷ࣭ྔͷٯ਺ inv_i0 : ߶ମͷ׳ੑϞʔϝϯτͷٯߦྻ
  35. const uint rigid0 = particle_pool[ dc.x ].rigid; const vec3 p0

    = particle_pool[ dc.x ].position; const vec3 p_prev0 = particle_pool[ dc.x ].previous_position; const vec4 q0 = matrix_pool[ rigid_pool[ rigid0 ].trs ][ 1 ]; const vec3 n0 = particle_pool[ dc.x ].normal; const vec3 r0 = particle_pool[ dc.x ].local_r; const float inv_m0 = 1.0/rigid_pool[ rigid0 ].mass; const mat3 inv_i0 = mat3( matrix_pool[ rigid_pool[ rigid0 ].inversed_momentum_inertia_tensor ] ); const uint rigid1 = particle_pool[ dc.y ].rigid; const vec3 p1 = particle_pool[ dc.y ].position; const vec3 p_prev1 = particle_pool[ dc.y ].previous_position; const vec3 n1 = particle_pool[ dc.y ].normal; const vec3 r1 = particle_pool[ dc.y ].local_r; const float inv_m1 = 1.0/rigid_pool[ rigid1 ].mass; const mat3 inv_i1 = mat3( matrix_pool[ rigid_pool[ rigid1 ].inversed_momentum_inertia_tensor ] ); if( dot( n0, n1 ) >= 0.0 ) is_end == true; const float scale = 0.05; const float friction_scale = 0.02; const float c = dot( n1, p1 - p0 ) * scale; if( c < 0.0 ) is_end = true; const vec3 n = n1; const float w0 = inv_m0 + dot( cross( r0, n1 ), inv_i0 * cross( r0, n1 ) ); const float w1 = inv_m1 + dot( cross( r1, n0 ), inv_i1 * cross( r1, n0 ) ); const float dl = ( -c ) / ( w0 + w1 ); vec3 p = inv_m0 * n * dl; িಥ͍ͯ͠Δ૬खͷ৘ใ΋ಉ༷ʹಘΔ
  36. if( dot( n0, n1 ) >= 0.0 ) is_end ==

    true; const float scale = 0.05; const float friction_scale = 0.02; const float c = dot( n1, p1 - p0 ) * scale; if( c < 0.0 ) is_end = true; const vec3 n = n1; const float w0 = inv_m0 + dot( cross( r0, n1 ), inv_i0 * cross( r0, n1 ) ); const float w1 = inv_m1 + dot( cross( r1, n0 ), inv_i1 * cross( r1, n0 ) ); const float dl = ( -c ) / ( w0 + w1 ); vec3 p = inv_m0 * n * dl; const vec3 relative_velocity = ( p0 - p_prev0 ) - ( p1 - p_prev1 ); const vec3 friction = ( relative_velocity - dot( relative_velocity, n ) * n ) * friction_scale; vec3 dx = p + friction; vec4 dq = 1.0/2.0 * quaternion_quaternion_mult( vec4( inv_i0 * cross( r0, n * dl ), 0.0 ), q0 ); dx = is_end ? vec3( 0, 0, 0 ) : dx; dq = is_end ? vec4( 0, 0, 0, 0 ) : dq; return rigid_collision_dx_dq( dx, dq, dx == vec3( 0, 0, 0 ) ? 0u : 1u, dq == vec4( 0, 0, 0, 0 ) ? 0u : 1u ); } Δλij = −c 1 mi + (ri × n) T I−1 i (ri × n) + 1 mj + (rj × n) T I−1 j (rj × n) w0 w1 Δpi = 1 mi nΔλij
  37. const vec3 relative_velocity = ( p0 - p_prev0 ) -

    ( p1 - p_prev1 ); const vec3 friction = ( relative_velocity - dot( relative_velocity, n ) * n ) * friction_scale; vec3 dx = p + friction; vec4 dq = 1.0/2.0 * quaternion_quaternion_mult( vec4( inv_i0 * cross( r0, n * dl ), 0.0 ), q0 ); dx = is_end ? vec3( 0, 0, 0 ) : dx; dq = is_end ? vec4( 0, 0, 0, 0 ) : dq; return rigid_collision_dx_dq( dx, dq, dx == vec3( 0, 0, 0 ) ? 0u : 1u, dq == vec4( 0, 0, 0, 0 ) ? 0u : 1u ); } p0 − pprev0 p1 − pprev1 ઀৮͢Δ2ͭͷཻࢠͷ ઀ฏ໘্Ͱͷ ૬ର଎౓͕0Ͱͳ͍৔߹ ૬ର଎౓ΛݮΒ͢ํ޲ʹ ߶ମͷҐஔΛमਖ਼͢Δ ຎࡲ
  38. const vec3 relative_velocity = ( p0 - p_prev0 ) -

    ( p1 - p_prev1 ); const vec3 friction = ( relative_velocity - dot( relative_velocity, n ) * n ) * friction_scale; vec3 dx = p + friction; vec4 dq = 1.0/2.0 * quaternion_quaternion_mult( vec4( inv_i0 * cross( r0, n * dl ), 0.0 ), q0 ); dx = is_end ? vec3( 0, 0, 0 ) : dx; dq = is_end ? vec4( 0, 0, 0, 0 ) : dq; return rigid_collision_dx_dq( dx, dq, dx == vec3( 0, 0, 0 ) ? 0u : 1u, dq == vec4( 0, 0, 0, 0 ) ? 0u : 1u ); } ωi = I−1 i (ri × (nΔλij)) Δqi = 1 2 [ωi ,0] q1,s
  39. const vec3 relative_velocity = ( p0 - p_prev0 ) -

    ( p1 - p_prev1 ); const vec3 friction = ( relative_velocity - dot( relative_velocity, n ) * n ) * friction_scale; vec3 dx = p + friction; vec4 dq = 1.0/2.0 * quaternion_quaternion_mult( vec4( inv_i0 * cross( r0, n * dl ), 0.0 ), q0 ); dx = is_end ? vec3( 0, 0, 0 ) : dx; dq = is_end ? vec4( 0, 0, 0, 0 ) : dq; return rigid_collision_dx_dq( dx, dq, dx == vec3( 0, 0, 0 ) ? 0u : 1u, dq == vec4( 0, 0, 0, 0 ) ? 0u : 1u ); } ٻΊͨ ͱ Λฦ͢ Δp Δq
  40. R = cos θ −sin θ 0 0 sin θ

    cos θ 0 0 0 0 1 0 0 0 0 1 T = 1 0 0 x 0 1 0 y 0 0 1 z 0 0 0 1 S = x 0 0 0 0 y 0 0 0 0 z 0 0 0 0 1 xworld yworld zworld 1 = m00 m01 m02 m03 m10 m11 m12 m13 m20 m21 m22 m23 0 0 0 1 xlocal ylocal zlocal 1 Ҡಈ ճస ֦େॖখ m00 m01 m02 m03 m10 m11 m12 m13 m20 m21 m22 m23 0 0 0 1 ಉ࣍࠲ඪ
  41. xworld yworld zworld 1 = M xlocal ylocal zlocal 1

    M M = ߦྻ ͱϕΫτϧͷੵΛٻΊΔ͚ͩͰ ্ͷશͯͷม׵Λߦͬͨͷͱ౳ՁʹͳΔ M TRTTTRSTRTRS⋯ M
  42. Δpi = 1 mi nΔλij Δqi = 1 2 [ωi

    ,0] q1,s m00 m01 m02 m03 m10 m11 m12 m13 m20 m21 m22 m23 0 0 0 1 ࠷ॳͷҐஔͱ޲͖͕ ͜ͷܗͰ౉ͬͯ͘Δ ܭࢉ݁Ռ͸ ͜ͷܗͰॻ͖ࠐΉ ෺ཧγϛϡϨʔγϣϯ͸ ҠಈͱճసΛ ผʑʹܭࢉ͢Δ ߦྻ͔ΒҠಈͱճసΛ෼཭ͯ͠໭͢ඞཁ͕͋Δ
  43. M = m00 m01 m02 m03 m10 m11 m12 m13

    m20 m21 m22 m23 0 0 0 1 M = TRS ΋͠ ͕Ҡಈ ͱճస ͱ֦େॖখ ͷੵͰදͤΔͳΒ M T R S T = 1 0 0 m03 0 1 0 m13 0 0 1 m23 0 0 0 1 TRS෼ղ
  44. T−1M = m00 m01 m02 0 m10 m11 m12 0

    m20 m21 m22 0 0 0 0 1 T−1M = RS ΋͠ ͕Ҡಈ ͱճస ͱ֦େॖখ ͷੵͰදͤΔͳΒ M T R S S = [m00 , m10 , m20] 0 0 0 0 [m01 , m11 , m21] 0 0 0 0 [m02 , m12 , m22] 0 0 0 0 1 TRS෼ղ
  45. T−1MS−1 = m00 [m00 , m10 , m20] m01 [m01

    , m11 , m21] m02 [m02 , m12 , m22] 0 m10 [m00 , m10 , m20] m11 [m01 , m11 , m21] m12 [m02 , m12 , m22] 0 m20 [m00 , m10 , m20] m21 [m01 , m11 , m21] m22 [m02 , m12 , m22] 0 0 0 0 1 T−1MS−1 = R ͜ͷ࣌఺Ͱ࢒Γ͕ਖ਼ن௚ަߦྻʹͳΔ R = m00 [m00 , m10 , m20] m01 [m01 , m11 , m21] m02 [m02 , m12 , m22] 0 m10 [m00 , m10 , m20] m11 [m01 , m11 , m21] m12 [m02 , m12 , m22] 0 m20 [m00 , m10 , m20] m21 [m01 , m11 , m21] m22 [m02 , m12 , m22] 0 0 0 0 1 TRS෼ղ
  46. mat4 matrix_to_trs( mat4 m ) { vec3 t = vec3(

    m[ 3 ] ); const bool inverted = dot( cross( vec3( m[ 0 ] ), vec3( m[ 1 ] ) ), vec3( m[ 2 ] ) ) < 0.0; vec3 s = vec3( length( vec3( m[ 0 ] ) ) * ( inverted ? -1 : 1 ), length( vec3( m[ 1 ] ) ) * ( inverted ? -1 : 1 ), length( vec3( m[ 2 ] ) ) * ( inverted ? -1 : 1 ) ); mat3 ms = mat3( m ); ms[ 0 ] /= s[ 0 ]; ms[ 1 ] /= s[ 1 ]; ms[ 2 ] /= s[ 2 ]; vec4 r = matrix_to_quaternion( ms ); return mat4( vec4( t, 1.0 ), r, vec4( s, 1.0 ), vec4( 0, 0, 0, 0 ) ); } TRS෼ղ ճసߦྻΛ࢛ݩ਺ʹ͢Δ
  47. vec4 matrix_to_quaternion( mat3 m ) { const float fourXSquaredMinus1 =

    m[ 0 ][ 0 ] - m[ 1 ][ 1 ] - m[ 2 ][ 2 ]; const float fourYSquaredMinus1 = m[ 1 ][ 1 ] - m[ 0 ][ 0 ] - m[ 2 ][ 2 ]; const float fourZSquaredMinus1 = m[ 2 ][ 2 ] - m[ 0 ][ 0 ] - m[ 1 ][ 1 ]; const float fourWSquaredMinus1 = m[ 0 ][ 0 ] + m[ 1 ][ 1 ] + m[ 2 ][ 2 ]; int biggestIndex = 0; float fourBiggestSquaredMinus1 = fourWSquaredMinus1; if( fourXSquaredMinus1 > fourBiggestSquaredMinus1 ) { fourBiggestSquaredMinus1 = fourXSquaredMinus1; biggestIndex = 1; } if( fourYSquaredMinus1 > fourBiggestSquaredMinus1 ) { fourBiggestSquaredMinus1 = fourYSquaredMinus1; biggestIndex = 2; } if( fourZSquaredMinus1 > fourBiggestSquaredMinus1 ) { fourBiggestSquaredMinus1 = fourZSquaredMinus1; biggestIndex = 3; } const float biggestVal = sqrt( fourBiggestSquaredMinus1 + 1.0 ) * 0.5; const float mult = 0.25 / biggestVal; return ( ( biggestIndex == 0 ) ? vec4( ( m[ 1 ][ 2 ] - m[ 2 ][ 1 ] ) * mult, ( m[ 2 ][ 0 ] - m[ 0 ][ 2 ] ) * mult, ( m[ 0 ][ 1 ] - m[ 1 ][ 0 ] ) * mult, biggestVal ) : ( ( biggestIndex == 1 ) ? vec4( biggestVal, ( m[ 0 ][ 1 ] + m[ 1 ][ 0 ] ) * mult, ( m[ 2 ][ 0 ] + m[ 0 ][ 2 ] ) * mult, ( m[ 1 ][ 2 ] - m[ 2 ][ 1 ] ) * mult ) : ( ( biggestIndex == 2 ) ? vec4( ( m[ 0 ][ 1 ] + m[ 1 ][ 0 ] ) * mult, biggestVal, ( m[ 1 ][ 2 ] + m[ 2 ][ 1 ] ) * mult, ( m[ 2 ][ 0 ] - m[ 0 ][ 2 ] ) * mult ) : vec4( ( m[ 2 ][ 0 ] + m[ 0 ][ 2 ] ) * mult, ( m[ 1 ][ 2 ] + m[ 2 ][ 1 ] ) * mult, biggestVal, ( m[ 0 ][ 1 ] - m[ 1 ][ 0 ] ) * mult ) ) ) ); } ճసߦྻ Λ࢛ݩ਺ ʹม׵ R q GLMͷ glm::gtc::quaternion::quat_cast ΛGLSLʹॻ͖׵͑ https://github.com/g-truc/glm
  48. mat3 quaternion_to_matrix( vec4 q ) { const float qxx =

    q.x * q.x; const float qyy = q.y * q.y; const float qzz = q.z * q.z; const float qxz = q.x * q.z; const float qxy = q.x * q.y; const float qyw = q.y * q.w; const float qzw = q.z * q.w; const float qyz = q.y * q.z; const float qxw = q.x * q.w; return mat3( vec3(1.0 - 2.0 * (qyy + qzz), 2.0 * (qxy - qzw), 2.0 * (qxz + qyw)), vec3(2.0 * (qxy + qzw), 1.0 - 2.0 * (qxx + qzz), 2.0 * (qyz - qxw)), vec3(2.0 * (qxz - qyw), 2.0 * (qyz + qxw), 1.0 - 2.0 * (qxx + qyy)) ); } ࢛ݩ਺ Λճసߦྻ ʹม׵ q R GLMͷ glm::gtc::quaternion::mat3_cast ΛGLSLʹॻ͖׵͑ https://github.com/g-truc/glm
  49. xworld yworld zworld 1 = TRS xlocal ylocal zlocal 1

    ෺ཧγϛϡϨʔγϣϯ͸ॏ৺ճΓͷճసΛٻΊΔ ͸ϩʔΧϧ࠲ඪܥͷݪ఺ճΓͷճసʹͳΔ R
  50. xworld yworld zworld 1 = TRSTc xlocal ylocal zlocal 1

    Tc Tc TRS = MT−1 c q R ϩʔΧϧ࠲ඪܥ͔Βॏ৺Λݪ఺ͱ͢Δ࠲ඪܥ΁ͷม׵ߦྻ
  51. yworld zworld 1 = TRSTc ylocal zlocal 1 Tc TRS

    = MT−1 c q R TRSTc = M R Δqi = 1 2 [ωi ,0] q1,s
  52. ϝογϡ͔ΒཻࢠΛੜ੒ ଎౓ͱ֎ྗΛ࢖ཻͬͯࢠͷҐஔΛߋ৽ ཻࢠͷҐஔΛ࢖ͬͯ଎౓Λमਖ਼ ϨϯμϦϯά Ұఆճ਺܁Γฦͨ͠ GBMTF ϝογϡ͔Β੍໿Λੜ੒ ۭؒతϋογϡςʔϒϧʹॻ͘ ۭؒతϋογϡςʔϒϧΛಡΉ ཻࢠͷҐஔΛ࢖ͬͯϝογϡΛߋ৽

    ΛθϩΫϦΞ͢Δ λ ཻࢠͷҐஔΛमਖ਼ USVF Miles Macklin, Kier Storey, Michelle Lu, Pierre Terdiman, Nuttapong Chentanez, Stefan Jeschke, and Matthias Müller. 2019. Small steps in physics simulation. In Proceedings of the 18th annual ACM SIGGRAPH/Eurographics Symposium on Computer Animation (SCA '19). Association for Computing Machinery, New York, NY, USA, Article 2, 1–7. https://doi.org/10.1145/3309486.3340247 Small Steps in Physics Simulation
  53. িಥ൑ఆཻࢠͷҐஔΛߋ৽ ߶ମͷҐஔͱ޲͖Λߋ৽(ࡉ) ϨϯμϦϯά Ұఆճ਺܁Γฦͨ͠ GBMTF ۭؒతϋογϡςʔϒϧʹॻ͘ ۭؒతϋογϡςʔϒϧΛಡΉ ߶ମͷҐஔͱ޲͖͔ΒߦྻΛٻΊΔ USVF ߶ମͷҐஔͱ޲͖Λमਖ਼

    ߦྻ͔Β߶ମͷҐஔͱ޲͖ΛٻΊΔ ߶ମͷ଎౓ͱ֯଎౓Λमਖ਼(ࡉ) ߶ମͷҐஔͱ޲͖͔ΒߦྻΛٻΊΔ িಥ൑ఆཻࢠͷҐஔΛߋ৽ ߶ମͷҐஔͱ޲͖Λߋ৽ ߥ ߶ମͷ଎౓ͱ֯଎౓Λߋ৽(ߥ) ࿦จͰ͸ λΠϜεςοϓTVCTUFQ TVCTUFQ൓෮ܭࢉճ ࿦จͱಉ͡৚݅Ͱ࣮૷͢Δ λΠϜεςοϓ TVCTUFQ
  54. Detailed Rigid Body Simulation with Extended Position Based Dynamics ·ͱΊ

    XPBDΛ߶ମͷಈ͖Λѻ͑ΔΑ͏ʹ֦ு͢Δ ߶ମ͸ճస͢ΔͷͰ XPBDͷ࿮૊ΈͷதͰճస΋ѻ͑ΔΑ͏ʹ͢Δ