BBox::IntDist BBox::intersect(const rt::Ray& ray) const { float t_entr, t_exit, t_y_min, t_y_max, t_z_min, t_z_max; const bool sign[3] = {ray.inv_d.x >= 0.0f, ray.inv_d.y >= 0.0f, ray.inv_d.z >= 0.0f}; t_entr = (m_bound_pts[1 - sign[0]].x - ray.o.x) * ray.inv_d.x; t_exit = (m_bound_pts[ sign[0]].x - ray.o.x) * ray.inv_d.x; t_y_min = (m_bound_pts[1 - sign[1]].y - ray.o.y) * ray.inv_d.y; t_y_max = (m_bound_pts[ sign[1]].y - ray.o.y) * ray.inv_d.y; if ((t_entr > t_y_max) || (t_y_min > t_exit)) { return BBox::IntDist{}; } t_entr = max(t_entr, t_y_min); t_exit = min(t_exit, t_y_max); t_z_min = (m_bound_pts[1 - sign[2]].z - ray.o.z) * ray.inv_d.z; t_z_max = (m_bound_pts[ sign[2]].z - ray.o.z) * ray.inv_d.z; if ((t_entr > t_z_max) || (t_z_min > t_exit)) { return BBox::IntDist{}; } t_entr = max(t_entr, t_z_min); t_exit = min(t_exit, t_z_max); if (t_entr < ray.t_max && t_exit > ray.t_min) { return BBox::IntDist{t_entr, t_exit}; } else { return BBox::IntDist{}; } }
void DensityField::computePiDensity(const PerspectiveCamera& cam, const Scene& scene) { printInfo("Performing fog density preintegration."); printInfo("This will take a few seconds. Please wait! :-)"); const auto& res = m_pi_dens_res = ivec3{cam.resolution(), m_res.z}; // Allocate storage m_pi_dens_data = new float[piDensSize()]; // Set up render loop assert(0 == res.x % PACKET_SZ && 0 == res.y % PACKET_SZ); const ivec2 n_packets{cam.resolution() / PACKET_SZ}; #pragma omp parallel for for (int p_j = 0; p_j < n_packets.y; ++p_j) for (int p_i = 0; p_i < n_packets.x; ++p_i) for (int p_y = 0; p_y < PACKET_SZ; ++p_y) for (int p_x = 0; p_x < PACKET_SZ; ++p_x) { const int x{p_x + p_i * PACKET_SZ}; const int y{p_y + p_j * PACKET_SZ}; // Use pixel center: offset by 0.5 rt::Ray ray{cam.getPrimaryRay(x + 0.5f, y + 0.5f)}; // Intersect the bounding volume of density field const auto is = m_bbox.intersect(ray); if (is) { // Determine distance to the geometry scene.trace(ray); // Compute parametric ray bounds const float t_min{max(is.entr, 0.0f)}; const float t_max{min(is.exit, ray.inters.distance)}; // Sample density at interval endpoints const int n_intervals{res.z * 4}; const float dt{(t_max - t_min) / n_intervals}; // Perform ray marching float prev_dens{sampleDensity(ray.o + t_min * ray.d)}; float dens{0.0f}; for (int i = 1; i <= n_intervals; ++i) { // Distance to the end of the interval const float t{t_min + i * dt}; const float curr_dens{sampleDensity(ray.o + t * ray.d)}; // Use trapezoidal rule for integration dens += 0.5f * (curr_dens + prev_dens); prev_dens = curr_dens; if (2 == i % 4) { // We are in the middle of the camera-space voxel (froxel) const int z{i / 4}; m_pi_dens_data[x + y * res.x + z * res.x * res.y] = dens * dt; } } } else { // Set density to zero along the ray for (int z = 0; z < res.z; ++z) { m_pi_dens_data[x + y * res.x + z * res.x * res.y] = 0.0f; } } } // Save it to disk writePiDens("Assets\\pi_df.3dt"); // Load data into OpenGL texture createPiDensTex(); }
inline bool rayAABBIntersection( const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& aabbMin, const glm::vec3& aabbMax, float& distance) { using glm::max; using glm::min; // r.dir is unit direction vector of ray glm::vec3 dirfrac = 1.0f / direction; // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner // r.org is origin of ray float t1 = (aabbMin.x - origin.x) * dirfrac.x; float t2 = (aabbMax.x - origin.x) * dirfrac.x; float t3 = (aabbMin.y - origin.y) * dirfrac.y; float t4 = (aabbMax.y - origin.y) * dirfrac.y; float t5 = (aabbMin.z - origin.z) * dirfrac.z; float t6 = (aabbMax.z - origin.z) * dirfrac.z; float tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6)); float tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6)); // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behind us if (tmax < 0) { distance = tmax; return false; } // if tmin > tmax, ray doesn't intersect AABB if (tmin > tmax) { distance = tmax; return false; } distance = tmin; return true; }
void GLElementBuffer::loadData(const size_t n_elems, const GLuint* const data, const GLuint offset) { m_data_vec.reserve(m_data_vec.size() + n_elems); for (auto i = 0; i < n_elems; ++i) { const GLuint idx{offset + data[i]}; m_min_idx = min(m_min_idx, idx); m_max_idx = max(m_max_idx, idx); m_data_vec.push_back(idx); } m_is_buffered = false; }
DensityField::DensityField(const BBox& bb, const int(&res)[3], const float freq, const float ampl, const PerspectiveCamera& cam, const Scene& scene): m_bbox{bb}, m_res{res[0], res[1], res[2]}, m_data{new GLubyte[res[0] * res[1] * res[2]]}, m_pi_dens_data{nullptr} { // Validate parameters assert(m_res.x > 0 && m_res.y > 0 && m_res.z > 0); assert(freq > 0.0f && 0.0f < ampl && ampl <= 1.0f); // Initialize maximal density float max_dens{0.0f}; // Compute normalization factors const float x_norm{1.0f / (m_res.x - 1)}; const float y_norm{1.0f / (m_res.y - 1)}; const float z_norm{1.0f / (m_res.z - 1)}; printInfo("The renderer has been started for the first time."); printInfo("Procedurally computing fog density values using simplex noise."); // Compute per-pixel noise values #pragma omp parallel for for (int z = 0; z < m_res[2]; ++z) for (int y = 0; y < m_res[1]; ++y) for (int x = 0; x < m_res[0]; ++x) { const vec3 norm_pos{x * x_norm, y * y_norm, z * z_norm}; float sum{0.0f}; float curr_freq{freq}; float curr_ampl{ampl}; // Compute value for each octave for (int oct = 0; oct < N_OCTAVES; ++oct) { // Value in range [-1, 1] float val{glm::simplex(curr_freq * norm_pos)}; // Now mapped to [0, 0.5] val = 0.25f * (val + 1.0f); // curr_ampl <= 1 // Therefore, sum is range [0, 1): at most 0.5 + 0.25 + 0.125 + ... sum += curr_ampl * val; // Double the frequency, half the amplitude curr_freq *= 2.0f; curr_ampl /= 2.0f; } const float dens_val{sum / N_OCTAVES}; const GLubyte byte_dens{static_cast<GLubyte>(255.0f * dens_val)}; max_dens = max(max_dens, dens_val); m_data[x + y * m_res.x + z * m_res.x * m_res.y] = byte_dens; } const float inv_max_dens{1.0f / max_dens}; // Initialize minimal, average, maximal densities float min_dens{FLT_MAX}, avg_dens{0.0f}; max_dens = 0.0f; // Linearly rescale the values for (int z = 0; z < m_res[2]; ++z) for (int y = 0; y < m_res[1]; ++y) for (int x = 0; x < m_res[0]; ++x) { const GLubyte old_byte_dens{m_data[x + y * m_res.x + z * m_res.x * m_res.y]}; const float new_dens{min(old_byte_dens * inv_max_dens / 255.0f, 1.0f)}; const GLubyte new_byte_dens{static_cast<GLubyte>(255.0f * new_dens)}; min_dens = min(min_dens, new_dens); max_dens = max(max_dens, new_dens); avg_dens += new_dens; m_data[x + y * m_res.x + z * m_res.x * m_res.y] = new_byte_dens; } #ifndef NDEBUG printInfo("Minimal density: %.2f", min_dens); printInfo("Maximal density: %.2f", max_dens); printInfo("Average density: %.2f", avg_dens / static_cast<float>(m_res.x * m_res.y * m_res.z)); #endif // Save it to disk write("Assets\\df.3dt"); // Load data into OpenGL texture createTex(); // Preintegrate density values along primary rays computePiDensity(cam, scene); }
void BBox::extend(const vec3& p) { for (auto axis = 0; axis < 3; ++axis) { m_bound_pts[0][axis] = min(m_bound_pts[0][axis], p[axis]); m_bound_pts[1][axis] = max(m_bound_pts[1][axis], p[axis]); } }