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