As it has been concluded, we use the ray marching technique to render Zetabulbs. Several free ray marching engines that can visualize Mandelbulbs are available. However, none allow efficient CUDA utilization for the Riemann zeta function calculation. Therefore, we will create a basic ray marching engine from scratch. The engine’s source code and the
MB-algorithm’s implementation can be found in Kuzma (
2025). The new instrument will exhibit the following features:
-
• object rendering (for a given SDF);
-
• object colouring with a multi-colour gradient;
-
• object shading (for the object including ambient lighting);
-
• FXAA anti-aliasing (using fast approximate anti-aliasing algorithm);
-
• enabling camera movement with keyboard and mouse controls.
6.1 Zetabulb Signed Distance Function
Zetabulb visualization requires associating the Mandelbulb signed distance function with the Riemann zeta function. When combining a fractal with another object (e.g. shape, fractal, surface), we transform the fractal’s space by the object’s properties and then visualize the fractal in this new space. Thereby, we will combine the Mandelbulb with the surface of the Riemann zeta function.
The procedure is nontrivial. The Mandelbulb has a fractal surface; however, its volume is finite, whereas the Riemann zeta function’s domain is an infinite complex plane. Thus, the question arises: how do we combine a finite volume and an infinite plane? We will solve this problem by adding two more degrees of freedom: movements in the complex plane of the Riemann zeta function. This would allow the Mandelbulb to inherit the properties of a small section of the Riemann zeta functions surface rather than the entire plane. We can implement the new degrees of freedom by adding a complex point c. This point will associate the Mandelbulb with the Riemann zeta function.
The association of the complex plane and the Cartesian coordinate system can be accomplished in two steps. First, we compute the Riemann zeta function value
z for an argument related to the point
c. Next, we construct a new point
η and compute the SDF of a Mandelbulb for this point. An example of this association is presented in Algorithm
2. Figure
6 displays Zetabulbs obtained by several association methods. We will use the same principle when associating Mandelbulbs with Dirichlet
L-functions.

Algorithm 2
Signed distance function of a Zetabulb. Here η – the three-dimensional coordinates of the origin point, p – the power of the Mandelbulb, k – the maximum number of iterations, c – the complex number used to associate the Mandelbulb with the real and imaginary surfaces of the Riemann zeta function.
6.2 Visualization Quality
We have discovered that algorithms using float type variables perform $\sim 22$ times faster than equivalent ones employing double type variables. However, the second experiment showed that this gain comes with a significant loss in accuracy. In this section, we will determine how this loss of accuracy impacts Zetabulb image quality.

Fig. 6
Zetabulbs constructed using different association methods with identical
c values. Values
z and
η correspond to the rows 2 and 3 of Algorithm
2.

