]> gitweb.factorcode.org Git - factor.git/blob - extra/gpu/demos/raytrace/raytrace.f.glsl
Fixes #2966
[factor.git] / extra / gpu / demos / raytrace / raytrace.f.glsl
1 #version 110
2
3 struct sphere
4 {
5     vec3 center;
6     float radius;
7     vec4 color;
8 };
9
10 uniform sphere spheres[4];
11 uniform float floor_height;
12 uniform vec4 floor_color[2];
13 uniform vec4 background_color;
14 uniform vec3 light_direction;
15
16 varying vec3 ray_origin, ray_direction;
17
18 const float FAR_AWAY = 1.0e20;
19 const vec4 reflection_color = vec4(1.0, 0.0, 1.0, 0.0);
20
21 float sphere_intersect(sphere s, vec3 ro, vec3 rd)
22 {
23     vec3 dist = (ro - s.center);
24
25     float b = dot(dist, normalize(rd));
26     float c = dot(dist, dist) - s.radius*s.radius;
27     float d = b * b - c;
28
29     return d > 0.0 ? -b - sqrt(d) : FAR_AWAY;
30 }
31
32 float floor_intersect(float height, vec3 ro, vec3 rd)
33 {
34     return (height - ro.y) / rd.y;
35 }
36
37 void
38 cast_ray(vec3 ro, vec3 rd, out sphere intersect_sphere, out bool intersect_floor, out float intersect_distance)
39 {
40     intersect_floor = false;
41     intersect_distance = FAR_AWAY;
42
43     for (int i = 0; i < 4; ++i) {
44         float d = sphere_intersect(spheres[i], ro, rd);
45
46         if (d > 0.0 && d < intersect_distance) {
47             intersect_distance = d;
48             intersect_sphere = spheres[i];
49         }
50     }
51
52     if (intersect_distance >= FAR_AWAY) {
53         intersect_distance = floor_intersect(floor_height, ro, rd);
54         if (intersect_distance < 0.0)
55             intersect_distance = FAR_AWAY;
56         intersect_floor = intersect_distance < FAR_AWAY;
57     }
58 }
59
60 vec4 render_floor(vec3 at, float distance, bool shadowed)
61 {
62     vec3 at2 = 0.125 * at;
63
64     float dropoff = exp(-0.005 * abs(distance)) * 0.8 + 0.2;
65     float fade = 0.5 * dropoff + 0.5;
66
67     vec4 color = fract((floor(at2.x) + floor(at2.z)) * 0.5) == 0.0
68         ? mix(floor_color[1], floor_color[0], fade)
69         : mix(floor_color[0], floor_color[1], fade);
70
71     float light = shadowed ? 0.2 : dropoff;
72
73     return color * light * dot(vec3(0.0, 1.0, 0.0), -light_direction);
74 }
75
76 vec4 sphere_color(vec4 color, vec3 normal, vec3 eye_ray, bool shadowed)
77 {
78     float light = shadowed
79         ? 0.2
80         : max(dot(normal, -light_direction), 0.0) * 0.8 + 0.2;
81
82     float spec = shadowed
83         ? 0.0
84         : 0.3 * pow(max(dot(reflect(-light_direction, normal), eye_ray), 0.0), 100.0);
85         
86     return color * light + vec4(spec);
87 }
88
89 bool reflection_p(vec4 color)
90 {
91     vec4 difference = color - reflection_color;
92     return dot(difference, difference) == 0.0;
93 }
94
95 vec4 render_sphere(sphere s, vec3 at, vec3 eye_ray, bool shadowed)
96 {
97     vec3 normal = normalize(at - s.center);
98
99     vec4 color;
100
101     if (reflection_p(s.color)) {
102         sphere reflect_sphere;
103         bool reflect_floor;
104         float reflect_distance;
105         vec3 reflect_direction = reflect(eye_ray, normal);
106
107         cast_ray(at, reflect_direction, reflect_sphere, reflect_floor, reflect_distance);
108
109         vec3 reflect_at = at + reflect_direction * reflect_distance;
110         if (reflect_floor)
111             color = render_floor(reflect_at, reflect_distance, false);
112         else if (reflect_distance < FAR_AWAY) {
113             vec3 reflect_normal = normalize(reflect_at - reflect_sphere.center);
114
115             color = sphere_color(reflect_sphere.color, reflect_normal, reflect_direction, false);
116         } else {
117             color = background_color;
118         }
119     } else
120         color = s.color;
121
122     return sphere_color(color, normal, eye_ray, shadowed);
123 }
124
125 void
126 main()
127 {
128     vec3 ray_direction_normalized = normalize(ray_direction);
129
130     sphere intersect_sphere;
131     bool intersect_floor;
132     float intersect_distance;
133
134     cast_ray(ray_origin, ray_direction_normalized, intersect_sphere, intersect_floor, intersect_distance);
135
136     vec3 at = ray_origin + ray_direction_normalized * intersect_distance;
137
138     sphere shadow_sphere;
139     bool shadow_floor;
140     float shadow_distance;
141
142     cast_ray(at - 0.0001 * light_direction, -light_direction, shadow_sphere, shadow_floor, shadow_distance);
143
144     bool shadowed = shadow_distance < FAR_AWAY;
145
146     if (intersect_floor)
147         gl_FragColor = render_floor(at, intersect_distance, shadowed);
148     else if (intersect_distance < FAR_AWAY)
149         gl_FragColor = render_sphere(intersect_sphere, at, ray_direction_normalized, shadowed);
150     else
151         gl_FragColor = background_color;
152 }
153