2P by GN⁺ | ★ favorite | 댓글 1개
  • 3D 그래픽스에서 관성처럼 쓰이던 4x4 행렬 대신 Euclidean PGA를 glTF 호환 포워드 렌더러에 끝까지 적용해 보는 실험임
  • 회전과 이동은 8개 float의 PGA motor로 표현되며, 일반 motor 합성은 4x4 행렬 곱의 64곱셈·48덧셈보다 적은 48곱셈·40덧셈으로 처리됨
  • 점 변환은 단순 전개하면 행렬보다 비싸지만, 정규화 조건을 이용한 sandwich product로 21곱셈·18덧셈까지 줄어들고 방향·기저 방향 변환은 더 저렴함
  • tangent space normal mapping에서는 normal과 tangent를 tangentRotor로 대체해 정점 데이터를 12 float에서 9 float로 줄이면서, world-space 변환 비용도 행렬 방식과 비슷한 47곱셈·38덧셈 수준에 맞춤
  • 실제 glTF 콘텐츠와 맞물리려면 로드 시 행렬을 motor로 바꾸고 uniform scale을 별도 float로 추적해야 하며, non-uniform scale은 제한 처리나 4x4 행렬 대체 경로가 필요함

PGA로 만든 행렬 없는 포워드 렌더러

  • 프로젝트는 Look, Ma, No Matrices로, 행렬 없는 포워드 렌더러 구현을 목표로 함
  • 2019년 SIGGRAPH 이후 Geometric Algebra, 특히 Euclidean PGA가 그래픽스와 머신러닝 커뮤니티에서 관심을 얻었지만, 전통적인 3D 그래픽스에서는 dual quaternion을 PGA motor로 다시 부르는 수준에 머문 경우가 많았음
  • 이 구현은 glTF 호환 3D 엔진에 PGA algebra를 통합해, 단순한 대수 이름 바꾸기가 아니라 그래픽스 파이프라인의 여러 부분을 PGA 방식으로 다시 구성함
  • 기준 구현은 Khronos glTF viewer이며, 최적 성능 구현이라기보다 행렬을 타협 없이 대체하는 실험에 가까움
    • 최종적으로는 hybrid solution이 더 나은 선택일 가능성이 높음

4x4 행렬을 의심하는 이유

  • 4x4 행렬은 그래픽스 API와 GPU 고정 기능 파이프라인에서 오랫동안 중심 역할을 했고, 여전히 일반적인 포워드 렌더링의 기본 도구임
  • 현대 GPU는 고정 기능 파이프라인보다 프로그래머블 스칼라 프로세서에 가까워져, 행렬 중심 표현이 반드시 필요하지는 않음
  • 실제 3D 엔진에서 많은 행렬은 회전과 이동만 담는 직교 행렬
  • PGA motor manifold는 Euclidean motion 전체를 더 낮은 계산·메모리 비용으로 표현하며, quaternion과 dual quaternion도 변환 없이 포함할 수 있음

PGA 데이터 표현과 기본 연산

  • PGA algebra는 네 기저 벡터 e0~e3에서 생성됨
    • e1, e2, e3는 각각 x=0, y=0, z=0 평면에 대응함
    • 특수한 degenerate vector e0는 무한원 평면을 나타냄
  • 셰이더에서는 GLSL 내장 타입을 사용해 연산자 오버로딩 없이 덧셈, 뺄셈, 스칼라 곱을 활용함
    • motor mat2x4
    • line mat2x3
    • point vec3
    • direction vec3
  • 일반 PGA motor 합성은 geometric product로 수행됨
    • 4x4 행렬 곱: 64 곱셈, 48 덧셈
    • 일반 motor 합성 gp_mm: 48 곱셈, 40 덧셈
  • 특수한 변환 조합에서는 더 싼 연산이 가능함
    • gp_rr: 16 곱셈, 12 덧셈
    • gp_tt: 0 곱셈, 3 덧셈
    • gp_rt / gp_tr: 12 곱셈, 8 덧셈
    • gp_rm / gp_mr: 32 곱셈, 24 덧셈
    • gp_tm / gp_mt: 12 곱셈, 12 덧셈

