# A Shader-Based Ray Tracing Engine
## 3 Design
### Ray Reflection Calculation  
The _light transport calculation_ is another important point of the ray-tracing method, as the ray reflection calculation step [27; 28]. Many illumination calculation methods are available, starting from physically correct _BRDF_ (bidirectional reflectance distribution function) and _BSDF_ (bidirectional scattering distribution function) formula to diverse artistic approaches including cell-shading and more.  
We opted for one of the most efficient illumination methods:  
\[I_{R,S}=A_{S}\times I_{R^{\prime},S^{\prime}}+E_{S},\]  
where \(I_{R,S}\) is the illumination of the current ray \(R\) and its hit surface \(S\). Upon hit, the surface's light emittance \(E_{S}\) and attenuation \(A_{S}\) is calculated into the traced light color. After the intersection, a new ray \(R^{\prime}\) reflected from the surface \(S\) and its new hit surface \(S^{\prime}\) is recursively attained to trace the next ray illumination \(I_{R^{\prime},S^{\prime}}\).  
To overcome the high computation burden of the ray-tracing, we adopted [PERSON]'s _upsampling method_[31]. The main idea is to improve performance by reducing the number of pixels to compute.  
Figure 10 depicts the detail of our rendering process. We first generate the full resolution \(G\)-buffer. The \(G\)-buffer is a collection of all lighting-relevant textures produced to compose the final lighting pass, which is an essential step for all deferred rendering pipelines [35; 36]. The \(G\)-buffer includes normal vectors, depth values, and material information of the rendered image. Material data carries its surface image, roughness, and emittance. These textures can be generated through a conventional rasterization pipeline since it does not involve indirect lighting. In this work, we decided to use the compute shader-based ray casting for efficiency. We then compute the ray-traced indirect illumination in a half-resolution and upsample it back to the full resolution based on these pre-generated \(G\)-buffer images.  
Figure 9: Two separate BVH tree constructions: the scene comprehensive construction uses the per-static models as the pre-processed and duplicated models.  
We used edge-preserving _joint bilateral upsampling_ to avoid the flaws of fuzzy and noisy upsampling [37; 38; 39]. We upsample the low-resolution image with the help of full-resolution normal vector and depth value information, preserving the notable edges.  
Figure 11 depicts the difference between these methods. _Joint bilateral upsampling_ is one derivation of the _bilateral filtering_ method, utilizing both spatial and range filter kernel [40; 41]. The final color composition of the pixel \(I_{p}\) at the pixel position \(p\) in the spatial kernel \(\Omega\) can be denoted as below:  
\[I_{p}=\frac{1}{W_{p}}\sum_{q\in\Omega}I_{q}f(||p-q||)\,g(G_{p},G_{q}),\]  
where \(W_{p}\) is the normalization factor and \(f\), \(g\) is the spatial and range filter kernel, respectively. Unlike the conventional bilateral filter, the joint bilateral upsampling uses the guidance image \(G\) to evaluate the range factor, which is normal vector and depth value images in our case.  
Figure 11: Joint upsampling results.  
Figure 10: Our rendering pipeline in order.  
The spatial kernel \(f\) is a _truncated Gaussian function_ and is pre-computed before the run-time usage:  
\[f(x)=\mathrm{e}^{-\left(\frac{2x}{s}\right)^{2}},\]  
where \(s\) is the kernel size. The range kernel \(g\) is the edge-preserving factor evaluated by the \(G\)-buffer normal vector \(n\) and depth value \(z\):  
\[g(G_{p},G_{q})=||G_{pz}-G_{qz}||\big{(}G_{pn}\cdot G_{qn}\big{)}.\]  
For efficient computation, we filter the row and column separately to minimize the execution time [42; 43]. The per- pixel \(n\) by \(n\) kernel operation is reduced down from \(n^{2}\) to \(2n\) with a little loss of filtering quality.