Algorithm 3
Ray marching function used for the custom rendering tool. This function fires a ray from $\textit{position}$ in $\textit{direction}$ until it hits or misses the object. Returns a colour for a pixel. It is assumed that the centre of the object defined by the SDF is at coordinates (0, 0, 0).
Algorithm
3 defines the process of how each pixel is coloured. In total, the Riemann zeta function is called up to
$\textit{maximumSteps}$ times and an additional 6 times when computing a surface normal if the ray hits the Zetabulb. Errors from having non-accurate Riemann zeta values can quickly accumulate and produce incorrect pixel colour values. We will perform an experiment to test how inaccurate the computed values are. First, we will generate ten Zetabulb images with
$1\leqslant d\leqslant 10$ decimal digits of accuracy with the
double algorithm and one with the
float algorithm. We will call the image with 10 decimal digits of accuracy our benchmark and the others our test images. Then, we will calculate the colour accuracies of each image when compared to the benchmarks’ corresponding pixels.
To compute each pixel’s colour accuracies
$\Delta E$, we will use the CIEDE2000 algorithm (see Sharma
et al. (
2005)), an industry-standard, to evaluate how similar colours are,
$\Delta E\in [0;100]$. Empirically, these values can be categorized into the following groups:
-
• $\Delta E=0$. The colours are the same.
-
• $\Delta E\in (0;1]$. The colour differences are not perceptible by the human eye.
-
• $\Delta E\in (1;2]$. Perceptible through close observation.
-
• $\Delta E\in (2;10]$. Perceptible at a glance.
-
• $\Delta E\in (10;50]$. Colours are more similar than different.
-
• $\Delta E\in (50;100]$. Colours are more different than similar.
In our experiment, we compute the percentages of pixels of our test images that fall in each group. By comparing the percentages of the float algorithm to the other test images, we should be able to evaluate the adequate visualization accuracy of the float algorithm.
Earlier, we have established that our implementation of the MB-algorithm will compute the coefficients $\{{\psi _{n,k}}\}$ for a maximum imaginary part ${t_{\max }}$ and d decimal digits of accuracy. The same coefficients $\{{\psi _{n,k}}\}$ will be used to compute Riemann zeta function values for $t\lt {t_{\max }}$. Naturally, these values will be computed with higher decimal accuracy than d. This property of our algorithm makes it problematic to compute the Riemann zeta function values for arguments with different imaginary parts while maintaining exactly the same accuracy. For this reason, as a benchmark, we use values computed using the double algorithm with $d=10$ decimal digits of accuracy. Next, by rounding down the benchmark values, we obtain Riemann zeta function values with lower accuracies.
Table 3
E values of the test images for Scenario 1. d – the decimal digits of accuracy with which an image was generated, and f stands for the float algorithm. Other columns lists the percentage of pixels that fall into each $\triangle E$ category when compared with the benchmark image.
|
$\Delta E\in $ |
| d |
0 |
$(0;1]$ |
$(1;2]$ |
$(2;10]$ |
$(10;50]$ |
$(50;100]$ |
| 1 |
31.93 |
1.68 |
1.52 |
11.47 |
39.96 |
13.44 |
| 2 |
35.04 |
3.71 |
3.15 |
14.13 |
33.97 |
10.00 |
| 3 |
43.39 |
5.58 |
3.34 |
14.29 |
27.59 |
5.80 |
| 4 |
59.41 |
8.83 |
4.74 |
13.65 |
11.50 |
1.86 |
| 5 |
72.65 |
15.18 |
4.49 |
5.42 |
1.96 |
0.29 |
| 6 |
86.45 |
10.94 |
1.10 |
1.08 |
0.38 |
0.05 |
| 7 |
95.02 |
4.21 |
0.30 |
0.33 |
0.12 |
0.02 |
| 8 |
98.55 |
1.23 |
0.08 |
0.09 |
0.04 |
0.00 |
| 9 |
99.81 |
0.17 |
0.01 |
0.01 |
0.00 |
0.00 |
| f |
90.32 |
7.89 |
0.63 |
0.67 |
0.32 |
0.15 |
Table 4
$\triangle E$ values of the test images for Scenario 2. d – the decimal digits of accuracy with which an image was generated, and f stands for the float algorithm. Other columns lists the percentage of pixels that fall into each $\triangle E$ category when compared with the benchmark image.
|
$\Delta E\in $ |
| d |
0 |
$(0;1]$ |
$(1;2]$ |
$(2;10]$ |
$(10;50]$ |
$(50;100]$ |
| 1 |
11.01 |
2.18 |
8.17 |
15.46 |
59.55 |
9.53 |
| 2 |
16.99 |
4.39 |
2.26 |
17.41 |
51.43 |
6.36 |
| 3 |
23.12 |
5.88 |
3.42 |
21.24 |
41.39 |
3.95 |
| 4 |
37.27 |
17.01 |
4.42 |
22.20 |
12.14 |
1.04 |
| 5 |
54.97 |
32.58 |
10.33 |
4.95 |
1.89 |
0.19 |
| 6 |
83.61 |
14.33 |
5.42 |
0.83 |
0.37 |
0.05 |
| 7 |
94.82 |
4.57 |
0.82 |
0.23 |
0.13 |
0.02 |
| 8 |
98.42 |
1.38 |
0.22 |
0.07 |
0.04 |
0.01 |
| 9 |
99.79 |
0.18 |
0.07 |
0.01 |
0.01 |
0.00 |
| f |
47.81 |
30.78 |
0.01 |
8.86 |
3.89 |
0.49 |
For this experiment, we evaluate the visualization accuracy in two scenarios, with
$\mathrm{\Im }c=5$ and with
$\mathrm{\Im }c=500$. We also produce a grayscale image of the
float image with highlighted pixels that fall into the groups
$\Delta E\in (10;50]$ and
$\Delta E\in (50;100]$. The computed
$\Delta E$ values are presented in Tables
3 and
4. Figure
7 presents the generated images for each scenario.
According to Table
3, in Scenario 1
$(\mathrm{\Im }c=5)$ the effective visualization quality could be achieved between
$d=6$ and
$d=7$. However, we can notice an unusually high number of very different pixels (groups
$\Delta E\in (10;50]$ and
$\Delta E\in (50;100]$) for these quality ranges. In Fig.
7(f), we can see these pixels highlighted by the colours red and green, respectively. We can see a small green region in the bottom right of the image. The
float algorithm appears to have generated a segment that does not exist in the benchmark image. After further investigation, we could see that the region exists, and the
double algorithm could not generate it. Scenario 2
$(\mathrm{\Im }c=500)$ did not have such anomalies, and its effective precision could be evaluated as between
$d=4$ and
$d=5$ in all
$\Delta E$ categories. As we can see, by increasing
$\mathrm{\Im }c$, we experienced a degradation in image visualization quality.
With the third experiment, we have discovered that the
float algorithm is inaccurate when computing the Riemann zeta function values in the neighbourhoods of the special points
${s_{k}}$. Scenario 3 aims to investigate Zetabulbs generated near one of these points. Figure
8(a) was generated using the
double algorithm with
$d=10$ decimal digits of accuracy, whereas Fig.
8(b) was generated using the
float algorithm. We can see that the
float algorithm generated different although similar structures.