점·방향 변환 최적화

  • PGA에서 motor M으로 점 p를 변환할 때는 sandwich product M p M̃를 사용함
  • 단순 전개는 33 곱셈, 29 덧셈으로, 행렬-벡터 곱의 16 곱셈, 12 덧셈보다 큼
  • 정규화된 motor가 M M̃ = 1을 만족한다는 점을 이용해 식을 바꾸면 점 변환을 21 곱셈, 18 덧셈으로 줄일 수 있음
  • 방향, 즉 무한원 점은 implied e123 계수가 0이므로 더 저렴함
    • 일반 방향 변환: 18 곱셈, 12 덧셈
    • basis direction 변환은 예를 들어 x축 변환을 6 곱셈, 4 덧셈까지 낮출 수 있음
  • 이 basis direction 최적화는 이후 tangent frame 처리에서 행렬이 항상 가장 빠르다는 통념을 흔드는 근거가 됨

정규화, 제곱근, 지수·로그 맵

  • PGA motor의 squared pseudonorm은 M M̃ = a + b e0123 형태의 Study Number
  • 정규화는 단순 벡터 정규화가 아니라, 결과 motor가 orthonormal transformation이 되도록 보장하는 절차임
    • 일반 motor 정규화 구현 비용: 21 곱셈, 5 덧셈
    • 순수 translation이나 rotation에서는 더 효율적인 버전을 사용할 수 있음
  • 두 점·두 선·두 평면 a, b 사이의 rigid transformation은 M = sqrt(b / a)로 표현됨
    • 같은 종류의 두 원소에 대한 geometric product baa에서 b로 가는 변환의 두 배에 해당하는 motor를 만듦
    • sqrt M = normalize(1 + M) 형태로 계산 가능함
  • PGA motor의 logarithm은 scaled line이고, scaled line은 exponentiation으로 회전 motor를 만들 수 있음
  • 일반 4x4 행렬의 exponential map은 수치적으로 비싸지만, PGA motor manifold에서는 효율적인 closed form이 가능함

역원과 motor factorization

  • Geometric Algebra는 정규화된 객체의 역원을 효율적으로 계산할 수 있음
    • plane inverse: 자기 자신
    • line inverse: 부호 반전
    • point inverse: 부호 반전
    • motor inverse: reversion
  • 일반 bivector가 Plücker condition을 만족하지 않아 단일 line을 나타내지 않을 때는 Study Number inverse를 이용해 역원을 계산함
  • 렌더링 구현에는 두 가지 factorization이 쓰임
    • Euclidean factorization: motor를 origin 주변 rotation 뒤 translation으로 분해함
    • Invariant factorization: motor를 서로 commuting하는 translation과 rotation으로 분해하며, 3D에서는 Mozzi-Chasles theorem으로 알려진 형태임
  • tangent frame과 object-to-world motor를 합성할 때는 translation에 invariant한 frame 특성 때문에 Euclidean factorization이 유용함

glTF 행렬과 scale 처리

  • 기존 glTF 콘텐츠와 상호 운용하려면 로드 시점에 행렬을 PGA motor로 변환해야 함
  • 4x4 orthogonal matrix는 quaternion과의 동형성을 이용해 motor로 변환함
    • import된 모든 matrix와 transformation은 load time에 변환됨
  • PGA motor는 rigid body transformation을 다루므로 scaling을 포함하지 않음
  • uniform scaling은 rotation과 translation에 invariant하므로 각 노드당 float 하나로 추적함
    • 각 요소의 total scale은 자기 scale과 parent scale의 곱으로 계산함
    • vertex에는 load time 또는 vertex shader 첫 단계에서 total scale을 적용함
    • translation에는 parent scale을 load time과 animation update 시 적용함
  • 약 400개의 무작위 glTF 파일 샘플에서 scale animation이 있는 경우는 0.5% 미만이었고, fixed uniform scale은 꽤 많았음
  • non-uniform scaling은 rotation에 invariant하지 않아 더 까다로움
    • 일반적인 non-uniform scale 처리는 4x4 matrix 대체 경로가 불가피함
    • 샘플 glTF에서는 non-uniform scale이 leaf node에만 적용된 사례를 찾았고, 이 경우 animation key에 영향 없이 나머지 변환 전에 별도로 scale을 적용함

