#version 430 layout(local_size_x = 1, local_size_y = 1) in; #include #include #include const float MAX_DISTANCE = 1000.; const int MAX_ITERATIONS = 500; const float MIN_GAP = 0.001; uniform float seconds = 0; layout(rgba8) uniform readonly image2D noise_texture; layout(rgba8) uniform writeonly image2D output_image; uniform float antialiasing = 0.28; uniform bool enable_antialiasing = false; // uniform vec3 camera_position = vec3(0, 1, -2); // uniform vec3 camera_position = vec3(-9.5, 2.0, 30.0); // character focus uniform vec3 camera_position = vec3(12., 16, 60); // death focus const vec3 character_offset = vec3(-9.5, 2.6, 36.0); const vec3 death_offset = vec3(12., 16, 80); vec3 pink = srgb(0.82, 0.33, 0.51); vec3 teal = srgb(0.28, 0.74, 0.75); vec3 skyblue = srgb(0.73, 0.93, 0.95); vec3 skygreen = srgb(0.53, 0.72, 0.54); vec3 violet = srgb(0.38, 0.21, 0.51); vec3 sandred = srgb(0.82, 0.44, 0.42); vec3 sandyellow = srgb(0.81, 0.66, 0.43); // 0.79,0.60,0.34 vec3 red1 = srgb(0.69, 0.09, 0.06); vec3 gold = srgb(0.89, 0.77, 0.26); float sample_noise(in vec2 p) { vec4 noise = imageLoad(noise_texture, ivec2(p * imageSize(noise_texture))); return noise.x; } const int O_DUNE1 = 1; const int O_CAPE = 2; const int O_LEGS = 3; const int O_DEATH = 4; const int O_DEATHCONE = 5; float scene(in vec3 p, out int obj_id) { float dune; { float dune_elevation = 100.0 * fbm((p.xz + vec2(10.0, -40)) / 360) - 0.1 * fbm(p.xz / 1.2); dune_elevation = mix(dune_elevation, dune_elevation - 2.0 - 0.3 * fbm((p.xz - 1) / 1.1), smoothstep(-50, 100, p.z) * step(p.z, 200)); dune_elevation = mix(dune_elevation, dune_elevation * 1.4 - 86, smoothstep(740, 920, p.z)); dune_elevation = mix(dune_elevation, 0, 1 - step(p.z, 1200)); dune_elevation = mix(dune_elevation, dune_elevation + 44, step(p.z, 740) * smoothstep(100, 740, p.z)); dune = sdf_plane(p, vec3(0, 1, 0), dune_elevation); } float cape, legs; { float ctheta = PI / 9; vec3 charp = p - character_offset; cape = sd_cone(charp, vec2(sin(ctheta), cos(ctheta))); cape = smin(cape, sdf_circle(charp + vec3(0, 0.12, 0), 0.21), 0.26); cape = max(cape, -sdf_plane(charp, vec3(0, 1, 0), 1.20 + 0.04 * sin(16 * charp.x) + 0.6 * (0.76 - fbm(charp.xz / 1.5)))); cape = max(cape, -sdf_circle(charp + vec3(0, 1.42, -0.12), 0.51)); legs = sdf_box(charp + vec3(0.1, 1.6, 0.1), vec3(0.08, 0.3, 0.08), 0.1); legs = min(legs, sdf_box(charp + vec3(-0.2, 1.6, 0.1), vec3(0.08, 0.3, 0.08), 0.1)); } float death, deathcone; { vec3 dp = p - death_offset; death = sdf_circle(dp, 2.0 - 1.4 * fbm(p, 2)); float ctheta = PI / 21; vec3 conep = dp; float deaththeta = PI / 3; conep.xy *= mat2x2(cos(deaththeta), -sin(deaththeta), sin(deaththeta), cos(deaththeta)); conep -= vec3(0, 11.0, 0); conep -= vec3(0, sin(conep.y * 10), 0); deathcone = sd_cone(conep, vec2(sin(ctheta), cos(ctheta))); deathcone = max(deathcone, -sdf_plane(conep, vec3(0, 1, 0), 10)); } float dist = dune; dist = min(dist, cape); dist = min(dist, legs); dist = min(dist, death); dist = min(dist, deathcone); if (dist == dune) obj_id = O_DUNE1; else if (dist == cape) obj_id = O_CAPE; else if (dist == legs) obj_id = O_LEGS; else if (dist == death) obj_id = O_DEATH; else if (dist == deathcone) obj_id = O_DEATHCONE; return dist; } vec3 get_material_properties(in int object_id, in vec3 surface_point, in vec2 screen_point, out float ambient) { vec3 color = skyblue; color = mix(color, skygreen, 1 - smoothstep(-0.2, 0.5, screen_point.y)); ambient = 0.06; if (object_id == O_DUNE1) { color = sandred; color = mix(color, sandyellow, 1 - smoothstep(-0.2, 0.2, fbm(surface_point.xz * 2))); color = mix(color, red1, 1 - smoothstep(-0.2, 0.2, fbm((surface_point.xz + 2) * 4))); } else if (object_id == O_CAPE) { color = srgb(0.3, 0.3, 0.5); float x = surface_point.x * 10; float y = surface_point.y * 32; color = mix(color, sandyellow, step(surface_point.y, character_offset.y - 0.38) * step(abs(sin(x) + cos(y) - cos(x) * sin(y)) - 0.18, 0)); color = mix(color, sandyellow, (1 - step(surface_point.y, character_offset.y - 0.40)) * step(surface_point.y, character_offset.y - 0.35) * step(sin(surface_point.y * 50), 0.38)); ambient = 0.2; } else if (object_id == O_LEGS) { color = vec3(0.04); color = mix(color, srgb(0.3, 0.3, 0.5), step(sin(surface_point.y * 90), 0)); ambient = 0.01; } else if (object_id == O_DEATH) { color = srgb(0.6, 0.5, 0.6); ambient = 0.2; } else if (object_id == O_DEATHCONE) { color = srgb(1.0, 0.5, 0.6); ambient = 1; } return color; } float raymarch_scene(vec3 ray_origin, vec3 ray_direction, out int obj_id) { float dist = 0.; for (int i = 0; i < MAX_ITERATIONS && dist <= MAX_DISTANCE; i++) { vec3 p = ray_origin + ray_direction * dist; float gap = scene(p, obj_id); dist += gap; if (gap <= MIN_GAP) return dist; } obj_id = 0; return -1; } vec3 surface_normal_scene(in vec3 p) { vec2 e = vec2(MIN_GAP, 0); int obj_id; return normalize(vec3( scene(p + e.xyy, obj_id) - scene(p - e.xyy, obj_id), scene(p + e.yxy, obj_id) - scene(p - e.yxy, obj_id), scene(p + e.yyx, obj_id) - scene(p - e.yyx, obj_id))); } vec3 image(vec2 uv) { int object_id = 0; vec3 uv_direction = normalize(vec3(uv, 1)); float dist = raymarch_scene(camera_position, uv_direction, object_id); vec3 p = camera_position + uv_direction * dist; vec3 norm = surface_normal_scene(p); vec3 light = vec3(8, 300, -4); light = mix(light, vec3(0, light.y, -100), smoothstep(-20, 50, p.z)); light = mix(light, vec3(0, light.y, 480), smoothstep(200, 400, p.z)); light = mix(light, vec3(-8, 280, 1000), smoothstep(800, 1000, p.z)); vec3 light_dir = normalize(light - p); float ambient; vec3 color = get_material_properties(object_id, p, uv, ambient); if (object_id > 0) { float contrast = clamp(dot(norm, light_dir), 0, 1); color *= clamp((1 - ambient) * contrast + ambient, 0, 1); } return color; } vec3 antialiased_image(vec2 uv) { vec2 resolution = vec2(imageSize(output_image).xy); float s = antialiasing / max(resolution.x, resolution.y); vec3 sum = vec3(0); int count = 0; for (int i = -2; i < 2; i++) for (int j = -2; j < 2; j++, count++) sum += image(uv + vec2(i, j) * s); return sum / count; } void main() { vec2 resolution = vec2(imageSize(output_image).xy); float aspect_ratio = resolution.y / resolution.x; vec2 uv = (gl_GlobalInvocationID.xy - resolution / 2) * vec2(1.0, aspect_ratio) / resolution; vec3 color = enable_antialiasing ? antialiased_image(uv) : image(uv); imageStore(output_image, ivec2(gl_GlobalInvocationID.xy), vec4(color, 1)); }