#version 430 layout(local_size_x = 1, local_size_y = 1) in; #include #include #include // TODO: fix terrain lighting // TODO: add burn in front of asteroid // TODO: change leg texture // TODO: fog // TODO: post processing // TODO: structure on mountain const float MAX_DISTANCE = 1000.; // 1000. const int MAX_ITERATIONS = 800; // 600+ 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); const vec3 character_offset = vec3(-9.5, 2.6, 36.0); const vec3 death_offset = vec3(42., 70, 300); const vec3 plant1_offset = vec3(186.0, -68.0, 450.0); 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_WALKSTICK = 3; const int O_LEGS = 4; const int O_DEATH = 5; const int O_DEATHCONE = 6; const int O_PLANT1 = 7; const int O_PLANT1_BASE = 8; 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, stick; { float ctheta = PI / 9; vec3 charp = p - character_offset; cape = sdf_cone(charp, vec2(sin(ctheta), cos(ctheta))); 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)); cape = smin(cape, sdf_circle(charp + vec3(0, 0.12, 0), 0.21), 0.26); cape = smin(cape, sdf_box(charp + vec3(0.22, 0.64, 0), vec3(0.1, 0.22, 0.1), 0.1), 0.1); cape = smin(cape, sdf_box(charp + vec3(-0.22, 0.64, 0), vec3(0.1, 0.22, 0.1), 0.1), 0.1); float tilt = -PI / 3; vec3 handp = charp + vec3(-0.42, 0.82, 0); handp.xz *= mat2x2(cos(tilt), -sin(tilt), sin(tilt), cos(tilt)); cape = smin(cape, sdf_box(handp, vec3(0.22, 0.08, 0.08), 0.08), 0.06); stick = sdf_box(charp + vec3(-0.48, 0.86, -0.1), vec3(0.02, 0.9, 0.02), 0.04); 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, 4.2 - 1.8 * fbm(p / 2, 2)); float ctheta = PI / 17; vec3 conep = dp; float deaththeta = PI / 3; conep.xy *= mat2x2(cos(deaththeta), -sin(deaththeta), sin(deaththeta), cos(deaththeta)); conep -= vec3(0.0, 20.0, 1); conep -= vec3(0, sin(conep.y * 2.8) * (0.2 + fbm(conep.xy * 2)) * 2.2, 0); deathcone = sdf_cone(conep, vec2(sin(ctheta), cos(ctheta))); deathcone = max(deathcone, -sdf_plane(conep, vec3(0, 1, 0), 20.0)); } float plant1, plant1_base; { vec3 pos = p - plant1_offset; vec3 pp = pos; float spread = 0.7; pp += vec3(fbm(pp.yy / 1.5) * spread, 0, fbm((pp.yy + 2) / 1.5) * spread) - vec3(pp.y * spread / 3, 0, pp.y * spread / 3); pp.x = abs(pp.x) - 0.6 * pp.y + 0.06; pp.x = abs(pp.x) - 0.2 * pp.y + 0.06; pp.z = abs(pp.z) - 0.6 * pp.y + 0.06; pp.z = abs(pp.z) - 0.2 * pp.y + 0.06; plant1 = sdf_capsule(pp, 15, 1.2 * (sin(pp.y / 2 + 0.9) + 1.5) / 2); plant1_base = sdf_circle(pos - vec3(-0.05, 0.1, -0.36), 2.8); } float dist = dune; dist = min(dist, cape); dist = min(dist, stick); dist = min(dist, legs); dist = min(dist, death); dist = min(dist, deathcone); dist = min(dist, smin(plant1, plant1_base, 0.1)); if (dist == dune) obj_id = O_DUNE1; else if (dist == cape) obj_id = O_CAPE; else if (dist == stick) obj_id = O_WALKSTICK; else if (dist == legs) obj_id = O_LEGS; else if (dist == death) obj_id = O_DEATH; else if (dist == deathcone) obj_id = O_DEATHCONE; else if (dist == plant1) obj_id = O_PLANT1; else if (dist == plant1_base) obj_id = O_PLANT1_BASE; return dist; } vec3 get_material_properties(in int obj_id, in vec3 surface_point, in vec2 screen_point, in float dist, out float ambient) { vec3 color = skyblue; color = mix(color, skygreen, 1 - smoothstep(-0.2, 0.5, screen_point.y)); float fog = 0.3 * (1 - smoothstep(-0.5, 0.5, exp(-0.000001 * dist * dist * dist))); ambient = 0.06; if (obj_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.3, 0.3, fbm((surface_point.xz * 5) * 4))); } else if (obj_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 (obj_id == O_WALKSTICK) { color = vec3(0); } else if (obj_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 (obj_id == O_DEATH) { color = srgb(0.6, 0.5, 0.6); ambient = 0.2; } else if (obj_id == O_DEATHCONE) { float tail = sdf_circle(surface_point.xy - death_offset.xy - vec2(2.6, 1.3), 1.5); color = mix(color, srgb(1.0, 0.6, 0.0), (1 - smoothstep(-0, 10, tail)) * 0.6); color = mix(color, srgb(1.0, 0.3, 0.0), (1 - smoothstep(-0.5, 1, tail)) * 0.76); ambient = 1; fog = 0; } else if (obj_id == O_PLANT1) { color = srgb(0.48, 0.31, 0.61); color = mix(color, srgb(0.78, 0.31, 0.61), 1 - step(sin((surface_point.x) * 20) * cos((surface_point.y) * 20) - 0.5, 0)); ambient = 0.2; } else if (obj_id == O_PLANT1_BASE) { color = srgb(0.78, 0.31, 0.61); ambient = 0.2; } else { fog = 0; } color += fog; 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 = -1; return dist; } 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 obj_id = 0; vec3 campos = camera_position; // campos = character_offset - vec3(0, 0.5, 8); // character focus // campos = death_offset - vec3(0, 0, 38); // death focus // campos = plant1_offset - vec3(0, -12, 100); // plant focus vec3 uv_direction = normalize(vec3(uv, 1)); float dist = raymarch_scene(campos, uv_direction, obj_id); vec3 p = campos + uv_direction * dist; vec3 norm = surface_normal_scene(p); vec3 light = vec3(8, 500, -4); light = mix(light, vec3(0, light.y, -100), smoothstep(-20, 50, p.z)); light = mix(light, vec3(0, 280, 480), smoothstep(200, 400, p.z)); light = mix(light, vec3(-8, 500, 1000), smoothstep(760, 1000, p.z)); vec3 light_dir = normalize(light - p); float ambient; vec3 color = get_material_properties(obj_id, p, uv, dist, ambient); if (obj_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); vec3 color = image(uv); imageStore(output_image, ivec2(gl_GlobalInvocationID.xy), vec4(color, 1)); }