gpuray_glsl

annotate src/rend.cc @ 4:2ed3da7dc0bc

broken
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 10 Nov 2014 01:26:00 +0200
parents f234630e38ff
children
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