nuclear@0: /* vi:set filetype=glsl ts=4 sw=4: */ nuclear@0: #version 120 nuclear@0: #extension GL_ARB_gpu_shader5 : enable nuclear@0: nuclear@0: #define M_PI 3.1415926 nuclear@0: nuclear@0: #define INIT_EVERYTHING nuclear@0: #define USE_XFORM nuclear@0: #define OBJ_LINE_WIDTH 16.0 nuclear@0: nuclear@0: struct Ray { nuclear@0: vec3 origin, dir; nuclear@0: }; nuclear@0: nuclear@0: struct Material { nuclear@0: vec3 diffuse, specular; nuclear@0: float shininess; nuclear@0: vec4 megatex_rect; nuclear@0: float reflectivity; nuclear@0: }; nuclear@0: nuclear@0: struct HitPoint { nuclear@0: float dist; nuclear@0: vec3 pos, normal; nuclear@0: vec2 texcoord; nuclear@0: struct Material mat; nuclear@0: }; nuclear@0: nuclear@0: struct Sphere { nuclear@0: float index; nuclear@0: vec3 pos; nuclear@0: float radius; nuclear@0: struct Material mat; nuclear@0: }; nuclear@0: nuclear@0: struct Plane { nuclear@0: float index; nuclear@0: vec3 normal; nuclear@0: float dist; nuclear@0: struct Material mat; nuclear@0: }; nuclear@0: nuclear@0: struct Box { nuclear@0: float index; nuclear@0: vec3 min, max; nuclear@0: struct Material mat; nuclear@0: }; nuclear@0: nuclear@0: struct Light { nuclear@0: vec3 pos, color; nuclear@0: }; nuclear@0: nuclear@0: vec3 shade(in Ray ray, in HitPoint hit); nuclear@0: bool find_intersection(in Ray ray, out HitPoint hit); nuclear@0: bool sphere_intersect(in Sphere sph, in Ray ray, out HitPoint pt); nuclear@0: bool plane_intersect(in Plane plane, in Ray ray, out HitPoint pt); nuclear@0: bool box_intersect(in Box box, in Ray ray, out HitPoint pt); nuclear@0: vec3 transform(in vec3 v, in mat4 inv_xform); nuclear@0: Ray transform(in Ray ray, in mat4 xform, in mat4 inv_xform); nuclear@0: Ray get_primary_ray(); nuclear@0: nuclear@0: Sphere read_sphere(in float idx); nuclear@0: Plane read_plane(in float idx); nuclear@0: Box read_box(in float idx); nuclear@0: Material read_material(in sampler2D tex, in float ty); nuclear@0: void read_xform(in float idx, out mat4 xform, out mat4 inv_xform); nuclear@0: nuclear@0: uniform sampler2D tex_raydir; nuclear@0: uniform sampler2D tex_spheres, tex_planes, tex_boxes; nuclear@0: uniform sampler2D tex_megatex; nuclear@0: uniform sampler2D tex_xforms; nuclear@0: uniform samplerCube tex_env; nuclear@0: uniform vec2 fog; nuclear@0: nuclear@0: uniform Light lights[8]; nuclear@0: uniform int num_lights; nuclear@0: nuclear@0: int num_spheres, num_planes, num_boxes; nuclear@0: float sph_tex_sz, plane_tex_sz, box_tex_sz, xform_tex_sz; nuclear@0: nuclear@0: #ifdef INIT_EVERYTHING nuclear@0: Material default_material; nuclear@0: #endif nuclear@0: nuclear@0: void main() nuclear@0: { nuclear@0: #ifdef INIT_EVERYTHING nuclear@0: default_material.diffuse = default_material.specular = vec3(0.0, 0.0, 0.0); nuclear@0: default_material.shininess = 1.0; nuclear@0: default_material.reflectivity = 0.0; nuclear@0: default_material.megatex_rect = vec4(0.0, 0.0, 0.0, 0.0); nuclear@0: #endif nuclear@0: nuclear@0: Ray ray = get_primary_ray(); nuclear@0: nuclear@0: /* read the various descriptors specifying dimensions and counts for nuclear@0: * all the relevant data textures nuclear@0: */ nuclear@0: vec4 desc = texture2D(tex_spheres, vec2(0.0, 0.0)); nuclear@0: num_spheres = int(desc.x); nuclear@0: sph_tex_sz = desc.y; nuclear@0: nuclear@0: desc = texture2D(tex_planes, vec2(0.0, 0.0)); nuclear@0: num_planes = int(desc.x); nuclear@0: plane_tex_sz = desc.y; nuclear@0: nuclear@0: desc = texture2D(tex_boxes, vec2(0.0, 0.0)); nuclear@0: num_boxes = int(desc.x); nuclear@0: box_tex_sz = desc.y; nuclear@0: nuclear@0: xform_tex_sz = texture2D(tex_xforms, vec2(0.0, 0.0)).x; nuclear@0: nuclear@0: nuclear@0: HitPoint hit; nuclear@0: #ifdef INIT_EVERYTHING nuclear@0: hit.dist = 0.0; nuclear@0: hit.pos = hit.normal = vec3(0.0, 0.0, 0.0); nuclear@0: #endif nuclear@0: nuclear@0: vec3 color = vec3(0.0, 0.0, 0.0); nuclear@0: float energy = 1.0; nuclear@0: nuclear@0: int iter = 0; nuclear@0: while(energy > 0.01 && iter++ < 4) { nuclear@0: vec3 envcol = textureCube(tex_env, ray.dir).xyz; nuclear@0: nuclear@0: if(find_intersection(ray, hit)) { nuclear@0: float fog_t = clamp((hit.dist - fog.x) / (fog.y - fog.x), 0.0, 1.0); nuclear@0: color += mix(shade(ray, hit), envcol, fog_t) * energy; nuclear@0: energy *= hit.mat.reflectivity * (1.0 - fog_t); nuclear@0: ray.origin = hit.pos; nuclear@0: ray.dir = reflect(ray.dir, hit.normal); nuclear@0: } else { nuclear@0: color += envcol * energy; nuclear@0: energy = 0.0; nuclear@0: iter = 100; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: gl_FragColor.xyz = color; nuclear@0: gl_FragColor.w = 1.0; nuclear@0: } nuclear@0: nuclear@0: vec3 shade(in Ray ray, in HitPoint hit) nuclear@0: { nuclear@0: vec3 normal = faceforward(hit.normal, ray.dir, hit.normal); nuclear@0: nuclear@0: vec3 vdir = normalize(ray.dir); nuclear@0: vec3 vref = reflect(vdir, normal); nuclear@0: nuclear@0: /* if there's no texture rect.zw will be (0, 0, 0, 0) so this will map onto nuclear@0: * the top-left 1x1 null texture which is all white (having no effect) nuclear@0: */ nuclear@0: vec2 tc = mod(hit.texcoord, vec2(1.0, 1.0)) * hit.mat.megatex_rect.zw + hit.mat.megatex_rect.xy; nuclear@0: nuclear@0: vec3 diffuse_color = hit.mat.diffuse * texture2D(tex_megatex, tc).xyz; nuclear@0: nuclear@0: vec3 color = vec3(0.0, 0.0, 0.0); nuclear@0: for(int i=0; i 1.0) { nuclear@0: vec3 ldir = normalize(shadow_ray.dir); nuclear@0: nuclear@0: float diffuse = max(dot(ldir, normal), 0.0); nuclear@0: float specular = pow(max(dot(ldir, vref), 0.0), hit.mat.shininess); nuclear@0: nuclear@0: color += (diffuse_color * diffuse + hit.mat.specular * specular) * lights[i].color; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return color; nuclear@0: } nuclear@0: nuclear@0: bool find_intersection(in Ray ray, out HitPoint hit) nuclear@0: { nuclear@0: hit.dist = 100000.0; nuclear@0: #ifdef INIT_EVERYTHING nuclear@0: hit.pos = hit.normal = vec3(0.0, 0.0, 0.0); nuclear@0: hit.mat = default_material; nuclear@0: hit.texcoord = vec2(0.0, 0.0); nuclear@0: #endif nuclear@0: bool found = false; nuclear@0: nuclear@0: for(int i=0; i 0.0 ? 1.0 : -1.0, 0.0, 0.0); nuclear@0: nuclear@0: if(tmin > tymax || tymin > tmax) { nuclear@0: return false; nuclear@0: } nuclear@0: if(tymin > tmin) { nuclear@0: pt.normal = vec3(0.0, ray.origin.y > 0.0 ? 1.0 : -1.0, 0.0); nuclear@0: tmin = tymin; nuclear@0: } nuclear@0: if(tymax < tmax) { nuclear@0: tmax = tymax; nuclear@0: } nuclear@0: nuclear@0: float tzmin = (param[sgn[2]].z - ray.origin.z) * inv_dir.z; nuclear@0: float tzmax = (param[1 - sgn[2]].z - ray.origin.z) * inv_dir.z; nuclear@0: nuclear@0: if(tmin > tzmax || tzmin > tmax) { nuclear@0: return false; nuclear@0: } nuclear@0: if(tzmin > tmin) { nuclear@0: pt.normal = vec3(0.0, 0.0, ray.origin.z > 0.0 ? 1.0 : -1.0); nuclear@0: tmin = tzmin; nuclear@0: } nuclear@0: if(tzmax < tmax) { nuclear@0: tmax = tzmax; nuclear@0: } nuclear@0: nuclear@0: float t = tmin < EPSILON ? tmax : tmin; nuclear@0: if(t >= 1e-4) { nuclear@0: pt.dist = t; nuclear@0: pt.pos = ray.origin + ray.dir * t; nuclear@0: pt.mat = box.mat; nuclear@0: nuclear@0: float min_dist = 10000.0; nuclear@0: nuclear@0: vec3 offs = box.min + (box.max - box.min) / 2.0; nuclear@0: vec3 local_pt = pt.pos - offs; nuclear@0: nuclear@0: vec3 dist = abs((box.max - offs) - abs(local_pt)); nuclear@0: if(dist.x < min_dist) { nuclear@0: min_dist = dist.x; nuclear@0: pt.normal = sign(local_pt.x) * vec3(1.0, 0.0, 0.0); nuclear@0: pt.texcoord = pt.pos.zy; nuclear@0: } nuclear@0: if(dist.y < min_dist) { nuclear@0: min_dist = dist.y; nuclear@0: pt.normal = sign(local_pt.y) * vec3(0.0, 1.0, 0.0); nuclear@0: pt.texcoord = pt.pos.xz; nuclear@0: } nuclear@0: if(dist.z < min_dist) { nuclear@0: pt.normal = sign(local_pt.y) * vec3(0.0, 0.0, 1.0); nuclear@0: pt.texcoord = pt.pos.xy; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: #ifdef USE_XFORM nuclear@0: pt.pos = (xform * vec4(pt.pos, 1.0)).xyz; nuclear@0: pt.normal = normalize(transform(pt.normal, xform)); nuclear@0: #endif nuclear@0: return true; nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: vec3 transform(in vec3 v, in mat4 xform) nuclear@0: { nuclear@0: return mat3(xform) * v; nuclear@0: } nuclear@0: nuclear@0: Ray transform(in Ray ray, in mat4 xform, in mat4 inv_xform) nuclear@0: { nuclear@0: Ray res; nuclear@0: res.origin = (xform * vec4(ray.origin, 1.0)).xyz; nuclear@0: res.dir = transform(ray.dir, xform); nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: Ray get_primary_ray() nuclear@0: { nuclear@0: Ray ray; nuclear@0: ray.origin = (gl_ModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; nuclear@0: vec3 dir = texture2D(tex_raydir, gl_TexCoord[0].st).xyz; nuclear@0: ray.dir = normalize(gl_NormalMatrix * dir); nuclear@0: return ray; nuclear@0: } nuclear@0: nuclear@0: #define ITEM(x) ((float(x) + 0.5) / OBJ_LINE_WIDTH) nuclear@0: nuclear@0: Sphere read_sphere(in float idx) nuclear@0: { nuclear@0: Sphere sph; nuclear@0: // +1 because the first scanline is the descriptor nuclear@0: float ty = (idx + 1.0) / sph_tex_sz; nuclear@0: nuclear@0: sph.index = texture2D(tex_spheres, vec2(ITEM(0), ty)).x; nuclear@0: nuclear@0: vec4 texel = texture2D(tex_spheres, vec2(ITEM(1), ty)); nuclear@0: sph.pos = texel.xyz; nuclear@0: sph.radius = texel.w; nuclear@0: nuclear@0: sph.mat = read_material(tex_spheres, ty); nuclear@0: return sph; nuclear@0: } nuclear@0: nuclear@0: Plane read_plane(in float idx) nuclear@0: { nuclear@0: Plane plane; nuclear@0: // +1 (see above) nuclear@0: float ty = (idx + 1.0) / plane_tex_sz; nuclear@0: nuclear@0: plane.index = texture2D(tex_planes, vec2(ITEM(0), ty)).x; nuclear@0: nuclear@0: vec4 texel = texture2D(tex_planes, vec2(ITEM(1), ty)); nuclear@0: plane.normal = texel.xyz; nuclear@0: plane.dist = texel.w; nuclear@0: nuclear@0: plane.mat = read_material(tex_planes, ty); nuclear@0: return plane; nuclear@0: } nuclear@0: nuclear@0: Box read_box(in float idx) nuclear@0: { nuclear@0: Box box; nuclear@0: float ty = (idx + 1.0) / box_tex_sz; nuclear@0: nuclear@0: box.index = texture2D(tex_boxes, vec2(ITEM(0), ty)).x; nuclear@0: nuclear@0: box.min = texture2D(tex_boxes, vec2(ITEM(1), ty)).xyz; nuclear@0: box.max = texture2D(tex_boxes, vec2(ITEM(2), ty)).xyz; nuclear@0: nuclear@0: box.mat = read_material(tex_boxes, ty); nuclear@0: return box; nuclear@0: } nuclear@0: nuclear@0: void read_xform(in float idx, out mat4 xform, out mat4 inv_xform) nuclear@0: { nuclear@0: float ty = (idx + 1.0) / xform_tex_sz; nuclear@0: nuclear@0: for(int i=0; i<4; i++) { nuclear@0: xform[i] = texture2D(tex_xforms, vec2(ITEM(i), ty)); nuclear@0: } nuclear@0: inv_xform = inverse(xform); nuclear@0: /*for(int i=0; i<4; i++) { nuclear@0: inv_xform[i] = texture2D(tex_xforms, vec2(ITEM(float(i) + 4.0), ty)); nuclear@0: }*/ nuclear@0: } nuclear@0: nuclear@0: #define MAT_START 4 nuclear@0: Material read_material(in sampler2D tex, in float ty) nuclear@0: { nuclear@0: Material mat; nuclear@0: nuclear@0: vec4 texel = texture2D(tex, vec2(ITEM(MAT_START), ty)); nuclear@0: mat.diffuse = texel.xyz; nuclear@0: nuclear@0: texel = texture2D(tex, vec2(ITEM(MAT_START + 1), ty)); nuclear@0: mat.specular = texel.xyz; nuclear@0: mat.shininess = texel.w; nuclear@0: nuclear@0: texel = texture2D(tex, vec2(ITEM(MAT_START + 2), ty)); nuclear@0: mat.reflectivity = texel.x; nuclear@0: nuclear@0: mat.megatex_rect = texture2D(tex, vec2(ITEM(MAT_START + 3), ty)); nuclear@0: nuclear@0: return mat; nuclear@0: }