#include "Brouillard.h" #include "ObjetsCourbes.h" #include Objet::extension_t Objet_Brouillard::objet_extension () const { vec_t v = { lx*reso_x/2, ly*reso_y/2 }; return { .pos = o + v, .rayon = std::max(v.x, v.y) }; } // Test d'interception avec le brouillard : parcours du rayon à travers les cellules // et décision si oui ou non on diffuserait le rayon // Objet::intercept_t Objet_Brouillard::essai_intercept (const Rayon& ray) const { if (ray.spectre.intensite_tot() < intens_cutoff) return { .dist2 = Inf, .intercept_struct = nullptr }; // on test si le rayon passe sur le rectange bornant le brouillard std::vector isects; auto test_isect = [&] (point_t a, point_t b) { auto isect = ObjetLigne::intersection_segment_demidroite(a, b, ray.orig, ray.dir_angle); if (isect.has_value()) isects.push_back(isect.value()); }; point_t o2 = o + vec_t{lx*reso_x,0}, o3 = o + vec_t{lx*reso_x,ly*reso_y}, o4 = o + vec_t{0,ly*reso_y}; test_isect(o , o2); test_isect(o2, o3); test_isect(o3, o4); test_isect(o4, o ); // assert(isects.size() <= 2); // le rayon ne peut avoir que 1 (si source interne) ou 2 intersections (ou zéro) avec les bords if (isects.size() != 0) { float s; float s_fin = isects[0].t_dd; if (isects.size() == 1) { // rayon émis à l'intérieur du brouillard s = 0; } else { // rayon provenant de l'expérieur du brouillard s = isects[1].t_dd; if (s > s_fin) std::swap(s, s_fin); } float ds = std::min(reso_x,reso_y) / 2; vec_t u_ray = isects[0].u_dd; // le rayon parcourt le brouillard jusqu'à s_fin par incréments de ds // avec une probabilité donnée par `densit_brouillard` d'être diffusé à chaque pas, pour la méthode 1 // et avec une probabilité de `diffus_partielle_syst_proba` intercept_brouillard_t p; float proba_acc = 0; while (s < s_fin) { p.p_diff = ray.orig + s * u_ray; vec_t v = p.p_diff - this->o; p.x_diff = floorf( v.x / reso_x ); p.y_diff = floorf( v.y / reso_y ); if (0 <= p.x_diff and p.x_diff < (int)this->lx and 0 <= p.y_diff and p.y_diff < (int)this->ly) { float proba = ds / diffus_partielle_syst_libreparcours; if (proba > 1e-5) { // méthode par diffusion systématique partielle proba_acc += proba; p.fraction_transmis = std::max(0, 1 - ds/proba * this->densit_brouillard(p.x_diff, p.y_diff)); } else { // méthode par diffusion probabiliste complète proba = ds * this->densit_brouillard(p.x_diff, p.y_diff); proba_acc += proba; p.fraction_transmis = 0; } bool diffuse = parcours_deterministe ? (proba_acc >= 1) : (rand01() < proba); if (diffuse) { // rayon diffusé s += /*rand01() */ ds; p.p_diff = ray.orig + s * u_ray; return { .dist2 = s*s, .intercept_struct = std::make_shared(std::move(p)) }; } } s += ds; } } return { .dist2 = Inf, .intercept_struct = nullptr }; }; // Ré-émission du rayon intercepté par le brouillard // std::vector Objet_Brouillard::re_emit (const Rayon& ray, std::shared_ptr interception) { const intercept_brouillard_t& intercept = *(intercept_brouillard_t*)interception.get(); std::vector rayons; // Nombre de rayons secondaires size_t n_re_emit = std::max(1, lroundf( n_re_emit_par_intens * ray.spectre.intensite_tot() * (1-intercept.fraction_transmis)) ); float fact_I_re_emit = 1./n_re_emit; // On laisse vivre le rayon incident seulement si il a une intensité > 0.01 if (intercept.fraction_transmis > 0.01) { Rayon ray_trsm = ray; ray_trsm.spectre.for_each([&] (float lambda, pola_t, float& I) { I *= intercept.fraction_transmis; }); ray_trsm.orig = intercept.p_diff; rayons.push_back(std::move(ray_trsm)); fact_I_re_emit *= 1 - intercept.fraction_transmis; } // Émission des rayons secondaires for (size_t k = 0; k < n_re_emit; k++) { float ang_diff = 2*M_PI * rand01(); Rayon ray_diff; ray_diff.orig = intercept.p_diff; ray_diff.dir_angle = ray.dir_angle + ang_diff; ray_diff.spectre = ray.spectre; ray_diff.spectre.for_each([&] (float lambda, pola_t, float& I) { I *= this->directivite_diffus(ang_diff, lambda) * fact_I_re_emit; }); rayons.push_back(std::move(ray_diff)); } return rayons; } #include "sfml_c01.hpp" void Objet_Brouillard::dessiner (sf::RenderWindow& window, bool emphasize) const { for (size_t x = 0; x < this->lx; x++) { for (size_t y = 0; y < this->ly; y++) { sf::RectangleShape dens; sf::c01::setRectShape(dens, o + vec_t{reso_x*x, reso_y*y}, {reso_x, reso_y}); dens.setFillColor(sf::Color(255,255,255, std::min(255.f, this->densit_brouillard(x,y)) )); window.draw(dens); } } } std::optional Objet_Brouillard::point_interception (std::shared_ptr interception) const { if (interception) { return ((intercept_brouillard_t*)interception.get())->p_diff; } else return std::nullopt; }