Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A vkrunner compatible shader test that renders a mandelbulb fractal (ray marching).
[require]
vulkan 1.1.3
fbsize 800 600
[vertex shader passthrough]
[fragment shader]
#version 450
#define T 0.001 //threshold
#define MAX_POS 1000.0
#define MAX_STEPS 500
#define MAX_STEP_SIZE 0.3
#define DELTA 0.001
#define M_PI 3.14168
#define NUM_ITER 50
#define POWER 9.0
#define MAX_ITER 100.0
#define AO_STEP 0.05
#define AO_MAGIC 8.0
const vec3 bcolor = vec3(0.0, 0.0, 0.03);
const vec3 ldir = vec3(1.0, 1.0, -1.5);
const vec3 fcolor = vec3(0.1, 0.46, 0.52);
const vec2 iResolution = vec2(800.0, 600.0);
vec3 get_ray_direction(in vec2 uv, in float z)
{
float aspect = iResolution.x / iResolution.y;
return normalize(vec3(aspect * (uv.x - 0.5) * 2.0, (uv.y - 0.5) * 2.0, z));
}
float frac_distance(vec3 pos)
{
// from iq's blog post here:
// https://www.iquilezles.org/www/articles/mandelbulb/mandelbulb.htm
vec3 p = pos;
float dr = 1.0;
float r = 1.0;
for(int i=0; i<NUM_ITER; i++) {
r = length(p);
if(r > MAX_ITER) {
break;
}
// conversion to polar coordinates
float theta = acos(p.z/r);
float phi = atan(p.y, p.x);
dr = pow(r, POWER - 1.0) * POWER * dr + 1.0;
// scale and rotate the point
float pr = pow(r, POWER);
theta = theta * POWER;
phi = phi * POWER;
// conversion to cartesian coordinates
p = pr * vec3(sin(theta) * cos(phi), sin(phi) * sin(theta), cos(theta));
p += pos;
}
return 0.5 * log(r) * r/dr;
}
float min_distance(vec3 position)
{
return frac_distance(position);
// distance from sphere for test
vec3 fish_local_pos = position * vec3(0.4, 1.0, 2.0); //warped
float radius = 3.0;
float sph_distance = length(fish_local_pos) - radius;
return sph_distance;
}
float ambient_occlusion(in vec3 pos, in vec3 normal)
{
float ao = 0.0;
for(int i=0; i<5; i++) {
// step away from the surface
float sample_dist = float(i) * AO_STEP;
vec3 new_pos = pos + sample_dist * normal;
float dist = frac_distance(new_pos);
float dist_diff = max(sample_dist - dist, T);
ao += 1.0 / pow(2.0, float(i)) * dist_diff;
}
ao = 1.0 - AO_MAGIC * ao;
return clamp(ao, 0.0, 1.0);
}
vec3 shade(in vec3 pos, in vec3 normal, in vec3 color)
{
/*
float ndotl = max(dot(normal, normalize(ldir)), 0.0);
vec3 diffuse = fcolor * ndotl;
return diffuse;
*/
return ambient_occlusion(pos, normal) * color;
}
vec3 ray_march(vec3 pos, vec3 dir, vec3 color)
{
float dist;
int steps = 0;
while((dist = min_distance(pos)) > T) {
pos += dir * min(dist, MAX_STEP_SIZE);
++steps;
if((steps > MAX_STEPS) || (dot(pos, pos) > MAX_POS * MAX_POS)) {
return bcolor;
}
}
/* normal = grad = vec3(af/ax, af/ay, af/az) */
float dfdx = min_distance(pos + vec3(DELTA, 0.0, 0.0)) - dist;
float dfdy = min_distance(pos + vec3(0.0, DELTA, 0.0)) - dist;
float dfdz = min_distance(pos + vec3(0.0, 0.0, DELTA)) - dist;
vec3 normal = normalize(vec3(dfdx, dfdy, dfdz));
return shade(pos, normal, color);
}
layout(location = 0) out vec4 fragColor;
void main()
{
vec2 uv = gl_FragCoord.xy / iResolution.xy;
vec3 rd = get_ray_direction(uv, 2.0);
vec3 origin = vec3(0.0, 0.0, -3.0);
float theta = M_PI; // -(iMouse.x / iResolution.x) * 8.0;
float phi = M_PI;//((iMouse.y / iResolution.y) * 2.0 - 1.0) * M_PI;
mat3 rot_x = mat3(cos(theta), 0.0, sin(theta),
0.0, 1.0, 0.0,
-sin(theta), 0.0, cos(theta));
mat3 rot_y = mat3(1.0, 0.0, 0.0,
0.0, cos(phi), -sin(phi),
0.0, sin(phi), cos(phi));
mat3 cam_trans = rot_x * rot_y;
vec3 color = vec3(1.0, 207.0 / 255.0, 0.0); //normalize(vec3(sin(theta) + 1.0 / AO_MAGIC, cos(phi) + 1.0 / AO_MAGIC, sin(phi + theta) + 1.0 / AO_MAGIC));
fragColor.rgb = ray_march(/*cam_trans */ origin, cam_trans * rd, color);
fragColor.a = 1.0;
}
[test]
clear
draw rect -1 -1 2 2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.