Model-View-Projection 대체

  • 포워드 렌더러는 object space의 mesh geometry를 screen space로 변환하고, 각 triangle이 덮는 pixel을 결정함
  • 일반적인 파이프라인의 model, view, projection matrix 중 model과 view를 PGA motor로 대체함
    • vertex position은 sw_mp
    • normal과 tangent 방향은 sw_md
  • projection matrix는 일반적으로 non-zero entry가 5개뿐이므로, PGA로 억지로 바꾸지 않고 직접 projection expression을 사용함
  • CPU 쪽 scene graph hierarchy update는 matrix composition 대신 motor composition을 쓰면서 계산량이 줄어듦
  • GPU 쪽 vertex 변환은 단순 비교만 하면 motor가 불리해 보이지만, tangent frame 표현을 바꾸면 결과가 달라짐

tangent space normal mapping 최적화

  • 일반 tangent space normal-mapped mesh의 vertex shader는 position, normal, tangent를 변환해야 함
  • normal, tangent, bitangent는 orthonormal frame을 이루므로, PGA에서는 canonical basis frame에서 원하는 tangent frame으로 가는 tangentRotor로 표현할 수 있음
  • 이 방식은 vertex descriptor를 줄임
    • 기존: position 3 + normal 3 + tangent 4 + uv 2 = 12 floats
    • PGA 방식: position 3 + tangentRotor 4 + uv 2 = 9 floats
    • 정점당 float 수가 25% 감소함
  • tangentRotor는 double cover를 가지며, scalar coefficient의 sign을 classical handedness flag와 맞춰 even/odd k-reflection을 구분함
    • signed zero에 의존하며, vertex shader에서 sign(1/tangentRotor.x)로 handedness를 추출함
  • position, normal, tangent를 4x4 matrix로 변환하면 총 48 곱셈, 36 덧셈이 필요함
  • PGA 방식은 전체 tangent frame을 한 번에 변환한 뒤 normal과 tangent를 추출함
    • tangent frame 합성: 16 곱셈, 12 덧셈
    • normal/tangent 추출: 9 곱셈, 8 덧셈
    • position 변환: 21 곱셈, 18 덧셈
    • handedness 추출용 곱셈 1개
    • 총 47 곱셈, 38 덧셈
  • 정점 변환 비용은 행렬 방식과 거의 같고, transform 저장은 32 floats에서 8 floats로 줄어듦

fragment shader와 baked texture 제약

  • 기존 콘텐츠를 로드하려면 fragment shader 단계에서는 다시 TBN matrix가 필요함
  • baking tool은 high-detail mesh를 low-detail mesh에 굽는 과정에서 vertex normal과 tangent를 triangle face 위에서 보간하고, 각 fragment에서 orthogonal TBN matrix를 구성해 tangent space normal texture를 만듦
  • basis vector 보간은 행렬 방식의 전형적인 오차를 만들고, 그 오차가 texture에 이미 baked되어 있음
  • 그래서 이 구현은 tangentRotor에서 normal과 tangent vector를 명시적으로 추출함
  • baking tool까지 제어할 수 있다면 tangentRotor를 fragment shader로 그대로 넘겨 정규화 후 sampled normal 변환에 사용할 수 있음
    • TBN matrix를 만들 필요가 없음
    • vertex shader의 normal/tangent 추출이 불필요함
    • varying parameter 하나를 줄일 수 있음
    • fragment shader의 expensive orthogonalization도 제거할 수 있음

