gpuray_glsl

annotate src/rend.cc @ 1:92695e89164b

vc project
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 09 Nov 2014 14:30:37 +0200
parents
children 2ed3da7dc0bc
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