Ray marching is very similar to ray-tracing, which is a common technique used in rendering and game engines to create nearly photoreal results. It consists of taking multiple "steps" along a ray for each pixel created by the camera.
This graphic shows how ray tracing works, by shooting out rays from the camera source point. Ray Marching uses the same ray technique, but "traces" them slightly differently
Instead of shooting a ray and checking for intersection with a 3D object that has vertices and faces, ray marching checks the distance from a point on the ray, and then "marches" that distance along the ray and continues until the distance reaches a certain very small point.
Here is a visualization of that stepping. The circles represent the "radius" that it can step forward. The reason this works is because if the circle intersects with a shape, and marches that far, it will have no chance of runnning through it. This is because even if the shape is directly in front, which is theoretically impossible, it would just march that far. The end of the ray in this diagram shows the circle getting smaller and smaller until its theoretical limit of zero
How do you know the distance to a shape? Well, since ray marching is entirely math based, we can use some interesting formulas to derive what are called "signed distance functions" from formulas for shapes. For example a sphere would have the signed distance function of:
float sdSphere( vec3 p, float s )
{
return length(p)-s;
}
The input variables for this function, p and s, are the position in 3D space that cooresponds to the location on the ray, and s is the radius of the sphere. A list of many more signed distance functions can be found here: (https://iquilezles.org/articles/distfunctions/)
What does this have to do with the formulas we had before? Well, since we have a formula for the mandelbulb using cartesian coordinates as inputs, we can derive a function for raymarching. There are various formulas for raymarching, but the one I'm showing here is a commonly used version using trigonometric functions:
float MandelbulbDE( float3 p, float power, float iterations ) {
float3 w = p;
float m = dot(w,w);
float dz = 1.0;
for( int i=0; i< iterations; i++ )
{
dz = power*pow(sqrt(m), power - 1.0 )*dz + 1.0;
float r = length(w);
float b = power*acos( w.y/r);
float a = power*atan2( w.x, w.z );
w = p + pow(r,power) * float3( sin(b)*sin(a), cos(b), sin(b)*cos(a) );
m = dot(w,w);
if( m > 2)
break;
}
return 0.25*log(m)*sqrt(m)/dz;
}
The inputs in this function are, again p for position, iterations, which controls how many times the recursive function is looped, and power, which controls the power in the function: ZN^power + C.