motor skinning과 animation blending

  • PGA motor는 dual quaternion과 동형이므로 skinning에 자연스럽게 적용됨
  • inverse bind matrix를 motor로 변환한 뒤, dual quaternion skinning과 같은 패턴으로 bone motor를 blend함
  • blend되는 transformation은 shortest arc를 따르도록 부호를 맞추고, 결과 transformation을 다시 정규화함
  • animation blending도 같은 방식으로 CPU에서 PGA motor를 직접 blend한 뒤 정규화함

행렬 대체 실험의 결과

  • glTF 호환 포워드 렌더러에서 PGA만으로 행렬을 대체하는 구현은 가능함
  • 변환 비용이 더 비쌀 것이라는 예상은 tangent frame 표현과 sandwich product 최적화를 적용하면 단순하지 않음
  • tangent space normal mapping의 일반적인 경우, PGA motor 방식은 vertex shader 비용을 행렬 방식과 거의 같게 유지하면서 vertex memory footprint를 크게 줄임
  • 같은 storage에 약 33% 더 많은 vertices를 담을 수 있는 메모리 개선이 특히 큼
  • 이 기법은 vertex shader 비용을 거의 늘리지 않고, 파이프라인 나머지 부분을 수정하지 않는 drop-in replacement로 기존 3D 엔진에 적용 가능함

댓글과 토론

