Normalized Device Coordinates

How NDC Works for Ray

NDC: Normalized Device Coordinates

referenced blog :
https://yconquesty.github.io/blog/ml/nerf/nerf_ndc.html#background

Motivation

NeRF에서
MLP의 input은 3D world-coordinate이고,
MLP의 output인 \(c, \sigma\) 를 accumulate해서 2D pixel-coordinate을 채운다
이 때, LLFF (Local Light Field Fusion) dataset 에 있는
unbounded (in single direction) 3D world-coordinate의 scene 정보를
bounded 3D NDC space로 project하면
MLP를 효율적으로 쓸 수 있다
NDC space로의 projection 과정을 수식적으로 알아보고자 한다.

From world-coordinate To NDC To pixel-coordinate

Projection Transformation

Step 1. Perspective Projection

3D camera-coordinate

Step 2. Orthographic Projection

Step 3. Projection Matrix

Since perspective projection matrix is scalable,
\(M_{proj} = M_{orth} (- P_{per})\)
\(= \begin{bmatrix} \frac{2}{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\ 0 & \frac{2}{t-b} & 0 & -\frac{t+b}{t-b} \\ 0 & 0 & \frac{2}{n-f} & -\frac{n+f}{n-f} \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} -n & 0 & 0 & 0 \\ 0 & -n & 0 & 0 \\ 0 & 0 & -n-f & nf \\ 0 & 0 & -1 & 0 \end{bmatrix}\)
\(= \begin{bmatrix} -\frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & -\frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\ 0 & 0 & -\frac{n+f}{n-f} & \frac{2nf}{n-f} \\ 0 & 0 & -1 & 0 \end{bmatrix}\)

camera-coordinate에서 \(z \in [f, n]\) where \(f \lt 0\), \(n \lt 0\) 이었는데,
NDC에서는 z-axis의 방향이 반대이므로
\(f \lt 0\), \(n \lt 0\) 대신 \(f = -f \gt 0\), \(n = -n \gt 0\) 를 대입하면,
\(M_{proj} = \begin{bmatrix} \frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\ 0 & 0 & -\frac{n+f}{n-f} & -\frac{2nf}{n-f} \\ 0 & 0 & -1 & 0 \end{bmatrix}\)

OpenGL과 같은 graphics frameworks에서는 보통
\(M_{proj}X\) 를 \(M_{proj}X\) 의 fourth entry로 나눴을 때 \(M_{proj}X\) 의 third entry(Z 값)이 양수가 되도록 하기 때문에 (아래 Step 4의 NDC 참고)
조금 수정하면
최종적인 projection matrix는
\(M_{proj} = \begin{bmatrix} \frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\ 0 & 0 & -\frac{n+f}{f-n} & -\frac{2nf}{f-n} \\ 0 & 0 & -1 & 0 \end{bmatrix}\)

camera frustum은 보통 symmetric하므로 \(l = -r\), \(b = -t\) 라 했을 때
projection matrix는
\(M_{proj} = \begin{bmatrix} \frac{n}{r} & 0 & 0 & 0 \\ 0 & \frac{n}{t} & 0 & 0 \\ 0 & 0 & -\frac{f+n}{f-n} & -\frac{2nf}{f-n} \\ 0 & 0 & -1 & 0 \end{bmatrix}\)

Step 4. from camera-coordinate to NDC

Linear in Disparity

Projection in NeRF ray

any 3D points on ray \(r = o + td\)를
NDC space (camera 원점이 중앙에 있는 \([-1, 1]^3\) cube) 로 projection하면
3D points on projected ray \(r^{\ast} = o^{\ast} + t^{\ast} d^{\ast}\) 가 된다
위에서 유도한 Projection Matrix 를 사용하면
\(\boldsymbol x = \begin{bmatrix} a_x\frac{o_x + td_x}{o_z + td_z} \\ a_y\frac{o_y + td_y}{o_z + td_z} \\ a_z + \frac{b_z}{o_z + td_z} \end{bmatrix} = \begin{bmatrix} o_x^{\ast} + t^{\ast} d_x^{\ast} \\ o_y^{\ast} + t^{\ast} d_y^{\ast} \\ o_z^{\ast} + t^{\ast} d_z^{\ast} \end{bmatrix}\)

먼저 projected 원점 좌표를 구해보자
\(t = t^{\ast} = 0\) 를 대입하면
\(o^{\ast} = \begin{bmatrix} o_x^{\ast} \\ o_y^{\ast} \\ o_z^{\ast} \end{bmatrix} = \begin{bmatrix} a_x\frac{o_x}{o_z} \\ a_y\frac{o_y}{o_z} \\ a_z + \frac{b_z}{o_z} \end{bmatrix}\)

