The following are the steps you need to follow:
- Get the normal vector for the reflecting point as $n$.
- Calculate reflected direction from normal vector and $I$ in the world frame, as $I_r$
- Sample Phong BRDF. This will yield a local frame vector as $r$. The next thing you need to do is to convert this local vector to world frame vector (details are below).
- Once you converted this to world frame as $r_w$, the view direction you want will be $r_w$. Now you just need to evaluate the BRDF, return PDF, sampled direction and throughput.
I assume the difficult part for you is the 3rd step. How do I convert a local vector to the world vector? How does the frame get defined? Let's answer the second question first.
The Phong model proposes that the view direction should be sampled around the reflected ray direction. So we know that one of the axis of the coordinate frame is the reflected ray direction. The convention is that this vector usually serves as the z-axis for this local frame. That is, if you are talking about $(0, 0, 1)^T$ in this local sampling frame, the actual vector in the world frame for $(0, 0, 1)^T$ is the reflected ray direction. Since Phong model is isotropic on $\phi$ direction, the other two axes are free to choose.
Next, how to convert from local frame to world frame? Say we have this $r$ which is sampled in a frame of which the z-axis is $(0, 0, 1)^T$, and we want to convert it as if it was sampled in a frame of which the z-axis is the reflected ray direction ($I_r$). You actually need to calculate the rotation matrix between $(0, 0, 1)^T$ and $I_r$. This can be done with the following steps:
- Calculate the cross product of $(0, 0, 1)^T$ and $I_r$. Since we know that cross product will yield a vector that is perpendicular to both input vectors, this will be our axis of rotation, that is, we will later rotate around this axis.
- Calculate the dot product of $(0, 0, 1)^T$ and $I_r$. This will yield the cosine value for the angle between the two vectors, and actually, if we rotate one of the vectors around the axis we get in step 1 by this angle, we will get the other vector.
- So, we rotate the vector around the cross-product axis by the dot-product angle. This can be achieved by Rodrigues' rotation formula.
The following is the code for Rodrigues' rotation formula. np_rotation_between
uses the transform lib provided by scipy
and rotation_between
is hand-crafted (though in Taichi Lang).
from scipy.spatial.transform import Rotation as Rot
@ti.func
def skew_symmetry(vec: vec3):
return mat3([
[0, -vec[2], vec[1]],
[vec[2], 0, -vec[0]],
[-vec[1], vec[0], 0]
])
def np_rotation_between(fixed: Arr, target: Arr) -> Arr:
"""
Transform parsed from xml file is merely camera orientation (numpy CPU version)
INPUT arrays [MUST] be normalized
Orientation should be transformed to be camera rotation matrix
Rotation from <fixed> vector to <target> vector, defined by cross product and angle-axis
"""
axis = np.cross(fixed, target)
dot = np.dot(fixed, target)
if abs(dot) > 1. - 1e-5: # nearly parallel
return np.sign(dot) * np.eye(3, dtype = np.float32)
else:
# Not in-line, cross product is valid
axis /= np.linalg.norm(axis)
axis *= np.arccos(dot)
euler_vec = Rot.from_rotvec(axis).as_euler('zxy')
euler_vec[0] = 0 # eliminate roll angle
return Rot.from_euler('zxy', euler_vec).as_matrix()
@ti.func
def rotation_between(fixed: vec3, target: vec3) -> mat3:
"""
Transform parsed from xml file is merely camera orientation (Taichi version)
Rodrigues transformation is implemented here
INPUT arrays [MUST] be normalized
Orientation should be transformed to be camera rotation matrix
Rotation from <fixed> vector to <target> vector, defined by cross product and angle-axis
"""
axis = tm.cross(fixed, target)
cos_theta = tm.dot(fixed, target)
ret_R = ti.Matrix.zero(float, 3, 3)
if ti.abs(cos_theta) < 1. - 1e-5:
normed_axis = axis.normalized()
ret_R = ti.Matrix.diag(3, cos_theta) + ((1 - cos_theta) * colv3(*normed_axis)) @ rowv3(*normed_axis) + skew_symmetry(axis)
else:
ret_R = ti.Matrix.diag(3, tm.sign(cos_theta))
return ret_R
With this, you can get the rotation matrix between $(0, 0, 1)^T$ and $I_r$. Let's suppose you get the rotation matrix to rotate $(0, 0, 1)^T$ to $I_r$ as $R$, then the sampled view direction for you will be:
$$
v = R(0, 0, 1)^T
$$
Otherwise, if the rotation is in the opposite direction, just use $R^T$ for this matrix-vector multiplication. There you go, the core mathematical formulae you need are (1) reflection (2) Phong local sampling (3) Rodrigues' rotation formula.