gpuray_glsl
diff src/rend.cc @ 0:f234630e38ff
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 09 Nov 2014 13:03:36 +0200 |
parents | |
children | 2ed3da7dc0bc |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/rend.cc Sun Nov 09 13:03:36 2014 +0200 1.3 @@ -0,0 +1,372 @@ 1.4 +#include <assert.h> 1.5 +#include "scene.h" 1.6 +#include "image.h" 1.7 +#include "rend.h" 1.8 +#include "opengl.h" 1.9 +#include "glsdr.h" 1.10 + 1.11 +enum { 1.12 + TEX_RAYDIR, 1.13 + TEX_SPHERES, 1.14 + TEX_PLANES, 1.15 + TEX_BOXES, 1.16 + TEX_TEXTURES, 1.17 + TEX_ENV, 1.18 + TEX_XFORM, 1.19 + 1.20 + NUM_SDR_TEXTURES 1.21 +}; 1.22 + 1.23 +bool reload_shader(); 1.24 +void gen_ray_texture(unsigned int tex, int xsz, int ysz, float vfov); 1.25 +static Vector3 get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg); 1.26 +static int round_pow2(int x); 1.27 + 1.28 +static GPUScene *scn; 1.29 + 1.30 +static unsigned int sdr; 1.31 +static unsigned int textures[NUM_SDR_TEXTURES]; 1.32 + 1.33 +bool init_renderer(GPUScene *s, int xsz, int ysz) 1.34 +{ 1.35 + scn = s; 1.36 + 1.37 + glGenTextures(1, textures + TEX_RAYDIR); 1.38 + glBindTexture(GL_TEXTURE_2D, textures[TEX_RAYDIR]); 1.39 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 1.40 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 1.41 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 1.42 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 1.43 + 1.44 + if(!(s->create_textures())) { 1.45 + fprintf(stderr, "failed to create scene textures\n"); 1.46 + return false; 1.47 + } 1.48 + 1.49 + textures[TEX_SPHERES] = s->get_texture(GPUScene::TEX_SPHERE); 1.50 + textures[TEX_PLANES] = s->get_texture(GPUScene::TEX_PLANE); 1.51 + textures[TEX_BOXES] = s->get_texture(GPUScene::TEX_BOX); 1.52 + textures[TEX_TEXTURES] = s->get_texture(GPUScene::TEX_TEXTURE); 1.53 + textures[TEX_ENV] = s->get_texture(GPUScene::TEX_ENV); 1.54 + textures[TEX_XFORM] = s->get_texture(GPUScene::TEX_XFORM); 1.55 + 1.56 + if(!reload_shader()) { 1.57 + return false; 1.58 + } 1.59 + 1.60 + resize_renderer(xsz, ysz); 1.61 + 1.62 + return true; 1.63 +} 1.64 + 1.65 +bool reload_shader() 1.66 +{ 1.67 + if(sdr) { 1.68 + free_program(sdr); 1.69 + } 1.70 + 1.71 + printf("loading shader...\n"); 1.72 + 1.73 + if(!(sdr = create_program_load("sdr/vertex.glsl", "sdr/rt.glsl"))) { 1.74 + return false; 1.75 + } 1.76 + set_uniform_int(sdr, "tex_raydir", TEX_RAYDIR); 1.77 + set_uniform_int(sdr, "tex_spheres", TEX_SPHERES); 1.78 + set_uniform_int(sdr, "tex_planes", TEX_PLANES); 1.79 + set_uniform_int(sdr, "tex_boxes", TEX_BOXES); 1.80 + set_uniform_int(sdr, "tex_megatex", TEX_TEXTURES); 1.81 + set_uniform_int(sdr, "tex_env", TEX_ENV); 1.82 + set_uniform_int(sdr, "tex_xforms", TEX_XFORM); 1.83 + 1.84 + set_uniform_int(sdr, "num_lights", scn->get_light_count()); 1.85 + 1.86 + for(int i=0; i<scn->get_light_count(); i++) { 1.87 + const Light *lt = scn->get_light(i); 1.88 + 1.89 + char name[64]; 1.90 + sprintf(name, "lights[%d].pos", i); 1.91 + set_uniform_float3(sdr, name, lt->pos.x, lt->pos.y, lt->pos.z); 1.92 + 1.93 + sprintf(name, "lights[%d].color", i); 1.94 + set_uniform_float3(sdr, name, lt->color.x, lt->color.y, lt->color.z); 1.95 + } 1.96 + 1.97 + Vector2 fog; 1.98 + scn->get_fog(&fog.x, &fog.y); 1.99 + if(fog.x < 0.0 && fog.y < 0.0) { 1.100 + fog.x = 0.0; 1.101 + fog.y = 100000.0; 1.102 + } 1.103 + set_uniform_float2(sdr, "fog", fog.x, fog.y); 1.104 + return true; 1.105 +} 1.106 + 1.107 +void destroy_renderer() 1.108 +{ 1.109 + free_program(sdr); 1.110 + sdr = 0; 1.111 +} 1.112 + 1.113 +void resize_renderer(int xsz, int ysz) 1.114 +{ 1.115 + gen_ray_texture(textures[TEX_RAYDIR], xsz, ysz, 45.0f); 1.116 +} 1.117 + 1.118 +float *render_frame(long msec) 1.119 +{ 1.120 + scn->prepare_xform(msec); 1.121 + scn->update_xform_texture(); 1.122 + 1.123 + Camera *cam = scn->get_camera(); 1.124 + glMatrixMode(GL_MODELVIEW); 1.125 + glLoadIdentity(); 1.126 + Vector3 cpos = cam->get_position(); 1.127 + glTranslatef(cpos.x, cpos.y, cpos.z); 1.128 + Matrix4x4 cmat = cam->get_matrix(); 1.129 + glMultTransposeMatrixf(cmat[0]); 1.130 + 1.131 + for(int i=0; i<NUM_SDR_TEXTURES; i++) { 1.132 + glActiveTexture(GL_TEXTURE0 + i); 1.133 + if(i == TEX_ENV) { 1.134 + glBindTexture(GL_TEXTURE_CUBE_MAP, textures[i]); 1.135 + } else { 1.136 + glBindTexture(GL_TEXTURE_2D, textures[i]); 1.137 + } 1.138 + } 1.139 + glActiveTexture(GL_TEXTURE0); 1.140 + 1.141 + glUseProgram(sdr); 1.142 + 1.143 + glBegin(GL_QUADS); 1.144 + glTexCoord2f(0, 1); 1.145 + glVertex2f(-1, -1); 1.146 + glTexCoord2f(1, 1); 1.147 + glVertex2f(1, -1); 1.148 + glTexCoord2f(1, 0); 1.149 + glVertex2f(1, 1); 1.150 + glTexCoord2f(0, 0); 1.151 + glVertex2f(-1, 1); 1.152 + glEnd(); 1.153 + 1.154 + glUseProgram(0); 1.155 + 1.156 + assert(glGetError() == GL_NO_ERROR); 1.157 + return 0; 1.158 +} 1.159 + 1.160 +void gen_ray_texture(unsigned int tex, int xsz, int ysz, float vfov) 1.161 +{ 1.162 + int tex_xsz = round_pow2(xsz); 1.163 + int tex_ysz = round_pow2(ysz); 1.164 + float *teximg, *dir; 1.165 + 1.166 + teximg = new float[3 * tex_xsz * tex_ysz]; 1.167 + dir = teximg; 1.168 + 1.169 + for(int i=0; i<tex_ysz; i++) { 1.170 + for(int j=0; j<tex_xsz; j++) { 1.171 + if(j < xsz && i < ysz) { 1.172 + Vector3 rdir = get_primary_ray_dir(j, i, xsz, ysz, vfov); 1.173 + dir[0] = rdir.x; 1.174 + dir[1] = rdir.y; 1.175 + dir[2] = rdir.z; 1.176 + } else { 1.177 + dir[0] = dir[1] = 0.0f; 1.178 + dir[2] = 1.0f; 1.179 + } 1.180 + 1.181 + dir += 3; 1.182 + } 1.183 + } 1.184 + 1.185 + glBindTexture(GL_TEXTURE_2D, tex); 1.186 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, tex_xsz, tex_ysz, 0, GL_RGB, GL_FLOAT, teximg); 1.187 + delete [] teximg; 1.188 +} 1.189 + 1.190 +static Vector3 calc_sample_pos(int x, int y, int xsz, int ysz) 1.191 +{ 1.192 + float ppos[2]; 1.193 + float aspect = (float)xsz / (float)ysz; 1.194 + 1.195 + float pwidth = 2.0 * aspect / (float)xsz; 1.196 + float pheight = 2.0 / (float)ysz; 1.197 + 1.198 + ppos[0] = (float)x * pwidth - aspect; 1.199 + ppos[1] = 1.0 - (float)y * pheight; 1.200 + 1.201 + return Vector3(ppos[0], ppos[1], 0.0f); 1.202 +} 1.203 + 1.204 + 1.205 +static Vector3 get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg) 1.206 +{ 1.207 + float vfov = M_PI * vfov_deg / 180.0; 1.208 + 1.209 + Vector3 dir = calc_sample_pos(x, y, w, h); 1.210 + dir.z = 1.0 / tan(vfov / 2.0); 1.211 + dir.normalize(); 1.212 + 1.213 + return dir; 1.214 +} 1.215 + 1.216 +static int round_pow2(int x) 1.217 +{ 1.218 + x--; 1.219 + x = (x >> 1) | x; 1.220 + x = (x >> 2) | x; 1.221 + x = (x >> 4) | x; 1.222 + x = (x >> 8) | x; 1.223 + x = (x >> 16) | x; 1.224 + return x + 1; 1.225 +} 1.226 + 1.227 + 1.228 +#if 0 1.229 +Color trace_ray(const Scene *scn, const Ray &ray, int rdepth) 1.230 +{ 1.231 + HitPoint hit; 1.232 + 1.233 + if(scn->intersect(ray, &hit)) { 1.234 + float t; 1.235 + if(scn->fog_start >= 0.0 && (t = (hit.dist - scn->fog_start) / (scn->fog_end - scn->fog_start)) > 0.0) { 1.236 + return lerp(shade(scn, ray, hit, rdepth), scn->env_color(ray), t > 1.0 ? 1.0 : t); 1.237 + } 1.238 + return shade(scn, ray, hit, rdepth); 1.239 + } 1.240 + 1.241 + return scn->env_color(ray); 1.242 +} 1.243 + 1.244 +Color shade(const Scene *scn, const Ray &ray, const HitPoint &hit, int rdepth) 1.245 +{ 1.246 + const Material *mat = &hit.obj->material; 1.247 + 1.248 + // if we're leaving the object, we need to invert the normal (and ior) 1.249 + Vector3 normal; 1.250 + bool entering; 1.251 + if(dot_product(hit.normal, ray.dir) <= 0.0) { 1.252 + normal = hit.normal; 1.253 + entering = true; 1.254 + } else { 1.255 + normal = -hit.normal; 1.256 + entering = false; 1.257 + } 1.258 + 1.259 + Vector3 vdir = -ray.dir; 1.260 + 1.261 + Color diffuse_color = mat->diffuse; 1.262 + Color tex_color{1, 1, 1}; 1.263 + if(mat->tex) { 1.264 + tex_color *= mat->tex->sample(hit); 1.265 + diffuse_color *= tex_color; 1.266 + } 1.267 + 1.268 + Color color = mat->emission * tex_color; 1.269 + 1.270 + // image-based lighting 1.271 + if(scn->envmap_conv) { 1.272 + // pick a random direction and create a sampling ray 1.273 + Ray envray; 1.274 + envray.origin = hit.pos; 1.275 + rand_dir(&envray.dir.x, &envray.dir.y, &envray.dir.z, (unsigned int*)ray.user); 1.276 + if(dot_product(envray.dir, normal) < 0.0) { 1.277 + envray.dir = -envray.dir; 1.278 + } 1.279 + 1.280 + HitPoint env_hit; 1.281 + if(!scn->intersect(envray, &env_hit)) { 1.282 + Vector3 dir = envray.dir; 1.283 + color += scn->envmap_conv->sample(dir.x, dir.y, dir.z) * diffuse_color; 1.284 + } 1.285 + } 1.286 + 1.287 + for(Light *lt: scn->lights) { 1.288 + 1.289 + /* construct a shadow ray to determine if there is an uninterrupted 1.290 + * path between the intersection point and the light source 1.291 + */ 1.292 + Ray shadow_ray = ray; 1.293 + shadow_ray.origin = hit.pos; 1.294 + shadow_ray.dir = lt->pos - hit.pos; 1.295 + 1.296 + /* the interval [0, 1] represents the part of the ray from the origin 1.297 + * to the light. We don't care about intersections behind the origin 1.298 + * of the shadow ray (behind the surface of the object), or after the 1.299 + * light source. We only care if there's something in between hiding the 1.300 + * light. 1.301 + */ 1.302 + HitPoint shadow_hit; 1.303 + if(scn->intersect(shadow_ray, &shadow_hit) && shadow_hit.dist < 1.0f) { 1.304 + continue; // skip this light, it's hidden from view 1.305 + } 1.306 + 1.307 + // calculate the light direction 1.308 + Vector3 ldir = shadow_ray.dir.normalized(); 1.309 + // calculate the reflected light direction 1.310 + Vector3 lref = ldir.reflection(normal); 1.311 + 1.312 + float diffuse = std::max(dot_product(ldir, normal), 0.0f); 1.313 + float specular = pow(std::max(dot_product(lref, vdir), 0.0f), mat->shininess); 1.314 + 1.315 + color += (diffuse_color * diffuse + mat->specular * specular) * lt->color; 1.316 + } 1.317 + 1.318 + Color spec_col; 1.319 + 1.320 + if(mat->reflectivity > 0.001f && rdepth < MAX_RAY_DEPTH) { 1.321 + Ray refl_ray{ray}; 1.322 + refl_ray.origin = hit.pos; 1.323 + refl_ray.dir = -ray.dir.reflection(normal); 1.324 + 1.325 + spec_col += trace_ray(scn, refl_ray, rdepth + 1) * mat->reflectivity; 1.326 + } 1.327 + 1.328 + if(mat->transparency > 0.001f && rdepth < MAX_RAY_DEPTH) { 1.329 + float from_ior = entering ? 1.0 : mat->ior; 1.330 + float to_ior = entering ? mat->ior : 1.0; 1.331 + 1.332 + Ray refr_ray{ray}; 1.333 + refr_ray.origin = hit.pos; 1.334 + refr_ray.dir = ray.dir.refraction(normal, from_ior / to_ior); 1.335 + 1.336 + Color tcol = trace_ray(scn, refr_ray, rdepth + 1) * mat->transparency; 1.337 + 1.338 + float fres = fresnel(ray.dir, refr_ray.dir, normal, from_ior, to_ior); 1.339 + spec_col = spec_col * fres + tcol * (1.0 - fres); 1.340 + } 1.341 + 1.342 + return color + spec_col; 1.343 +} 1.344 + 1.345 + 1.346 +static void rand_dir(float *x, float *y, float *z, unsigned int *seedp) 1.347 +{ 1.348 + float u = (float)rand_r(seedp) / RAND_MAX; 1.349 + float v = (float)rand_r(seedp) / RAND_MAX; 1.350 + 1.351 + float theta = 2.0 * M_PI * u; 1.352 + float phi = acos(2.0 * v - 1.0); 1.353 + 1.354 + *x = cos(theta) * sin(phi); 1.355 + *y = sin(theta) * sin(phi); 1.356 + *z = cos(phi); 1.357 +} 1.358 + 1.359 +static float fresnel(const Vector3 &inc, const Vector3 &trans, const Vector3 &norm, float ior_inc, float ior_trans) 1.360 +{ 1.361 + float cos_inc = dot_product(-inc, norm); 1.362 + float cos_trans = dot_product(-trans, norm); 1.363 + 1.364 + return fresnel(cos_inc, cos_trans, ior_inc, ior_trans); 1.365 +} 1.366 + 1.367 +static float fresnel(float cos_inc, float cos_trans, float ior_inc, float ior_trans) 1.368 +{ 1.369 + float r0 = ((ior_trans * cos_inc) - (ior_inc * cos_trans)) / 1.370 + ((ior_trans * cos_inc) + (ior_inc * cos_trans)); 1.371 + float r1 = ((ior_inc * cos_inc) - (ior_trans * cos_trans)) / 1.372 + ((ior_inc * cos_inc) + (ior_trans * cos_trans)); 1.373 + return (r0 * r0 + r1 * r1) * 0.5f; 1.374 +} 1.375 +#endif