Fig. 7
Zetabulb images with different Riemann zeta value precisions for Scenario 1 $(\mathrm{\Im }c=5)$. Subfigures (a) through (d) were generated with the double algorithm and lower precision. Subfigure (e) was generated with the float algorithm. Subfigure (f) is a grayscale version of (e) with pixels that fall into the category $\Delta E\in (10;50]$ highlighted red and $\Delta E\in (50;100]$ highlighted green.

Fig. 8
Zetabulb images for Scenario 3 $(\mathrm{\Im }c=163.165)$ (18th special point). Subfigure (a) was generated using the double algorithm with $d=10$ decimal digits of accuracy, and (b) was generated using the float algorithm.
6.3 Visualization Performance
In this subsection, we evaluate the real-time Zetabulb visualization performance. We measure the average FPS (frames per second) that our Zetabulb visualization instrument can achieve with float and double algorithms and different GPUs in four experiments, each containing four scenarios lasting for 30 seconds:
-
• Scenario A. Mandelbulb with no movement.
-
• Scenario B. Mandelbulb with a varying power.
-
• Scenario C. Zetabulb with no movement and set ${t_{\max }}$ value.
-
• Scenario D. Zetabulb with increasing $\mathrm{\Im }c$ and set ${t_{\max }}$ value.
Table 5
Four tests of visualization performance (in FPS). First – float type variables with ${t_{\max }}=50$, second – float type with ${t_{\max }}=500$, third – double type with ${t_{\max }}=50$, fourth – double type with ${t_{\max }}=500$. The required memory in bytes to store the coefficients in VRAM is given for each of the tests. All the tests were performed on two GPUs: RTX 3080 and GTX 1060 Mobile. Values marked in blue denote satisfactory results.
| Variable |
float type |
double type |
| Test |
${T_{1}}$ |
${T_{2}}$ |
${T_{3}}$ |
${T_{4}}$ |
| t |
${t_{\max }}=50$ |
${t_{\max }}=500$ |
${t_{\max }}=50$ |
${t_{\max }}=500$ |
| Memory, bytes |
192 |
384 |
1200 |
2400 |
| Scenario |
RTX |
GTX |
RTX |
GTX |
RTX |
GTX |
RTX |
GTX |
| A |
129.71 |
21.31 |
126.71 |
21.10 |
7.12 |
1.97 |
7.17 |
1.98 |
| B |
137.89 |
22.03 |
142.14 |
21.30 |
6.68 |
1.94 |
7.44 |
1.96 |
| C |
27.35 |
3.99 |
5.78 |
0.97 |
0.11 |
0.03 |
0.02 |
0.005 |
| D |
27.26 |
3.87 |
5.60 |
0.87 |
0.11 |
0.02 |
0.02 |
0.005 |
The visualization performance results are presented in Table
5. The human eye needs at least 24 FPS to perceive a video as smooth, so this is the value that we will consider satisfactory (coloured blue in Table
5). As discussed before, using
double type variables in CUDA computations significantly increases performance time. We can see this effect again when comparing the results of
${T_{1}}$ to
${T_{3}}$ and
${T_{2}}$ to
${T_{4}}$ where the
double algorithm could not achieve even a single frame per second.
Scenarios A and B are used as benchmarks. As discussed before, this work required the creation of a custom ray marching graphics engine to test the EMB-algorithm efficiently. In order to show that this engine does not hinder the results, regular Mandelbulb images were generated. Scenarios A and C produce static scenes, whereas B and D produce dynamic scenes. We can see no difference in performance between static and dynamic scene results. This can be viewed as both an advantage and a disadvantage. Our tool does not have a feature that would cache pixel values if no scene parameters change between frames. However, this also means that changing scene parameters between frames has no impact on the performance, so we can create videos of transforming fractals at high frame rates.
The real-time Zetabulb visualization performance results were poor when using the GTX 1060; however, the RTX 3080 produced satisfactory results in ${T_{1}}$. It should be noted that the RTX 3080 GPU is now over three years old, and faster GPUs exist on the market. The usage of newer GPUs could produce even better performance results. We conclude that the proposed algorithm produces satisfactory results with low ${t_{\max }}$ values and higher-end GPUs.
6.4 Visualization Results
In this subsection, we will provide Zetabulb images with
c values near interesting features of the Riemann zeta function surface. All of the Figs.
10(a) through
10(b) depicting Zetabulbs were generated using Algorithm
3 where the SDF is set to Algorithm
2 using both the Riemann zeta function (which we will call Riemann Zetabulbs) and the Dirichlet
L-function (Dirichlet Zetabulbs) with the character
${\chi _{5,4}}$ (exact value shown in Table
6).
Table 6
Dirichlet character ${\chi _{5,4}}$. Note that ${\chi _{5,4}}(-1)={\chi _{5,4}}(4)=1$.
| n |
1 |
2 |
3 |
4 |
| ${\chi _{5,4}}(n)$ |
1 |
−1 |
−1 |
1 |
The argument value
c for Algorithm
2 has to be chosen carefully. Let us consider
ε-neighbourhood of the point
c. The algorithm will produce normal Mandelbulbs if
$|\zeta (c+\varepsilon )|\approx 0$. If
$|\zeta (c+\varepsilon )$ has very large values, the algorithm will not produce any fractal structures. Ideally, we want to position the point
c where
$|\zeta (c+\varepsilon )|\in (0;5]$ and where there is a lot of variance in the nearby values. The Riemann zeta function values can be divided into five regions: the pole, the critical strip, the plateau to the right of the critical strip where
$\zeta (c)\approx 1$, the wall to the left of the critical strip where
$\zeta (c)$ values are very large, and the small triangular plateau to the left of the pole where
$\zeta (c)\approx 0$. Figure
9 shows all five of these regions. The points that fit the criteria for generating fractal structures lie on the boundaries of all regions and the entire critical strip.

