rev |
line source |
nuclear@4
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <assert.h>
|
nuclear@4
|
3 #include <algorithm>
|
nuclear@0
|
4 #include "scene.h"
|
nuclear@0
|
5 #include "image.h"
|
nuclear@0
|
6 #include "rend.h"
|
nuclear@0
|
7 #include "opengl.h"
|
nuclear@0
|
8 #include "glsdr.h"
|
nuclear@0
|
9
|
nuclear@0
|
10 enum {
|
nuclear@0
|
11 TEX_RAYDIR,
|
nuclear@0
|
12 TEX_SPHERES,
|
nuclear@0
|
13 TEX_PLANES,
|
nuclear@0
|
14 TEX_BOXES,
|
nuclear@4
|
15 TEX_CONES,
|
nuclear@0
|
16 TEX_TEXTURES,
|
nuclear@0
|
17 TEX_ENV,
|
nuclear@0
|
18 TEX_XFORM,
|
nuclear@0
|
19
|
nuclear@0
|
20 NUM_SDR_TEXTURES
|
nuclear@0
|
21 };
|
nuclear@0
|
22
|
nuclear@0
|
23 bool reload_shader();
|
nuclear@0
|
24 void gen_ray_texture(unsigned int tex, int xsz, int ysz, float vfov);
|
nuclear@0
|
25 static Vector3 get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg);
|
nuclear@0
|
26 static int round_pow2(int x);
|
nuclear@0
|
27
|
nuclear@0
|
28 static GPUScene *scn;
|
nuclear@0
|
29
|
nuclear@0
|
30 static unsigned int sdr;
|
nuclear@0
|
31 static unsigned int textures[NUM_SDR_TEXTURES];
|
nuclear@0
|
32
|
nuclear@0
|
33 bool init_renderer(GPUScene *s, int xsz, int ysz)
|
nuclear@0
|
34 {
|
nuclear@0
|
35 scn = s;
|
nuclear@0
|
36
|
nuclear@4
|
37 int max_tex_units;
|
nuclear@4
|
38 glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_tex_units);
|
nuclear@4
|
39 printf("maximum texture units: %d\n", max_tex_units);
|
nuclear@4
|
40 if(NUM_SDR_TEXTURES > max_tex_units) {
|
nuclear@4
|
41 fprintf(stderr, "not enough texture units available!\n");
|
nuclear@4
|
42 return false;
|
nuclear@4
|
43 }
|
nuclear@4
|
44
|
nuclear@0
|
45 glGenTextures(1, textures + TEX_RAYDIR);
|
nuclear@0
|
46 glBindTexture(GL_TEXTURE_2D, textures[TEX_RAYDIR]);
|
nuclear@0
|
47 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
nuclear@0
|
48 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
nuclear@0
|
49 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
nuclear@0
|
50 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
nuclear@0
|
51
|
nuclear@0
|
52 if(!(s->create_textures())) {
|
nuclear@0
|
53 fprintf(stderr, "failed to create scene textures\n");
|
nuclear@0
|
54 return false;
|
nuclear@0
|
55 }
|
nuclear@0
|
56
|
nuclear@0
|
57 textures[TEX_SPHERES] = s->get_texture(GPUScene::TEX_SPHERE);
|
nuclear@0
|
58 textures[TEX_PLANES] = s->get_texture(GPUScene::TEX_PLANE);
|
nuclear@0
|
59 textures[TEX_BOXES] = s->get_texture(GPUScene::TEX_BOX);
|
nuclear@4
|
60 textures[TEX_CONES] = s->get_texture(GPUScene::TEX_CONE);
|
nuclear@0
|
61 textures[TEX_TEXTURES] = s->get_texture(GPUScene::TEX_TEXTURE);
|
nuclear@0
|
62 textures[TEX_ENV] = s->get_texture(GPUScene::TEX_ENV);
|
nuclear@0
|
63 textures[TEX_XFORM] = s->get_texture(GPUScene::TEX_XFORM);
|
nuclear@0
|
64
|
nuclear@0
|
65 if(!reload_shader()) {
|
nuclear@0
|
66 return false;
|
nuclear@0
|
67 }
|
nuclear@0
|
68
|
nuclear@0
|
69 resize_renderer(xsz, ysz);
|
nuclear@0
|
70
|
nuclear@0
|
71 return true;
|
nuclear@0
|
72 }
|
nuclear@0
|
73
|
nuclear@0
|
74 bool reload_shader()
|
nuclear@0
|
75 {
|
nuclear@0
|
76 if(sdr) {
|
nuclear@0
|
77 free_program(sdr);
|
nuclear@0
|
78 }
|
nuclear@0
|
79
|
nuclear@0
|
80 printf("loading shader...\n");
|
nuclear@0
|
81
|
nuclear@0
|
82 if(!(sdr = create_program_load("sdr/vertex.glsl", "sdr/rt.glsl"))) {
|
nuclear@0
|
83 return false;
|
nuclear@0
|
84 }
|
nuclear@0
|
85 set_uniform_int(sdr, "tex_raydir", TEX_RAYDIR);
|
nuclear@0
|
86 set_uniform_int(sdr, "tex_spheres", TEX_SPHERES);
|
nuclear@0
|
87 set_uniform_int(sdr, "tex_planes", TEX_PLANES);
|
nuclear@0
|
88 set_uniform_int(sdr, "tex_boxes", TEX_BOXES);
|
nuclear@4
|
89 set_uniform_int(sdr, "tex_cones", TEX_CONES);
|
nuclear@0
|
90 set_uniform_int(sdr, "tex_megatex", TEX_TEXTURES);
|
nuclear@0
|
91 set_uniform_int(sdr, "tex_env", TEX_ENV);
|
nuclear@0
|
92 set_uniform_int(sdr, "tex_xforms", TEX_XFORM);
|
nuclear@0
|
93
|
nuclear@0
|
94 set_uniform_int(sdr, "num_lights", scn->get_light_count());
|
nuclear@0
|
95
|
nuclear@0
|
96 for(int i=0; i<scn->get_light_count(); i++) {
|
nuclear@0
|
97 const Light *lt = scn->get_light(i);
|
nuclear@0
|
98
|
nuclear@0
|
99 char name[64];
|
nuclear@0
|
100 sprintf(name, "lights[%d].pos", i);
|
nuclear@0
|
101 set_uniform_float3(sdr, name, lt->pos.x, lt->pos.y, lt->pos.z);
|
nuclear@0
|
102
|
nuclear@0
|
103 sprintf(name, "lights[%d].color", i);
|
nuclear@0
|
104 set_uniform_float3(sdr, name, lt->color.x, lt->color.y, lt->color.z);
|
nuclear@0
|
105 }
|
nuclear@0
|
106
|
nuclear@0
|
107 Vector2 fog;
|
nuclear@0
|
108 scn->get_fog(&fog.x, &fog.y);
|
nuclear@0
|
109 if(fog.x < 0.0 && fog.y < 0.0) {
|
nuclear@0
|
110 fog.x = 0.0;
|
nuclear@0
|
111 fog.y = 100000.0;
|
nuclear@0
|
112 }
|
nuclear@0
|
113 set_uniform_float2(sdr, "fog", fog.x, fog.y);
|
nuclear@0
|
114 return true;
|
nuclear@0
|
115 }
|
nuclear@0
|
116
|
nuclear@0
|
117 void destroy_renderer()
|
nuclear@0
|
118 {
|
nuclear@0
|
119 free_program(sdr);
|
nuclear@0
|
120 sdr = 0;
|
nuclear@0
|
121 }
|
nuclear@0
|
122
|
nuclear@0
|
123 void resize_renderer(int xsz, int ysz)
|
nuclear@0
|
124 {
|
nuclear@0
|
125 gen_ray_texture(textures[TEX_RAYDIR], xsz, ysz, 45.0f);
|
nuclear@0
|
126 }
|
nuclear@0
|
127
|
nuclear@0
|
128 float *render_frame(long msec)
|
nuclear@0
|
129 {
|
nuclear@0
|
130 scn->prepare_xform(msec);
|
nuclear@0
|
131 scn->update_xform_texture();
|
nuclear@0
|
132
|
nuclear@0
|
133 Camera *cam = scn->get_camera();
|
nuclear@0
|
134 glMatrixMode(GL_MODELVIEW);
|
nuclear@0
|
135 glLoadIdentity();
|
nuclear@0
|
136 Vector3 cpos = cam->get_position();
|
nuclear@0
|
137 glTranslatef(cpos.x, cpos.y, cpos.z);
|
nuclear@0
|
138 Matrix4x4 cmat = cam->get_matrix();
|
nuclear@0
|
139 glMultTransposeMatrixf(cmat[0]);
|
nuclear@0
|
140
|
nuclear@0
|
141 for(int i=0; i<NUM_SDR_TEXTURES; i++) {
|
nuclear@0
|
142 glActiveTexture(GL_TEXTURE0 + i);
|
nuclear@0
|
143 if(i == TEX_ENV) {
|
nuclear@0
|
144 glBindTexture(GL_TEXTURE_CUBE_MAP, textures[i]);
|
nuclear@0
|
145 } else {
|
nuclear@0
|
146 glBindTexture(GL_TEXTURE_2D, textures[i]);
|
nuclear@0
|
147 }
|
nuclear@0
|
148 }
|
nuclear@0
|
149 glActiveTexture(GL_TEXTURE0);
|
nuclear@0
|
150
|
nuclear@0
|
151 glUseProgram(sdr);
|
nuclear@0
|
152
|
nuclear@0
|
153 glBegin(GL_QUADS);
|
nuclear@0
|
154 glTexCoord2f(0, 1);
|
nuclear@0
|
155 glVertex2f(-1, -1);
|
nuclear@0
|
156 glTexCoord2f(1, 1);
|
nuclear@0
|
157 glVertex2f(1, -1);
|
nuclear@0
|
158 glTexCoord2f(1, 0);
|
nuclear@0
|
159 glVertex2f(1, 1);
|
nuclear@0
|
160 glTexCoord2f(0, 0);
|
nuclear@0
|
161 glVertex2f(-1, 1);
|
nuclear@0
|
162 glEnd();
|
nuclear@0
|
163
|
nuclear@0
|
164 glUseProgram(0);
|
nuclear@0
|
165
|
nuclear@0
|
166 assert(glGetError() == GL_NO_ERROR);
|
nuclear@0
|
167 return 0;
|
nuclear@0
|
168 }
|
nuclear@0
|
169
|
nuclear@0
|
170 void gen_ray_texture(unsigned int tex, int xsz, int ysz, float vfov)
|
nuclear@0
|
171 {
|
nuclear@0
|
172 int tex_xsz = round_pow2(xsz);
|
nuclear@0
|
173 int tex_ysz = round_pow2(ysz);
|
nuclear@0
|
174 float *teximg, *dir;
|
nuclear@0
|
175
|
nuclear@0
|
176 teximg = new float[3 * tex_xsz * tex_ysz];
|
nuclear@0
|
177 dir = teximg;
|
nuclear@0
|
178
|
nuclear@0
|
179 for(int i=0; i<tex_ysz; i++) {
|
nuclear@0
|
180 for(int j=0; j<tex_xsz; j++) {
|
nuclear@0
|
181 if(j < xsz && i < ysz) {
|
nuclear@0
|
182 Vector3 rdir = get_primary_ray_dir(j, i, xsz, ysz, vfov);
|
nuclear@0
|
183 dir[0] = rdir.x;
|
nuclear@0
|
184 dir[1] = rdir.y;
|
nuclear@0
|
185 dir[2] = rdir.z;
|
nuclear@0
|
186 } else {
|
nuclear@0
|
187 dir[0] = dir[1] = 0.0f;
|
nuclear@0
|
188 dir[2] = 1.0f;
|
nuclear@0
|
189 }
|
nuclear@0
|
190
|
nuclear@0
|
191 dir += 3;
|
nuclear@0
|
192 }
|
nuclear@0
|
193 }
|
nuclear@0
|
194
|
nuclear@0
|
195 glBindTexture(GL_TEXTURE_2D, tex);
|
nuclear@0
|
196 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, tex_xsz, tex_ysz, 0, GL_RGB, GL_FLOAT, teximg);
|
nuclear@0
|
197 delete [] teximg;
|
nuclear@0
|
198 }
|
nuclear@0
|
199
|
nuclear@0
|
200 static Vector3 calc_sample_pos(int x, int y, int xsz, int ysz)
|
nuclear@0
|
201 {
|
nuclear@0
|
202 float ppos[2];
|
nuclear@0
|
203 float aspect = (float)xsz / (float)ysz;
|
nuclear@0
|
204
|
nuclear@0
|
205 float pwidth = 2.0 * aspect / (float)xsz;
|
nuclear@0
|
206 float pheight = 2.0 / (float)ysz;
|
nuclear@0
|
207
|
nuclear@0
|
208 ppos[0] = (float)x * pwidth - aspect;
|
nuclear@0
|
209 ppos[1] = 1.0 - (float)y * pheight;
|
nuclear@0
|
210
|
nuclear@0
|
211 return Vector3(ppos[0], ppos[1], 0.0f);
|
nuclear@0
|
212 }
|
nuclear@0
|
213
|
nuclear@0
|
214
|
nuclear@0
|
215 static Vector3 get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg)
|
nuclear@0
|
216 {
|
nuclear@0
|
217 float vfov = M_PI * vfov_deg / 180.0;
|
nuclear@0
|
218
|
nuclear@0
|
219 Vector3 dir = calc_sample_pos(x, y, w, h);
|
nuclear@0
|
220 dir.z = 1.0 / tan(vfov / 2.0);
|
nuclear@0
|
221 dir.normalize();
|
nuclear@0
|
222
|
nuclear@0
|
223 return dir;
|
nuclear@0
|
224 }
|
nuclear@0
|
225
|
nuclear@0
|
226 static int round_pow2(int x)
|
nuclear@0
|
227 {
|
nuclear@0
|
228 x--;
|
nuclear@0
|
229 x = (x >> 1) | x;
|
nuclear@0
|
230 x = (x >> 2) | x;
|
nuclear@0
|
231 x = (x >> 4) | x;
|
nuclear@0
|
232 x = (x >> 8) | x;
|
nuclear@0
|
233 x = (x >> 16) | x;
|
nuclear@0
|
234 return x + 1;
|
nuclear@0
|
235 }
|
nuclear@0
|
236
|
nuclear@0
|
237
|
nuclear@0
|
238 #if 0
|
nuclear@0
|
239 Color trace_ray(const Scene *scn, const Ray &ray, int rdepth)
|
nuclear@0
|
240 {
|
nuclear@0
|
241 HitPoint hit;
|
nuclear@0
|
242
|
nuclear@0
|
243 if(scn->intersect(ray, &hit)) {
|
nuclear@0
|
244 float t;
|
nuclear@0
|
245 if(scn->fog_start >= 0.0 && (t = (hit.dist - scn->fog_start) / (scn->fog_end - scn->fog_start)) > 0.0) {
|
nuclear@0
|
246 return lerp(shade(scn, ray, hit, rdepth), scn->env_color(ray), t > 1.0 ? 1.0 : t);
|
nuclear@0
|
247 }
|
nuclear@0
|
248 return shade(scn, ray, hit, rdepth);
|
nuclear@0
|
249 }
|
nuclear@0
|
250
|
nuclear@0
|
251 return scn->env_color(ray);
|
nuclear@0
|
252 }
|
nuclear@0
|
253
|
nuclear@0
|
254 Color shade(const Scene *scn, const Ray &ray, const HitPoint &hit, int rdepth)
|
nuclear@0
|
255 {
|
nuclear@0
|
256 const Material *mat = &hit.obj->material;
|
nuclear@0
|
257
|
nuclear@0
|
258 // if we're leaving the object, we need to invert the normal (and ior)
|
nuclear@0
|
259 Vector3 normal;
|
nuclear@0
|
260 bool entering;
|
nuclear@0
|
261 if(dot_product(hit.normal, ray.dir) <= 0.0) {
|
nuclear@0
|
262 normal = hit.normal;
|
nuclear@0
|
263 entering = true;
|
nuclear@0
|
264 } else {
|
nuclear@0
|
265 normal = -hit.normal;
|
nuclear@0
|
266 entering = false;
|
nuclear@0
|
267 }
|
nuclear@0
|
268
|
nuclear@0
|
269 Vector3 vdir = -ray.dir;
|
nuclear@0
|
270
|
nuclear@0
|
271 Color diffuse_color = mat->diffuse;
|
nuclear@0
|
272 Color tex_color{1, 1, 1};
|
nuclear@0
|
273 if(mat->tex) {
|
nuclear@0
|
274 tex_color *= mat->tex->sample(hit);
|
nuclear@0
|
275 diffuse_color *= tex_color;
|
nuclear@0
|
276 }
|
nuclear@0
|
277
|
nuclear@0
|
278 Color color = mat->emission * tex_color;
|
nuclear@0
|
279
|
nuclear@0
|
280 // image-based lighting
|
nuclear@0
|
281 if(scn->envmap_conv) {
|
nuclear@0
|
282 // pick a random direction and create a sampling ray
|
nuclear@0
|
283 Ray envray;
|
nuclear@0
|
284 envray.origin = hit.pos;
|
nuclear@0
|
285 rand_dir(&envray.dir.x, &envray.dir.y, &envray.dir.z, (unsigned int*)ray.user);
|
nuclear@0
|
286 if(dot_product(envray.dir, normal) < 0.0) {
|
nuclear@0
|
287 envray.dir = -envray.dir;
|
nuclear@0
|
288 }
|
nuclear@0
|
289
|
nuclear@0
|
290 HitPoint env_hit;
|
nuclear@0
|
291 if(!scn->intersect(envray, &env_hit)) {
|
nuclear@0
|
292 Vector3 dir = envray.dir;
|
nuclear@0
|
293 color += scn->envmap_conv->sample(dir.x, dir.y, dir.z) * diffuse_color;
|
nuclear@0
|
294 }
|
nuclear@0
|
295 }
|
nuclear@0
|
296
|
nuclear@0
|
297 for(Light *lt: scn->lights) {
|
nuclear@0
|
298
|
nuclear@0
|
299 /* construct a shadow ray to determine if there is an uninterrupted
|
nuclear@0
|
300 * path between the intersection point and the light source
|
nuclear@0
|
301 */
|
nuclear@0
|
302 Ray shadow_ray = ray;
|
nuclear@0
|
303 shadow_ray.origin = hit.pos;
|
nuclear@0
|
304 shadow_ray.dir = lt->pos - hit.pos;
|
nuclear@0
|
305
|
nuclear@0
|
306 /* the interval [0, 1] represents the part of the ray from the origin
|
nuclear@0
|
307 * to the light. We don't care about intersections behind the origin
|
nuclear@0
|
308 * of the shadow ray (behind the surface of the object), or after the
|
nuclear@0
|
309 * light source. We only care if there's something in between hiding the
|
nuclear@0
|
310 * light.
|
nuclear@0
|
311 */
|
nuclear@0
|
312 HitPoint shadow_hit;
|
nuclear@0
|
313 if(scn->intersect(shadow_ray, &shadow_hit) && shadow_hit.dist < 1.0f) {
|
nuclear@0
|
314 continue; // skip this light, it's hidden from view
|
nuclear@0
|
315 }
|
nuclear@0
|
316
|
nuclear@0
|
317 // calculate the light direction
|
nuclear@0
|
318 Vector3 ldir = shadow_ray.dir.normalized();
|
nuclear@0
|
319 // calculate the reflected light direction
|
nuclear@0
|
320 Vector3 lref = ldir.reflection(normal);
|
nuclear@0
|
321
|
nuclear@0
|
322 float diffuse = std::max(dot_product(ldir, normal), 0.0f);
|
nuclear@0
|
323 float specular = pow(std::max(dot_product(lref, vdir), 0.0f), mat->shininess);
|
nuclear@0
|
324
|
nuclear@0
|
325 color += (diffuse_color * diffuse + mat->specular * specular) * lt->color;
|
nuclear@0
|
326 }
|
nuclear@0
|
327
|
nuclear@0
|
328 Color spec_col;
|
nuclear@0
|
329
|
nuclear@0
|
330 if(mat->reflectivity > 0.001f && rdepth < MAX_RAY_DEPTH) {
|
nuclear@0
|
331 Ray refl_ray{ray};
|
nuclear@0
|
332 refl_ray.origin = hit.pos;
|
nuclear@0
|
333 refl_ray.dir = -ray.dir.reflection(normal);
|
nuclear@0
|
334
|
nuclear@0
|
335 spec_col += trace_ray(scn, refl_ray, rdepth + 1) * mat->reflectivity;
|
nuclear@0
|
336 }
|
nuclear@0
|
337
|
nuclear@0
|
338 if(mat->transparency > 0.001f && rdepth < MAX_RAY_DEPTH) {
|
nuclear@0
|
339 float from_ior = entering ? 1.0 : mat->ior;
|
nuclear@0
|
340 float to_ior = entering ? mat->ior : 1.0;
|
nuclear@0
|
341
|
nuclear@0
|
342 Ray refr_ray{ray};
|
nuclear@0
|
343 refr_ray.origin = hit.pos;
|
nuclear@0
|
344 refr_ray.dir = ray.dir.refraction(normal, from_ior / to_ior);
|
nuclear@0
|
345
|
nuclear@0
|
346 Color tcol = trace_ray(scn, refr_ray, rdepth + 1) * mat->transparency;
|
nuclear@0
|
347
|
nuclear@0
|
348 float fres = fresnel(ray.dir, refr_ray.dir, normal, from_ior, to_ior);
|
nuclear@0
|
349 spec_col = spec_col * fres + tcol * (1.0 - fres);
|
nuclear@0
|
350 }
|
nuclear@0
|
351
|
nuclear@0
|
352 return color + spec_col;
|
nuclear@0
|
353 }
|
nuclear@0
|
354
|
nuclear@0
|
355
|
nuclear@0
|
356 static void rand_dir(float *x, float *y, float *z, unsigned int *seedp)
|
nuclear@0
|
357 {
|
nuclear@0
|
358 float u = (float)rand_r(seedp) / RAND_MAX;
|
nuclear@0
|
359 float v = (float)rand_r(seedp) / RAND_MAX;
|
nuclear@0
|
360
|
nuclear@0
|
361 float theta = 2.0 * M_PI * u;
|
nuclear@0
|
362 float phi = acos(2.0 * v - 1.0);
|
nuclear@0
|
363
|
nuclear@0
|
364 *x = cos(theta) * sin(phi);
|
nuclear@0
|
365 *y = sin(theta) * sin(phi);
|
nuclear@0
|
366 *z = cos(phi);
|
nuclear@0
|
367 }
|
nuclear@0
|
368
|
nuclear@0
|
369 static float fresnel(const Vector3 &inc, const Vector3 &trans, const Vector3 &norm, float ior_inc, float ior_trans)
|
nuclear@0
|
370 {
|
nuclear@0
|
371 float cos_inc = dot_product(-inc, norm);
|
nuclear@0
|
372 float cos_trans = dot_product(-trans, norm);
|
nuclear@0
|
373
|
nuclear@0
|
374 return fresnel(cos_inc, cos_trans, ior_inc, ior_trans);
|
nuclear@0
|
375 }
|
nuclear@0
|
376
|
nuclear@0
|
377 static float fresnel(float cos_inc, float cos_trans, float ior_inc, float ior_trans)
|
nuclear@0
|
378 {
|
nuclear@0
|
379 float r0 = ((ior_trans * cos_inc) - (ior_inc * cos_trans)) /
|
nuclear@0
|
380 ((ior_trans * cos_inc) + (ior_inc * cos_trans));
|
nuclear@0
|
381 float r1 = ((ior_inc * cos_inc) - (ior_trans * cos_trans)) /
|
nuclear@0
|
382 ((ior_inc * cos_inc) + (ior_trans * cos_trans));
|
nuclear@0
|
383 return (r0 * r0 + r1 * r1) * 0.5f;
|
nuclear@0
|
384 }
|
nuclear@0
|
385 #endif
|