다음으로 projected t와 d를 구해보자
\(\begin{bmatrix} t^{\ast} d_x^{\ast} \\ t^{\ast} d_y^{\ast} \\ t^{\ast} d_z^{\ast} \end{bmatrix} = \begin{bmatrix} a_x\frac{o_x + td_x}{o_z + td_z} \\ a_y\frac{o_y + td_y}{o_z + td_z} \\ a_z + \frac{b_z}{o_z + td_z} \end{bmatrix} - \begin{bmatrix} o_x^{\ast} \\ o_y^{\ast} \\ o_z^{\ast} \end{bmatrix}\)
\(= \begin{bmatrix} a_x\frac{o_x + td_x}{o_z + td_z} - a_x\frac{o_x}{o_z} \\ a_y\frac{o_y + td_y}{o_z + td_z} - a_y\frac{o_y}{o_z} \\ a_z + \frac{b_z}{o_z + td_z} - (a_z + \frac{b_z}{o_z}) \end{bmatrix}\)
\(= \begin{bmatrix} a_x\frac{td_z}{o_z + td_z}(\frac{d_x}{d_z} - \frac{o_x}{o_z}) \\ a_y\frac{td_z}{o_z + td_z}(\frac{d_y}{d_z} - \frac{o_y}{o_z}) \\ -b_z\frac{td_z}{o_z + td_z}\frac{1}{o_z} \end{bmatrix}\)
\(= \frac{td_z}{o_z + td_z} \begin{bmatrix} a_x(\frac{d_x}{d_z} - \frac{o_x}{o_z}) \\ a_y(\frac{d_y}{d_z} - \frac{o_y}{o_z}) \\ -b_z\frac{1}{o_z} \end{bmatrix}\)

Result

ray \(r = o + td\)를
NDC space (camera 원점이 중앙에 있는 \([-1, 1]^3\) cube) 로 projection 했을 때
projected ray \(r^{\ast} = o^{\ast} + t^{\ast} d^{\ast}\) 는 아래와 같이 구할 수 있다

\(o^{\ast} = \begin{bmatrix} o_x^{\ast} \\ o_y^{\ast} \\ o_z^{\ast} \end{bmatrix} = \begin{bmatrix} a_x\frac{o_x}{o_z} \\ a_y\frac{o_y}{o_z} \\ a_z + \frac{b_z}{o_z} \end{bmatrix}\)
and
\(t^{\ast} = \frac{td_z}{o_z + td_z} = 1 - \frac{o_z}{o_z + td_z}\)
and
\(d^{\ast} = \begin{bmatrix} d_x^{\ast} \\ d_y^{\ast} \\ d_z^{\ast} \end{bmatrix} = \begin{bmatrix} a_x(\frac{d_x}{d_z} - \frac{o_x}{o_z}) \\ a_y(\frac{d_y}{d_z} - \frac{o_y}{o_z}) \\ -b_z\frac{1}{o_z} \end{bmatrix}\)

Ray Projection to NDC 장점

Projection transformation 한계

LLFF dataset과 같이 single direction으로만 unbounded된 camera frustum, 즉 front-facing scene에 대해서만 적용 가능하고
unbounded 360 scene에 대해서는 기본 NeRF가 잘 수행 못함
\(\rightarrow\) MipNeRF360 등 NeRF 후속 연구에서 해결됨

특정 case

\(f_{cam}\)이 camera의 focal length이고,
\(W, H\)가 image plane의 width, height in pix 일 때
image plane이 정확히 camera frustum의 near plane에 있고
camera frustum의 far plane을 infinity로 확장하도록
camera를 설정하면,
\(z = -n = -f_{cam} \lt 0\), \(r = \frac{W}{2}\), \(t = \frac{H}{2}\), \(z = -f \rightarrow -\infty\) 이므로

\(a_x = -\frac{n}{r} = -\frac{f_{cam}}{\frac{W}{2}}\)
\(a_y = -\frac{n}{t} = -\frac{f_{cam}}{\frac{H}{2}}\)
\(\lim_{f \rightarrow \infty} a_z = \lim_{f \rightarrow \infty} \frac{f+n}{f-n} = 1\)
\(\lim_{f \rightarrow \infty} b_z = \lim_{f \rightarrow \infty} \frac{2nf}{f-n} = 2n\)
이므로

ray \(r = o + td\) 를 NDC로 projection했을 때
projected ray \(r^{\ast} = o^{\ast} + t^{\ast} d^{\ast}\) 에서
\(o^{\ast} = \begin{bmatrix} -\frac{f_{cam}}{\frac{W}{2}}\frac{o_x}{o_z} \\ -\frac{f_{cam}}{\frac{H}{2}}\frac{o_y}{o_z} \\ 1 + \frac{2n}{o_z} \end{bmatrix}\)
and
\(t^{\ast} = \frac{td_z}{o_z + td_z} = 1 - \frac{o_z}{o_z + td_z}\)
and
\(d^{\ast} = \begin{bmatrix} -\frac{f_{cam}}{\frac{W}{2}}(\frac{d_x}{d_z} - \frac{o_x}{o_z}) \\ -\frac{f_{cam}}{\frac{H}{2}}(\frac{d_y}{d_z} - \frac{o_y}{o_z}) \\ -2n\frac{1}{o_z} \end{bmatrix}\)

NDC projection in NeRF Pytorch code

NeRF-Pytorch 기준으로
run_nerf_helpers.py의 ndc_rays()에 구현되어 있으며
자세한 설명은 NeRF-Code-Review에 있음