Hacker News 의견들
  • 좋아하는 수학/그래픽 YouTube 제작자 중 한 명인 Freya Holmér가 얼마 전 기하대수 입문 영상을 아주 잘 만들었음: https://www.youtube.com/watch?v=htYh-Tq7ZBI&ab_channel=Freya...
    3D 그래픽, 특히 스플라인/베지어 곡선에 관심이 있다면 이 사람의 영상들을 전부 볼 만함
    개인적으로 선형대수는 늘 어려웠는데, 이런 Clifford 대수 접근은 훨씬 직관적으로 느껴짐
    • 정말 좋은 발표였고, https://enkimute.github.io/ganja.js/가 떠올랐음
      이 라이브러리는 원글 작성자인 enkimute가 만든 것으로, 단일 파일·빌드 없는 스크립트인데도 N차원 대수와 렌더링 지원까지 제공하는 꽤 놀라운 라이브러리임
    • 이 글이 그 사람 얘기일 줄 알았음. 스플라인과 베지어 영상도 함께 재밌게 봤고, 발표가 절대 서두르는 느낌 없이 핵심으로 잘 들어감
    • YouTube 댓글에도 의외로 좋은 보충 설명과 질문이 있음
      예를 들면 곱의 비가환성처럼 Freya가 조금 빠르게 넘어가거나 생략한 부분에 대한 설명이 꽤 괜찮음
  • 기하대수는 한동안 완전히 수수께끼였는데, 결국 이렇게 이해하니 풀렸음: 그냥 다항식 곱셈인데, 곱하는 순서가 중요한 양들이 있고 곱셈표가 이상한 것뿐임. 예를 들면 i*i = 1, i*j = -j*i
    대부분의 입문 자료는 두 벡터의 기하곱 (x1*i + y1*j) * (x2*i + y2*j)를 깊고 신비한 것으로 보이게 하지만, 사실 신입생 대수에서 배운 FOIL 전개와 같음:
    (x1*i + y1*i)(x2*i+y2*j) = x1*x2*i*i + x1*y2*i*j + y1*x2*j*i + y1*y2*j*j = (x1*x2 + y1*y2) + (x1*y2 - y2*x1)*i*j
    첫 괄호 안의 값은 익숙한 내적이고, 두 번째 괄호 안의 값은 익숙한 외적에 해당하지만 i*j라는 새로운 차원의 기저로 표현됨. 그리고 외적과 달리 임의 차원으로 일반화되며, 기하대수에서는 이를 쐐기곱이라고 부름
    이걸 이해하면 회전 공식 유도 같은 것도 쉬워짐. 대수에서 익힌 기술을 기하 문제 풀이에 그대로 적용할 수 있기 때문임
    • 다른 댓글에서 나온 https://youtu.be/htYh-Tq7ZBI?si=lOmsCL2DoqUCQgh1&t=1540에서 Freya가 공리를 하나로 줄이는 설명을 훌륭하게 했음
      벡터를 자기 자신과 곱한 값을 그 벡터 길이의 제곱으로 정의하면, 나머지는 단순한 다항식 곱셈에서 전부 따라 나옴. 꽤 아름다움
    • 이 설명이 흥미로운 대비를 보여줌. 며칠 전 누군가 수학 수업이 왜 연산의 방법과 이유를 가르치지 않고 공식만 주고 계산하게 하느냐고 물었는데, 여기서는 연산이 왜 성립하는지보다 무엇을 하는지에 초점을 맞추고 있음
      “어떻게 작동하는가?”와 “왜 작동하는가?”는 수학 교사가 균형을 잡아야 하는 두 질문이고, 한 강좌에서 둘 다 항상 잘 답하기는 어려움
    • 두 번째 항은 외적이 아니라 외대수 곱, 또는 바이벡터임. 외적은 3차원에서만 동작하지만 외대수 곱은 더 높은 임의 차원에서도 가능함
      3차원 벡터 두 개의 외적은 두 벡터가 만드는 평면에 수직인 또 다른 벡터임. 반면 외대수 곱은 두 벡터 사이의 평행사변형을 훑는 2-벡터, 즉 바이벡터이고 두 벡터가 놓인 평면 위에 있음. 3차원에서는 외적 벡터가 이 바이벡터 평면에 수직임
    • 수학에서 배우는 데 가장 오래 걸리는 것 중 하나는, 대부분의 것들이 가능한 한 가장 단순한 방식으로 정의된다는 사실임
      특히 벡터공간 V 위에서 쌍선형 곱 m:V x V -> V를 정의하려면, 기저 벡터 쌍에 대해서만 m을 정하는 것과 정확히 같음. 이걸 “텐서곱의 보편 성질”이라고 부르면 아마 그냥 “아 그렇군요”라고 할 것임
  • 회전 보간에는 기하대수, 쿼터니언, 심지어 전체 행렬 보간까지 여러 접근이 있어 흥미로움: https://www.gamedev.net/tutorials/programming/math-and-physi...
    하지만 코드를 손으로 최적화하고 나면 최종 코드는 대부분의 접근에서 거의 비슷해짐. 차이는 규칙과 가능성을 어떻게 이해하느냐에 있음
    조금 아는 범위에서는 기하대수가 가장 일관되고 능력 있는 접근처럼 보임. 낯설고, 처음 받아들이기에는 꽤 버겁지만, 그 장벽을 넘은 사람들은 좋아함
    반대로 모두가 쿼터니언을 쓰면서도 이해하지 못한다고 불평하고, 시각화하려면 책 한 권이 필요하다고 함. Andrew J. Hanson과 Steve Cunningham의 『Visualizing Quaternions』 같은 책 말임
    • 수학자는 아니고 일에서도 기하를 많이 쓰지는 않지만, 재미로 기하대수를 배우고 있었고 예전에는 쿼터니언도 배우려 해봤음
      기하대수는 재미있고, 쿼터니언은 재미없음. 기하대수는 이해한 것 같은데, 쿼터니언은 강의와 문제를 따라가도 이해하지 못했다는 것만 확실했음. 이제 기하대수를 조금 알고 나니 마침내 쿼터니언도 어느 정도 아는 느낌이 듦
    • Naive Lie Theory』는 훌륭한 책이고, 첫 장에서 쿼터니언을 가르침
      https://www.goodreads.com/en/book/show/4419538
  • 이 주제에 관심 있다면 Grassman/Clifford/기하대수 개념을 훑는 좋은 슬라이드가 있음: http://www.terathon.com/gdc12_lengyel.pdf
    또 다른 좋은 사이트도 있음: https://mattferraro.dev/posts/geometric-algebra
    • Sudgy의 훌륭한 “A swift introduction to projective geometric algebra”도 빼먹으면 안 됨: https://www.youtube.com/watch?v=0i3ocLhbxJ4
      그리고 대표 참고 사이트는 https://bivector.net
      교수, 연구자, 애호가 1000명 이상이 있는 bivector Discord에도 참여할 수 있음: https://discord.gg/vGY6pPk
    • 그 발표의 저자인 Eric Lengyel은 『Foundations of Game Engine Development, Volume 1: Mathematics』도 썼고, 4장이 같은 주제를 다룸
  • 솔직히 기하대수는 무엇에 무엇을 곱하는지 조심하지 않으면 온갖 혼합 원소가 생기는 방식이 별로 마음에 들지 않았음
    n차원 공간이었던 것에 대해 최대 2^n개의 항이 필요해지는 것도 다루기 어렵게 느껴짐
    기하, 즉 내적을 더 잘 다룰 수 있어야 할 것 같지만, 그냥 쐐기곱과 Hodge 별 연산자 또는 musical isomorphism을 쓰면 안 되는 이유를 납득할 만한 설명을 못 봤음
    바이벡터 u^v를 그 평면에서의 회전 e^(u^v)t로 바꾸는 “마법” 같은 것도 본질적으로는 musical isomorphism으로 2-형식 u^v를 선형 자기동형으로 바꿔서 e^(u^v)t를 행렬 지수로 이해하게 만드는 것임
    또 자주 나오는 예로 Maxwell 방정식을 하나의 방정식으로 만들 수 있다는 점이 있는데, 미분형식을 쓰면 이미 서로 다른 이유로 성립하는 두 방정식으로 요약할 수 있으므로 그걸 하나로 합치는 효용을 이해하지 못했음
    • “n차원 공간이었던 것에 대해 최대 2^n개의 항이 필요하다”는 절약이 때로는 착시임
      예를 들어 법선 벡터는 위치 벡터와 다르게 변환됨. 둘을 같은 자료구조로 표현할 수는 있지만, 그 안에 어떤 종류의 벡터가 들어 있는지 추적해야 하고 코드 곳곳에 각각 다르게 처리하는 특수 사례를 넣어야 함
      기하대수는 이를 정면으로 다뤄서 벡터에는 (i,j,k)라는 기저를 쓰고, 다른 종류에는 (j*k, k*i, i*j)라는 별도 기저를 사용함
      하나의 방정식이 둘이나 넷보다 낫다는 점에서, 더 높은 차원의 공간이 낮은 차원보다 오히려 저장 면에서 더 경제적인 좋은 예가 됨
      전기장은 자기장과 벡터가 바이벡터와 다른 방식과 꽤 비슷하게 다름. 전기장과 자기장을 별도 방정식으로 특수 처리할 수도 있고, 하나의 방식으로 균일하게 다룰 수도 있음
    • 혼합 원소야말로 중요한 부분임
      w=1, x,y,z=0인 쿼터니언은 항등이고, w=0, x=1이거나 w=0, x=y=0.7 같은 쿼터니언은 180도 회전에만 해당함
      임의 회전을 원하면 둘의 조합이 필요함. “이 선 주위의 180도 회전을 조금, 그리고 0도 회전/항등을 조금” 섞는 것임. 스칼라와 바이벡터를 함께 갖는다는 뜻이 바로 이것임
      쐐기곱과 내적으로 “조심해서” 혼합을 피하려 한다면 잘못 쓰고 있는 것임. 기하곱이 주역이고, 아주 훌륭한 혼합을 만들어냄
    • 혼란이 이미 존재한다는 점에는 동의함. 전통적인 접근은 그 혼란을 그냥 양탄자 밑으로 쓸어 넣을 뿐임
      예를 들어 법선을 다룬다면, 서로 꽤 다르게 변환되는 n차원 공간을 적어도 두 개는 추적해야 함
      점, 평면, 선, 법선, 평행이동, 회전을 모두 하나의 multivector 타입과 일관된 규칙으로 표현하는 것은 이해하고 나면 꽤 해방감이 있음. 아직 나도 익히는 중이긴 함
  • 아래쪽의 애니메이션 보간은 정말 멋지지만, 페이지 나머지 부분의 모델들이 조금 덜 활발했으면 좋겠다는 생각이 듦
    수학은 작은 코끼리 치어리더가 없어도 충분히 어려움
    • 오히려 반대임. 그 코끼리 같은 응원이 없었다면 페이지 끝까지 못 갔을 것임
  • 글쓴이가 보고 있다면, PGA라는 약어를 처음 쓸 때 정의해줬으면 함
    • 궁금한 사람을 위해 말하면 PGA는 projective geometric algebra, 즉 사영 기하대수
      작업 중인 공간의 기저 벡터에 영기저 벡터를 하나 추가함. 이렇게 하면 원점을 지나지 않는 기하 객체도 대수로 표현할 수 있음
    • 특히 “Fast PGA”를 FPGA라고 쓴 부분은 꽤 헷갈렸음
    • 수정했음. mea maxima culpa
  • 이런 알고리즘이 GPU를 고려해도 효율적인가?
    GPU는 행렬 작업에 잘 맞춰져 있다는 막연한 인상이 있는데, 기하대수 공식화를 쓰면 그 이점을 잃어서 실제로는 앞서지 못하는 건 아닌지 궁금함
    잘 모르는 추측이니 틀렸으면 바로잡아주면 좋겠음
    • GPU 표준에 행렬-행렬 곱과 행렬-벡터 곱이 있으니 GPU 회사들이 반드시 이를 가속한다고 생각하는 건 매우 흔한 오해임
      실제로는 셰이더 코어 전체가 이미 SIMD라서 반드시 그렇게 할 수 있는 것은 아님. 어떤 GPU는 하고, 어떤 GPU는 하지 않음
    • 프로그래밍할 때는 두 가지를 알아내야 함: 계산하고 싶은 양이 무엇인지, 그리고 그것을 계산하는 가장 효율적인 방법이 무엇인지
      PGA는 이해하는 데 적잖은 부담이 있지만, 첫 번째를 다루는 데 아주 좋은 방식임. 어차피 보통은 가장 단순하고 구현하기 쉬운 방법을 먼저 시도하는 편이 좋음
      PGA로 첫 번째를 해결해 얻은 구현은 나머지 프로그램을 프로토타입으로 만들고 벤치마크해서 진짜 병목을 찾기에 충분함. 다행히 대부분의 경우 그것이 가장 빠른 계산법이거나, 병목이 되지 않을 정도로 충분히 빠름
      병목이 되더라도 풀려는 문제를 깊이 이해하게 해줌. 충분히 빨라지길 바라며 사이클을 깎기 시작하기 전에 그런 이해를 갖추는 편이 좋다고 봄
    • 이 글이 정확히 그 내용을 다룸. 요약하면 대체로 비슷한 수준이 될 수 있음
  • 이건 진보의 끝에서 벌어지는 미세한 차이 다툼처럼 보임
    3D 스켈레탈 애니메이션이 아직도 GPU에서 4x4 행렬을 쓴다는 건, Half-Life 1 무렵 CPU에서 이 용도로 개발된 수학이 여전히 최전선이라는 뜻임. 1998년에서 2024년까지 26년임
    1000년 뒤에도 3D 애니메이션은 그대로일 것임
  • 이 글은 이해 범위를 넘지만, 제목을 보고 간단한 3D 렌더러를 만들던 실험이 떠올랐음
    선형대수를 배우려다 여러 번 실패한 뒤, 샤워 중에 3D 회전은 그냥 2D 회전 세 개이고 그건 이미 알고 있다는 생각이 들었음. 한 시간쯤 뒤에는 원근법까지 갖춘 와이어프레임 3D 렌더러가 만들어졌음
    모두 한 번 시도해보길 권함