diff options
Diffstat (limited to 'src/photon.h')
-rw-r--r-- | src/photon.h | 483 |
1 files changed, 248 insertions, 235 deletions
diff --git a/src/photon.h b/src/photon.h index e781864..8b7e588 100644 --- a/src/photon.h +++ b/src/photon.h @@ -3,309 +3,322 @@ #include "stdio.h" #include "linalg.h" -#include "materials.h" #include "rotate.h" #include "random.h" #include "physical_constants.h" #include "mesh.h" +#include "geometry.h" struct Photon { - float3 position; - float3 direction; - float3 polarization; - float wavelength; - float time; + float3 position; + float3 direction; + float3 polarization; + float wavelength; + float time; - unsigned int history; + unsigned int history; - int last_hit_triangle; + int last_hit_triangle; }; struct State { - bool inside_to_outside; + bool inside_to_outside; - float3 surface_normal; + float3 surface_normal; - float refractive_index1, refractive_index2; - float absorption_length; - float scattering_length; + float refractive_index1, refractive_index2; + float absorption_length; + float scattering_length; - int surface_index; + int surface_index; - float distance_to_boundary; + float distance_to_boundary; }; enum { - NO_HIT = 0x1 << 0, - BULK_ABSORB = 0x1 << 1, - SURFACE_DETECT = 0x1 << 2, - SURFACE_ABSORB = 0x1 << 3, - RAYLEIGH_SCATTER = 0x1 << 4, - REFLECT_DIFFUSE = 0x1 << 5, - REFLECT_SPECULAR = 0x1 << 6, - NAN_ABORT = 0x1 << 31 + NO_HIT = 0x1 << 0, + BULK_ABSORB = 0x1 << 1, + SURFACE_DETECT = 0x1 << 2, + SURFACE_ABSORB = 0x1 << 3, + RAYLEIGH_SCATTER = 0x1 << 4, + REFLECT_DIFFUSE = 0x1 << 5, + REFLECT_SPECULAR = 0x1 << 6, + NAN_ABORT = 0x1 << 31 }; // processes enum {BREAK, CONTINUE, PASS}; // return value from propagate_to_boundary -__device__ float get_theta(const float3 &a, const float3 &b) +__device__ int +convert(int c) { - return acosf(fmaxf(-1.0f,fminf(1.0f,dot(a,b)))); + if (c & 0x80) + return (0xFFFFFF00 | c); + else + return c; } -__device__ void fill_state(State &s, Photon &p) +__device__ float +get_theta(const float3 &a, const float3 &b) { - p.last_hit_triangle = intersect_mesh(p.position, p.direction, s.distance_to_boundary, p.last_hit_triangle); - - if (p.last_hit_triangle == -1) - { - p.history |= NO_HIT; - return; - } - - uint4 triangle_data = g_triangles[p.last_hit_triangle]; - - float3 v0 = g_vertices[triangle_data.x]; - float3 v1 = g_vertices[triangle_data.y]; - float3 v2 = g_vertices[triangle_data.z]; - - int inner_material_index = convert(0xFF & (triangle_data.w >> 24)); - int outer_material_index = convert(0xFF & (triangle_data.w >> 16)); - s.surface_index = convert(0xFF & (triangle_data.w >> 8)); - - s.surface_normal = cross(v1-v0, v2-v1); - s.surface_normal /= norm(s.surface_normal); - - Material material1, material2; - if (dot(s.surface_normal,-p.direction) > 0.0f) - { - // outside to inside - material1 = materials[outer_material_index]; - material2 = materials[inner_material_index]; - - s.inside_to_outside = false; - } - else - { - // inside to outside - material1 = materials[inner_material_index]; - material2 = materials[outer_material_index]; - s.surface_normal = -s.surface_normal; - - s.inside_to_outside = true; - } + return acosf(fmaxf(-1.0f,fminf(1.0f,dot(a,b)))); +} - s.refractive_index1 = interp_property(p.wavelength, material1.refractive_index); - s.refractive_index2 = interp_property(p.wavelength, material2.refractive_index); - s.absorption_length = interp_property(p.wavelength, material1.absorption_length); - s.scattering_length = interp_property(p.wavelength, material1.scattering_length); +__device__ void +fill_state(State &s, Photon &p, Geometry *g) +{ + p.last_hit_triangle = intersect_mesh(p.position, p.direction, g, + s.distance_to_boundary, + p.last_hit_triangle); + + if (p.last_hit_triangle == -1) { + p.history |= NO_HIT; + return; + } + + Triangle t = get_triangle(g, p.last_hit_triangle); + + unsigned int material_code = g->material_codes[p.last_hit_triangle]; + + int inner_material_index = convert(0xFF & (material_code >> 24)); + int outer_material_index = convert(0xFF & (material_code >> 16)); + s.surface_index = convert(0xFF & (material_code >> 8)); + + float3 v01 = t.v1 - t.v0; + float3 v12 = t.v2 - t.v1; + + s.surface_normal = normalize(cross(v01, v12)); + + Material *material1, *material2; + if (dot(s.surface_normal,-p.direction) > 0.0f) { + // outside to inside + material1 = g->materials[outer_material_index]; + material2 = g->materials[inner_material_index]; + + s.inside_to_outside = false; + } + else { + // inside to outside + material1 = g->materials[inner_material_index]; + material2 = g->materials[outer_material_index]; + s.surface_normal = -s.surface_normal; + + s.inside_to_outside = true; + } + + s.refractive_index1 = interp_property(material1, p.wavelength, + material1->refractive_index); + s.refractive_index2 = interp_property(material2, p.wavelength, + material2->refractive_index); + s.absorption_length = interp_property(material1, p.wavelength, + material1->absorption_length); + s.scattering_length = interp_property(material1, p.wavelength, + material1->scattering_length); } // fill_state -__device__ float3 pick_new_direction(float3 axis, float theta, float phi) +__device__ float3 +pick_new_direction(float3 axis, float theta, float phi) { - // Taken from SNOMAN rayscatter.for - float cos_theta = cosf(theta); - float sin_theta = sinf(theta); - float cos_phi = cosf(phi); - float sin_phi = sinf(phi); + // Taken from SNOMAN rayscatter.for + float cos_theta = cosf(theta); + float sin_theta = sinf(theta); + float cos_phi = cosf(phi); + float sin_phi = sinf(phi); - float sin_axis_theta = sqrt(1.0f - axis.z*axis.z); - float cos_axis_phi, sin_axis_phi; + float sin_axis_theta = sqrt(1.0f - axis.z*axis.z); + float cos_axis_phi, sin_axis_phi; - if (isnan(sin_axis_theta) || sin_axis_theta < 0.00001f) { - cos_axis_phi = 1.0f; - sin_axis_phi = 0.0f; - } else { - cos_axis_phi = axis.x / sin_axis_theta; - sin_axis_phi = axis.y / sin_axis_theta; - } - - return make_float3(cos_theta*axis.x + sin_theta*(axis.z*cos_phi*cos_axis_phi - sin_phi*sin_axis_phi), - cos_theta*axis.y + sin_theta*(cos_phi*axis.z*sin_axis_phi - sin_phi*cos_axis_phi), - cos_theta*axis.z - sin_theta*cos_phi*sin_axis_theta); + if (isnan(sin_axis_theta) || sin_axis_theta < 0.00001f) { + cos_axis_phi = 1.0f; + sin_axis_phi = 0.0f; + } + else { + cos_axis_phi = axis.x / sin_axis_theta; + sin_axis_phi = axis.y / sin_axis_theta; + } + + float dirx = cos_theta*axis.x + + sin_theta*(axis.z*cos_phi*cos_axis_phi - sin_phi*sin_axis_phi); + float diry = cos_theta*axis.y + + sin_theta*(cos_phi*axis.z*sin_axis_phi - sin_phi*cos_axis_phi); + float dirz = cos_theta*axis.z - sin_theta*cos_phi*sin_axis_theta; + + return make_float3(dirx, diry, dirz); } -__device__ void rayleigh_scatter(Photon &p, curandState &rng) +__device__ void +rayleigh_scatter(Photon &p, curandState &rng) { - float cos_theta = 2.0f*cosf((acosf(1.0f - 2.0f*curand_uniform(&rng))-2*PI)/3.0f); - if (cos_theta > 1.0f) - cos_theta = 1.0f; - else if (cos_theta < -1.0f) - cos_theta = -1.0f; - - float theta = acosf(cos_theta); - float phi = uniform(&rng, 0.0f, 2.0f * PI); - - p.direction = pick_new_direction(p.polarization, theta, phi); - - if (1.0f - fabsf(cos_theta) < 1e-6f) { - p.polarization = pick_new_direction(p.polarization, PI/2.0f, phi); - } else { - // linear combination of old polarization and new direction - p.polarization = p.polarization - cos_theta * p.direction; - } - - p.direction /= norm(p.direction); - p.polarization /= norm(p.polarization); + float cos_theta = 2.0f*cosf((acosf(1.0f - 2.0f*curand_uniform(&rng))-2*PI)/3.0f); + if (cos_theta > 1.0f) + cos_theta = 1.0f; + else if (cos_theta < -1.0f) + cos_theta = -1.0f; + + float theta = acosf(cos_theta); + float phi = uniform(&rng, 0.0f, 2.0f * PI); + + p.direction = pick_new_direction(p.polarization, theta, phi); + + if (1.0f - fabsf(cos_theta) < 1e-6f) { + p.polarization = pick_new_direction(p.polarization, PI/2.0f, phi); + } + else { + // linear combination of old polarization and new direction + p.polarization = p.polarization - cos_theta * p.direction; + } + + p.direction /= norm(p.direction); + p.polarization /= norm(p.polarization); } // scatter __device__ int propagate_to_boundary(Photon &p, State &s, curandState &rng) { - float absorption_distance = -s.absorption_length*logf(curand_uniform(&rng)); - float scattering_distance = -s.scattering_length*logf(curand_uniform(&rng)); + float absorption_distance = -s.absorption_length*logf(curand_uniform(&rng)); + float scattering_distance = -s.scattering_length*logf(curand_uniform(&rng)); - if (absorption_distance <= scattering_distance) - { - if (absorption_distance <= s.distance_to_boundary) - { - p.time += absorption_distance/(SPEED_OF_LIGHT/s.refractive_index1); - p.position += absorption_distance*p.direction; - p.history |= BULK_ABSORB; + if (absorption_distance <= scattering_distance) { + if (absorption_distance <= s.distance_to_boundary) { + p.time += absorption_distance/(SPEED_OF_LIGHT/s.refractive_index1); + p.position += absorption_distance*p.direction; + p.history |= BULK_ABSORB; - p.last_hit_triangle = -1; + p.last_hit_triangle = -1; - return BREAK; - } // photon is absorbed in material1 - } - else - { - if (scattering_distance <= s.distance_to_boundary) - { - p.time += scattering_distance/(SPEED_OF_LIGHT/s.refractive_index1); - p.position += scattering_distance*p.direction; + return BREAK; + } // photon is absorbed in material1 + } + else { + if (scattering_distance <= s.distance_to_boundary) { + p.time += scattering_distance/(SPEED_OF_LIGHT/s.refractive_index1); + p.position += scattering_distance*p.direction; - rayleigh_scatter(p, rng); + rayleigh_scatter(p, rng); - p.history |= RAYLEIGH_SCATTER; + p.history |= RAYLEIGH_SCATTER; - p.last_hit_triangle = -1; + p.last_hit_triangle = -1; - return CONTINUE; - } // photon is scattered in material1 - } // if scattering_distance < absorption_distance + return CONTINUE; + } // photon is scattered in material1 + } // if scattering_distance < absorption_distance - p.position += s.distance_to_boundary*p.direction; - p.time += s.distance_to_boundary/(SPEED_OF_LIGHT/s.refractive_index1); + p.position += s.distance_to_boundary*p.direction; + p.time += s.distance_to_boundary/(SPEED_OF_LIGHT/s.refractive_index1); - return PASS; + return PASS; } // propagate_to_boundary -__device__ void propagate_at_boundary(Photon &p, State &s, curandState &rng) +__device__ void +propagate_at_boundary(Photon &p, State &s, curandState &rng) { - float incident_angle = get_theta(s.surface_normal,-p.direction); - float refracted_angle = asinf(sinf(incident_angle)*s.refractive_index1/s.refractive_index2); - - float3 incident_plane_normal = cross(p.direction, s.surface_normal); - float incident_plane_normal_length = norm(incident_plane_normal); - - // Photons at normal incidence do not have a unique plane of incidence, - // so we have to pick the plane normal to be the polarization vector - // to get the correct logic below - if (incident_plane_normal_length < 1e-6f) - incident_plane_normal = p.polarization; - else - incident_plane_normal /= incident_plane_normal_length; - - float normal_coefficient = dot(p.polarization, incident_plane_normal); - float normal_probability = normal_coefficient*normal_coefficient; - - float reflection_coefficient; - if (curand_uniform(&rng) < normal_probability) - { - reflection_coefficient = -sinf(incident_angle-refracted_angle)/sinf(incident_angle+refracted_angle); - - if ((curand_uniform(&rng) < reflection_coefficient*reflection_coefficient) || isnan(refracted_angle)) - { - p.direction = rotate(s.surface_normal, incident_angle, incident_plane_normal); + float incident_angle = get_theta(s.surface_normal,-p.direction); + float refracted_angle = asinf(sinf(incident_angle)*s.refractive_index1/s.refractive_index2); + + float3 incident_plane_normal = cross(p.direction, s.surface_normal); + float incident_plane_normal_length = norm(incident_plane_normal); + + // Photons at normal incidence do not have a unique plane of incidence, + // so we have to pick the plane normal to be the polarization vector + // to get the correct logic below + if (incident_plane_normal_length < 1e-6f) + incident_plane_normal = p.polarization; + else + incident_plane_normal /= incident_plane_normal_length; + + float normal_coefficient = dot(p.polarization, incident_plane_normal); + float normal_probability = normal_coefficient*normal_coefficient; + + float reflection_coefficient; + if (curand_uniform(&rng) < normal_probability) { + // photon polarization normal to plane of incidence + reflection_coefficient = -sinf(incident_angle-refracted_angle)/sinf(incident_angle+refracted_angle); + + if ((curand_uniform(&rng) < reflection_coefficient*reflection_coefficient) || isnan(refracted_angle)) { + p.direction = rotate(s.surface_normal, incident_angle, incident_plane_normal); - p.history |= REFLECT_SPECULAR; - } - else - { - p.direction = rotate(s.surface_normal, PI-refracted_angle, incident_plane_normal); - } - - p.polarization = incident_plane_normal; - } // photon polarization normal to plane of incidence - else - { - reflection_coefficient = tanf(incident_angle-refracted_angle)/tanf(incident_angle+refracted_angle); - - if ((curand_uniform(&rng) < reflection_coefficient*reflection_coefficient) || isnan(refracted_angle)) - { - p.direction = rotate(s.surface_normal, incident_angle, incident_plane_normal); - - p.history |= REFLECT_SPECULAR; - } - else - { - p.direction = rotate(s.surface_normal, PI-refracted_angle, incident_plane_normal); - } - - p.polarization = cross(incident_plane_normal, p.direction); - p.polarization /= norm(p.polarization); - } // photon polarization parallel to plane of incidence - -} // propagate_at_boundary - -__device__ int propagate_at_surface(Photon &p, State &s, curandState &rng) -{ - Surface surface = surfaces[s.surface_index]; - - float detect = interp_property(p.wavelength, surface.detect); - float absorb = interp_property(p.wavelength, surface.absorb); - float reflect_diffuse = interp_property(p.wavelength, surface.reflect_diffuse); - float reflect_specular = interp_property(p.wavelength, surface.reflect_specular); - - // since the surface properties are interpolated linearly, we are - // guaranteed that they still sum to 1.0. + p.history |= REFLECT_SPECULAR; + } + else { + p.direction = rotate(s.surface_normal, PI-refracted_angle, incident_plane_normal); + } - float uniform_sample = curand_uniform(&rng); + p.polarization = incident_plane_normal; + } + else { + // photon polarization parallel to plane of incidence + reflection_coefficient = tanf(incident_angle-refracted_angle)/tanf(incident_angle+refracted_angle); - if (uniform_sample < absorb) - { - p.history |= SURFACE_ABSORB; - return BREAK; + if ((curand_uniform(&rng) < reflection_coefficient*reflection_coefficient) || isnan(refracted_angle)) { + p.direction = rotate(s.surface_normal, incident_angle, incident_plane_normal); + + p.history |= REFLECT_SPECULAR; } - else if (uniform_sample < absorb + detect) - { - p.history |= SURFACE_DETECT; - return BREAK; + else { + p.direction = rotate(s.surface_normal, PI-refracted_angle, incident_plane_normal); } - else if (uniform_sample < absorb + detect + reflect_diffuse) - { - // diffusely reflect - p.direction = uniform_sphere(&rng); - if (dot(p.direction, s.surface_normal) < 0.0f) - p.direction = -p.direction; + p.polarization = cross(incident_plane_normal, p.direction); + p.polarization /= norm(p.polarization); + } - // randomize polarization? - p.polarization = cross(uniform_sphere(&rng), p.direction); - p.polarization /= norm(p.polarization); +} // propagate_at_boundary + +__device__ int +propagate_at_surface(Photon &p, State &s, curandState &rng, Geometry *geometry) +{ + Surface *surface = geometry->surfaces[s.surface_index]; + + /* since the surface properties are interpolated linearly, we are + guaranteed that they still sum to 1.0. */ + + float detect = interp_property(surface, p.wavelength, surface->detect); + float absorb = interp_property(surface, p.wavelength, surface->absorb); + float reflect_diffuse = interp_property(surface, p.wavelength, surface->reflect_diffuse); + float reflect_specular = interp_property(surface, p.wavelength, surface->reflect_specular); + + float uniform_sample = curand_uniform(&rng); + + if (uniform_sample < absorb) { + p.history |= SURFACE_ABSORB; + return BREAK; + } + else if (uniform_sample < absorb + detect) { + p.history |= SURFACE_DETECT; + return BREAK; + } + else if (uniform_sample < absorb + detect + reflect_diffuse) { + // diffusely reflect + p.direction = uniform_sphere(&rng); + + if (dot(p.direction, s.surface_normal) < 0.0f) + p.direction = -p.direction; + + // randomize polarization? + p.polarization = cross(uniform_sphere(&rng), p.direction); + p.polarization /= norm(p.polarization); - p.history |= REFLECT_DIFFUSE; + p.history |= REFLECT_DIFFUSE; - return CONTINUE; - } - else - { - // specularly reflect - float incident_angle = get_theta(s.surface_normal,-p.direction); - float3 incident_plane_normal = cross(p.direction, s.surface_normal); - incident_plane_normal /= norm(incident_plane_normal); + return CONTINUE; + } + else { + // specularly reflect + float incident_angle = get_theta(s.surface_normal,-p.direction); + float3 incident_plane_normal = cross(p.direction, s.surface_normal); + incident_plane_normal /= norm(incident_plane_normal); - p.direction = rotate(s.surface_normal, incident_angle, incident_plane_normal); + p.direction = rotate(s.surface_normal, incident_angle, + incident_plane_normal); - p.history |= REFLECT_SPECULAR; + p.history |= REFLECT_SPECULAR; - return CONTINUE; - } + return CONTINUE; + } } // propagate_at_surface |