Fig. 9
The real (yellow) and the imaginary (red) parts of the Riemann zeta function and the five principal regions: the pole neighbourhood (1), the critical strip (2), the plateau to the right of the critical strip (3), the wall to the left of the critical strip (4), and the triangular plateau (5).
We selected three points to showcase in this work. As the first point, we chose the pole of the Riemann zeta function
$c=1$. Here, the large values of the pole region completely remove the middle of the Mandelbulb, leaving a bow-shaped structure with holes, which can be seen in Figs.
10(a) and
10(b). For comparison, the Dirichlet
L-function does not have a pole, thus the Zetabulb in Fig.
10(c) appears similar to a regular Mandelbulb.

Fig. 10
(a) Riemann Zetabulb centered on the pole. (b) Side view of the same Zetabulb. (c) Dirichlet Zetabulb centered on the pole.
We chose the seventh trivial zero of the Riemann zeta function
$c={\rho _{7}}$ for the second point of interest. The corresponding fractal is visualized in Fig.
11(a). The Riemann zeta function has trivial zeroes at points
${\rho _{k}}=-2k$,
$k\in \mathbb{N}$. The trivial zeroes
${\rho _{k}}$, with
$k\in [1;6]$ lie in the triangular plateau. An example of one such fractal is presented in Fig.
11(b). Whereas, the trivial zeroes with
$k\in [8;\infty )$ belong to the wall region. This leaves only the point
${\rho _{7}}$ to produce distinct structures when compared to the original Mandelbulb (see Fig.
11) which is calculated using Algorithm
1.

Fig. 11
(a) Riemann Zetabulb centred on the 7th trivial zero. (b) Riemann Zetabulb centred on the 5th trivial zero. (c) Original Mandelbulb.
As the final point of interest, we have selected the fourth nontrivial zero of the Riemann zeta function
$c={\gamma _{4}}=1/2+30.42487i$. At this point, most of the fractal structure forms a round blob, whereas the top of the fractal is slightly extended, creating a bottle-like shape that can be seen in Figs.
12(a) and
12(b).

Fig. 12
(a) Riemann Zetabulb centred on the fourth non-trivial zero $c={\gamma _{4}}$. Note a bottle-like shape. (b) Closeup image of the bottleneck of the previous Zetabulb. (c) and (d) Dirichlet Zetabulbs centred on the fourth non-trivial zero of the Riemann zeta function. (e) and (f) Dirichlet Zetabulbs centred on the fourth non-trivial zero of the Dirichlet L-function ($c=1/2+16.03382i$).