nuclear@0: /* vim: set ft=glsl:ts=4:sw=4 */ nuclear@0: uniform vec4 seed; nuclear@0: uniform sampler2D ray_tex; nuclear@0: uniform float err_thres; nuclear@0: uniform int iter; nuclear@0: nuclear@0: #define quat(s, x, y, z) vec4(x, y, z, s) nuclear@0: #define quat_identity() vec4(0.0, 0.0, 0.0, 1.0) nuclear@0: nuclear@0: #define vec2quat(v) (v).wxyz nuclear@0: nuclear@0: struct Ray { nuclear@0: vec3 origin; nuclear@0: vec3 dir; nuclear@0: }; nuclear@0: nuclear@0: struct Julia { nuclear@0: bool inside; nuclear@0: vec4 q; nuclear@0: vec4 qprime; nuclear@0: }; nuclear@0: nuclear@0: struct ISect { nuclear@0: bool hit; nuclear@0: float t; nuclear@0: vec3 pos; nuclear@0: vec3 normal; nuclear@0: vec3 color; nuclear@0: float kr; nuclear@0: }; nuclear@0: nuclear@0: ISect find_intersection(Ray ray); nuclear@0: vec3 shade(Ray ray, ISect isect); nuclear@0: float amboc(ISect isect); nuclear@0: vec3 sky(Ray ray); nuclear@0: Julia julia(vec4 q, vec4 c); nuclear@0: float julia_dist(vec4 z); nuclear@0: vec3 julia_grad(vec4 z); nuclear@0: vec4 quat_mul(vec4 q1, vec4 q2); nuclear@0: vec4 quat_sq(vec4 q); nuclear@0: float quat_length_sq(vec4 q); nuclear@0: ISect ray_julia(Ray ray); nuclear@0: ISect ray_sphere(Ray ray, float rad); nuclear@0: ISect ray_floor(Ray ray); nuclear@0: Ray get_primary_ray(); nuclear@0: nuclear@0: nuclear@0: void main() nuclear@0: { nuclear@0: Ray ray = get_primary_ray(); nuclear@0: nuclear@0: float energy = 1.0; nuclear@0: vec3 color = vec3(0.0, 0.0, 0.0); nuclear@0: nuclear@0: while(energy > 0.001) { nuclear@0: ISect res = find_intersection(ray); nuclear@0: nuclear@0: if(res.hit) { nuclear@0: color += shade(ray, res) * energy; nuclear@0: energy *= res.kr; nuclear@0: nuclear@0: ray.origin = res.pos; nuclear@0: ray.dir = reflect(ray.dir, res.normal); nuclear@0: } else { nuclear@0: color += sky(ray) * energy; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: gl_FragColor = vec4(color, 1.0); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: ISect find_intersection(Ray ray) nuclear@0: { nuclear@0: ISect res; nuclear@0: res.hit = false; nuclear@0: nuclear@0: ISect bhit = ray_sphere(ray, 2.0); nuclear@0: if(bhit.hit) { nuclear@0: ray.origin = bhit.pos; nuclear@0: res = ray_julia(ray); nuclear@0: } nuclear@0: nuclear@0: if(!res.hit) { nuclear@0: res = ray_floor(ray); nuclear@0: } nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: vec3 shade(Ray ray, ISect isect) nuclear@0: { nuclear@0: vec3 ldir = normalize(vec3(10.0, 10.0, -10.0) - isect.pos); nuclear@0: vec3 vdir = -ray.dir; nuclear@0: vec3 hdir = normalize(ldir + vdir); nuclear@0: nuclear@0: float ndotl = dot(ldir, isect.normal); nuclear@0: float ndoth = dot(hdir, isect.normal); nuclear@0: nuclear@0: vec3 dcol = /*isect.color * max(ndotl, 0.0) */ amboc(isect); nuclear@0: vec3 scol = vec3(1.0, 1.0, 1.0) * pow(max(ndoth, 0.0), 40.0); nuclear@0: nuclear@0: return /*vec3(0.05, 0.05, 0.05) + */dcol;// + scol; nuclear@0: } nuclear@0: nuclear@0: #define AO_STEP 0.04 nuclear@0: #define AO_MAGIC 8.0 nuclear@0: float amboc(ISect isect) nuclear@0: { nuclear@0: float sum = 0.0; nuclear@0: nuclear@0: for(float fi=0.0; fi<5.0; fi+=1.0) { nuclear@0: float sample_dist = fi * AO_STEP; nuclear@0: vec3 pt = isect.pos + isect.normal * sample_dist; nuclear@0: float jdist = julia_dist(quat(pt.x, pt.y, pt.z, 0.0)); nuclear@0: nuclear@0: sum += 1.0 / pow(2.0, fi) * (sample_dist - jdist); nuclear@0: } nuclear@0: nuclear@0: return 1.0 - AO_MAGIC * sum; nuclear@0: } nuclear@0: nuclear@0: vec3 sky(Ray ray) nuclear@0: { nuclear@0: vec3 col1 = vec3(0.75, 0.78, 0.8); nuclear@0: vec3 col2 = vec3(0.56, 0.7, 1.0); nuclear@0: nuclear@0: float t = max(ray.dir.y, -0.5); nuclear@0: return mix(col1, col2, t); nuclear@0: } nuclear@0: nuclear@0: Julia julia(vec4 q, vec4 c) nuclear@0: { nuclear@0: Julia res; nuclear@0: res.inside = true; nuclear@0: nuclear@0: res.q = q; nuclear@0: res.qprime = quat_identity(); nuclear@0: nuclear@0: for(int i=0; i 8.0) { nuclear@0: res.inside = false; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: float julia_dist(vec4 z) nuclear@0: { nuclear@0: Julia jres = julia(z, seed); nuclear@0: nuclear@0: float lenq = length(jres.q); nuclear@0: float lenqprime = length(jres.qprime); nuclear@0: nuclear@0: return 0.5 * lenq * log(lenq) / lenqprime; nuclear@0: } nuclear@0: nuclear@0: #define OFFS 1e-4 nuclear@0: vec3 julia_grad(vec4 z) nuclear@0: { nuclear@0: vec3 grad; nuclear@0: grad.x = julia_dist(z + quat(OFFS, 0.0, 0.0, 0.0)) - julia_dist(z - quat(OFFS, 0.0, 0.0, 0.0)); nuclear@0: grad.y = julia_dist(z + quat(0.0, OFFS, 0.0, 0.0)) - julia_dist(z - quat(0.0, OFFS, 0.0, 0.0)); nuclear@0: grad.z = julia_dist(z + quat(0.0, 0.0, OFFS, 0.0)) - julia_dist(z - quat(0.0, 0.0, OFFS, 0.0)); nuclear@0: return grad; nuclear@0: } nuclear@0: nuclear@0: vec4 quat_mul(vec4 q1, vec4 q2) nuclear@0: { nuclear@0: vec4 res; nuclear@0: res.w = q1.w * q2.w - dot(q1.xyz, q2.xyz); nuclear@0: res.xyz = q1.w * q2.xyz + q2.w * q1.xyz + cross(q1.xyz, q2.xyz); nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: vec4 quat_sq(vec4 q) nuclear@0: { nuclear@0: vec4 res; nuclear@0: res.w = q.w * q.w - dot(q.xyz, q.xyz); nuclear@0: res.xyz = 2.0 * q.w * q.xyz; nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: ISect ray_julia(Ray inray) nuclear@0: { nuclear@0: float dist_acc = 0.0; nuclear@0: Ray ray = inray; nuclear@0: ISect res; nuclear@0: nuclear@0: for(float fi=0.0; ; fi+=0.1) { nuclear@0: vec4 q = quat(ray.origin.x, ray.origin.y, ray.origin.z, 0.0); nuclear@0: nuclear@0: float dist = julia_dist(q); nuclear@0: nuclear@0: ray.origin += ray.dir * dist; nuclear@0: dist_acc += dist; nuclear@0: nuclear@0: if(dist < err_thres) { nuclear@0: res.hit = true; nuclear@0: res.t = dist_acc; nuclear@0: res.pos = ray.origin; nuclear@0: res.normal = normalize(julia_grad(quat(res.pos.x, res.pos.y, res.pos.z, 0.0))); nuclear@0: res.color = vec3(0.75, 0.8, 0.9);//abs(res.normal) * 0.2; nuclear@0: //res.kr = 0.6; nuclear@0: res.kr = 0.0; nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: if(dot(ray.origin, ray.origin) > 100.0) { nuclear@0: res.hit = false; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: ISect ray_sphere(Ray ray, float rad) nuclear@0: { nuclear@0: ISect res; nuclear@0: res.hit = false; nuclear@0: nuclear@0: float a = dot(ray.dir, ray.dir); nuclear@0: float b = 2.0 * dot(ray.dir, ray.origin); nuclear@0: float c = dot(ray.origin, ray.origin) - rad * rad; nuclear@0: nuclear@0: float d = b * b - 4.0 * a * c; nuclear@0: if(d < 0.0) return res; nuclear@0: nuclear@0: float sqrt_d = sqrt(d); nuclear@0: float t1 = (-b + sqrt_d) / (2.0 * a); nuclear@0: float t2 = (-b - sqrt_d) / (2.0 * a); nuclear@0: nuclear@0: if((t1 >= 0.0 || t2 >= 0.0)) { nuclear@0: if(t1 < 0.0) t1 = t2; nuclear@0: if(t2 < 0.0) t2 = t1; nuclear@0: nuclear@0: res.hit = true; nuclear@0: res.t = min(t1, t2); nuclear@0: res.pos = ray.origin + ray.dir * res.t; nuclear@0: res.color = vec3(1.0, 0.3, 0.2); nuclear@0: res.normal = res.pos / rad; nuclear@0: } nuclear@0: nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: #define FLOOR_HEIGHT (-2.0) nuclear@0: nuclear@0: ISect ray_floor(Ray ray) nuclear@0: { nuclear@0: ISect res; nuclear@0: res.hit = false; nuclear@0: nuclear@0: if(ray.origin.y < FLOOR_HEIGHT || ray.dir.y >= 0.0) { nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: res.normal = vec3(0.0, 1.0, 0.0); nuclear@0: float ndotdir = dot(res.normal, ray.dir); nuclear@0: nuclear@0: float t = (FLOOR_HEIGHT - ray.origin.y) / ndotdir; nuclear@0: res.pos = ray.origin + ray.dir * t; nuclear@0: nuclear@0: if(abs(res.pos.x) > 8.0 || abs(res.pos.z) > 8.0) { nuclear@0: res.hit = false; nuclear@0: } else { nuclear@0: res.hit = true; nuclear@0: nuclear@0: float chess = mod(floor(res.pos.x) + floor(res.pos.z), 2.0); nuclear@0: res.color = mix(vec3(0.498, 0.165, 0.149), vec3(0.776, 0.851, 0.847), chess); nuclear@0: res.kr = 0.0; nuclear@0: } nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: Ray get_primary_ray() nuclear@0: { nuclear@0: Ray ray; nuclear@0: vec2 tc = gl_TexCoord[0].xy; nuclear@0: ray.dir = gl_NormalMatrix * normalize(texture2D(ray_tex, tc).xyz); nuclear@0: ray.origin = (gl_ModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; nuclear@0: return ray; nuclear@0: } nuclear@0: