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