Following the order-independent design for rendering triangles in conventional 3D graphics hardware *GL4D* also uses order-independent processing for the input tetrahedrons. After tetrahedron transfer from the CPU side, the GPU side uses the vertex shader and the geometry shader to transform and voxelize the input tetrahedrons, and then the fragment shader processes individual voxel fragments.

### 3.1 Vertex Shader: Transformation and Projection

The first step on the GPU side of *GL4D* is the vertex shader for per-vertex transformation in 4D; the 4D modelview transformation is first applied to transform each vertex coordinate from object-space (input from the 4D tetrahedrons) to eye space in 4D, while the projection transformation projects the resultant eye-space coordinates to the volumetric image buffer. Users can invoke various *GL4D* commands to set up the modelview and projection transformations in 4D. Note that in 3D, we project and image 3D objects on a 2D screen, whereas in 4D, we project and image 4D objects on a volumetric screen; later in the *GL4D* pipeline, GPU-accelerated 3D volume rendering is used to represent the contents of the volumetric screen image.

At the end of each vertex shader call, each resultant vertex has two coordinates attached: a 4D eye coordinate after the 4D modelview transform, and a normalized device coordinate defined in the space of the volumetric screen. In addition, we also transform the 4D normal vector from the 4D object space to the 4D eye space, to support pervoxel 4D lighting later in the fragment shader; see Figure 2. Note that we can support not only orthographic projection, but also perspective projection, as demonstrated in the hypercube visualization example shown in Section 5.

### 3.2 Geometry shader: Rasterizing the Tetrahedrons

In order to support per-voxel lighting and per-voxel hidden surface removal (and alpha composition) in the fragment shader, we first have to rasterize the input tetrahedrons into voxel fragments inside the volumetric screen buffer. Here we apply the geometry shader provided by the programmable rendering pipeline to carry out a per-primitive (per-tetrahedron) 3D rasterization. It is worthwhile noting that existing voxelization methods [14], [16], [29] were originally designed for voxelizing triangle-based 3D models; these methods do not apply efficiently to the voxelization of our volume-bounding tetrahedrons. Our 3D rasterization of tetrahedrons is designed as follows:

*Assembling Tetrahedrons*. First, since no rendering primitives in conventional graphics hardware are designed for tetrahedron transfer, we must adapt existing rendering primitives so that the graphics hardware can properly assemble individual tetrahedrons in each geometry shader call. Here we employ the geometry-shader-specific primitive, namely GL LINES ADJACENCY EXT; since this primitive type is 4-vertex-based, we can group the four vertices of each tetrahedron into a single adjacency line primitive. In this way, the primitive assembly unit in the graphics hardware can properly group the four corresponding vertices in a single geometry shader call.

*Backface Culling*. On commodity graphics hardware, backface culling of triangles can be done by computing the cross product of the two on-edge vectors from the first vertex in a triangle (in eye space), and then by checking the sign of the z-component in the resultant vector. If the z-component has the same sign as the viewing direction's z-component, the triangle is back-facing and can be culled.

In 4D graphics, we can implement an analogous mechanism for discarding back-facing tetrahedrons in the geometry shader. We first must ensure that the vertices in all input tetrahedrons are ordered in a consistent manner. Note that given a 4D tetrahedron with the vertex input sequence (*p*
_{0} *p*
_{1} *p*
_{2} *p*
_{3}), we can have two possible spatial arrangements as shown in Figure 3: First, we can determine a 3D subspace containing the tetrahedron similar to a 2D plane containing a triangle in 3D; then, the two possible spatial arrangements are:

*p*
_{1} *p*
_{2} *p*
_{3} are in clockwise order as seen from *p*
_{0} in the 3D subspace of the tetrahedron

*p*
_{1} *p*
_{2} *p*
_{3} are in anti-clockwise order as seen from *p*
_{0} in the 3D subspace of the tetrahedron

In *GL4D*, all input tetrahedrons should be ordered in anti-clockwise order, or else the face normal will be flipped. We can compute the 4D cross product as a determinant:

Face normal of a tetrahedron = ,

where *p*_{i} − *p*_{0} = (*vi*_{x} *vi*_{y} *vi*_{z} *vi*_{w}), with *i* = 1, 2, and 3, are the three on-edge vectors on the tetrahedron from p_{0}. Furthermore, since backface culling requires only the sign of the w-component in the resulting face normal, we can simplify the computation:

Face normal'*s w* − component

In this way, we can readily implement the above computation in the geometry shader as a 3D cross product followed by a 3D dot product. If the resultant w-component is negative, the tetrahedron is back-facing and can be culled. Following the convention in OpenGL, users of the *GL4D* API can also enable or disable this 4D culling feature.

*Multi-slice Rasterization.* In order to trigger a fragment shader call for each voxel fragment inside a 3D-rasterized tetrahedron, we employ a multi-slice (multi-pass) rasterization scheme to voxelize tetrahedrons from back to front inside the volumetric screen. In each slicing step, the tetrahedrons are voxelized on a specific slicing plane, and all these slicing planes are parallel to the 2D screen in the eye space with respect to the virtual camera that renders the volumetric screen onto the display window. Hence, we can properly voxelize each tetrahedron slice by slice from back to front in an order-independent manner, and can still correctly compose the fragment colors later in the fragment processing.

To efficiently voxelize a tetrahedron over a specific slicing plane, we adopt the Marching Tetrahedron method [37] to rasterize a tetrahedron volume in *GL4D*. When a slicing plane intersects a tetrahedron (see Figure 4) there are only two possible voxelizable footprints: 1) a triangle, where the slicing plane cuts three edges of the tetrahedron, and 2) a quadrilateral, where the slicing plane cuts four edges of the tetrahedron. For both cases, we find that the output can be modeled as a triangle strip, and, hence, we can employ triangle strip (i.e `GL TRIANGLE STRIP` in OpenGL) as the output primitive type from the geometry shader.

Moreover, our geometry shader can label each vertex as positive (takes value 1) or negative (takes value 0), depending on which side the vertex resides with respect to a given slicing plane; see also Figure 4. Then, we can pack the zeros and ones of the four vertices as a 4-bit integer so that we can quickly index the intersecting edges from a constructed edge table in the geometry shader code. In this way, we can efficiently compute the edge-plane intersections and output the intersecting shape as a triangle strip.

*Output from Geometry Shader.* Triangle strips output from the geometry shader are rasterized by the standard rasterization engine on the graphics hardware, and hence we can trigger a fragment shader call to process each rasterized voxel fragment.

When the geometry shader generates the triangle strips, we attach to each associated vertex a set of three attributes: a projected position (`gl Position`) inside the volume screen buffer, and a 4D position and normal in the 4D eye space. It is worthwhile noting that a few interpolation steps are required in the geometry shader to compute these per-vertex attributes at the edge-plane intersection points because these attributes are originally given (from the vertex shader) only at the tetrahedron vertices. After the geometry shader, the standard rasterization engine can then help to further interpolate these data over the rasterized triangle strips; the fragment shader that follows thus receives these data for each voxel fragment.