gk::MeshIO::read( )
pour la
charger) et ne
conservez que l'intersection valide la plus proche de l'origine du
rayon.
gk::Ray
permet de représenter et de manipuler
facilement un rayon.
gk::Hit
permet de représenter simplement une
intersection, gk::RTTriangle::intersect(
)
et gk::BBox::intersect( )
.bool intersection( const gk::Ray& ray,
const gk::Mesh *mesh, gk::Hit& hit );
gk::Hit
en cas
d'intersection. gk::Point p=
ray(hit.t);
remarque : pour choisir le point de vue, le plus simple
est d'utiliser la classe gk::FirstPersonCamera et sa fonction read().
Cela permet d'utiliser mesh_viewer pour naviguer dans la scene et
d'enregistrer les parametres de la camera (appuyez sur C pour copier, et
V pour coller...).
Construisez un arbre permettant de grouper les objets / les primitives de la scène en utilisant le premier algorithme décrit en cours.
Comparer les temps d'execution avec et sans la structure accélératrice. Est-elle rentable pour toutes les scènes ?
il faut fournir un prédicat à std::partition, la manière la plus souple consiste à créer un foncteur :
struct compareTriangle
{
int axe;
float coupe;
compareTriangle( const int _axe, const float _coupe )
:
axe(_axe),
coupe(_coupe)
{}
bool operator( )( const gk::RTTriangle& triangle ) const
{
return (triangle.getBBoxCenter(axe) < coupe);
}
};
on peut utiliser ce predicat avec std::partition :
gk::RTTriangle *pivot= std::partition( &triangles[triangle_start], &triangles[triangle_end],
compareTriangle
(axe, milieu) );
Ecrivez la fonction intersect( const gk::Ray& ray, gk::Hit& hit ) qui teste les intersections entre le rayon et les triangles stockés dans l'arbre.
Utilisez les champs de la structure gk::Hit pour stocker le point d'intersection, sa normale, sa matiere, etc.
indication : pour retrouver facilement la matière d'un
triangle lors du calcul d'éclairage, le plus simple est de la récuperer dans le gk::Mesh,
mais il faut stocker l'indice du triangle... cf gk::RTTriangle::id
// charger un objet gk::Mesh *mesh= gk::MeshIO::read("bigguy.obj"); if(mesh == NULL) return "erreur"; // recupere les triangles int n= mesh->triangleCount(); for(int i= 0; i < n; i++)Il faut également penser à remplir correctement la structure gk::Hit dans votre fonction d'intersection :
{
gk::RTTriangle triangle= mesh->getTriangle(i);
triangle.id= i; // conserver l'indice du triangle dans l'objet
triangles.push_back( triangle );
}
for(unsigned int i= debut; i < fin; i++) { float t, u, v; if(triangles[i].Intersect(ray, hit.t, t, u, v)) { hit.t= t; hit.u= u; hit.v= v; hit.p= ray(t); hit.object_id= triangles[i].id; // permet de retrouver toutes les infos associees au triangle } }
gk::MeshMaterial& material= mesh->getTriangleMaterial(hit.object_id);
gk::Normal normal= mesh->getTriangleNormal(hit.object_id);
// ou la normale interpolée à l'interieur du triangle, au lieu de la normale géométrique
gk::Normal normal= mesh->getUVNormal(hit.object_id, hit.u, hit.v);
Reprenez l'exemple du cours et écrivez la fonction direct( ) qui estime l'énergie directe réfléchie par un point p. :
gk::MeshMaterial.emission
) gk::Mesh::triangleMaterial( )
pour construire l'ensemble
des sources de
lumières éclairant la scène. // representation d'une source de lumiere
struct Source
{
gk::Triangle triangle;
gk::Energy emission;
};
// ensemble de sources de lumieres
std::vector<Source> sources;
// charger un objet
gk::Mesh *mesh= gk::MeshIO::read("geometry.obj");
assert(mesh != NULL);
for(int i= 0; i <
mesh->triangleCount()
; i++)
{
// recuperer la matiere associe a
chaque triangle de l'objet
const gk::MeshMaterial&
material= mesh->triangleMaterial(i);
if(isBlack(material.emission) == false)
{
// construire
une source de lumiere
Source source;
source.emission= material.emission;
source.triangle= mesh->getTriangle(i);
// inserer la
source de lumiere dans l'ensemble.
sources.push_back(source);
}
}
gk::Triangle::sampleUniform()
),gk::Triangle::sampleUniform()
et gk::Triangle::pdfUniform()
),gk::Color direct( const
gk::Point& p, const gk::Normal& n, const MeshMaterial&
matiere, const std::vector<Source>& sources );
rappel : pensez à exprimer toutes les probabilités sur le même domaine / mesure...
#include "Geometry.h"
#include "RTTriangle.h"
#include "Transform.h"
#include "MeshIO.h"
#include "ImageIO.h"
// objet
gk::Mesh *mesh= NULL;
// representation d'une source de lumiere
struct Source
{
gk::Triangle triangle;
gk::Energy emission;
};
// ensemble de sources de lumieres
std::vector<Source> sources;
// ensemble de triangles
std::vector<gk::RTTriangle> triangles;
// calcule l'intersection d'un rayon et de tous les triangles
bool intersect( const gk::Ray& ray, gk::Hit& hit )
{
for(unsigned int i= 0; i < triangles.size(); i++) { float t, u, v; if(triangles[i].Intersect(ray, hit.t, t, u, v)) { hit.t= t; hit.u= u; hit.v= v; hit.p= ray(t); hit.object_id= triangles[i].id; // permet de retrouver toutes les infos associees au triangle } }
return (hit.object_id != -1);}
int main( )
{
// charger un objet
mesh= gk::MeshIO::read("geometry.obj");
assert(mesh != NULL);
// identifier les sources de lumiere
for(int i= 0; i <
mesh->triangleCount()
; i++)
{
// recuperer la matiere associe a
chaque triangle de l'objet
const gk::MeshMaterial&
material= mesh->triangleMaterial(i);
if(
material.emission.
isBlack() == false)
{
// construire
une source de lumiere
Source source;
source.emission= material.emission;
source.triangle= mesh->getTriangle(i);
// inserer la
source de lumiere dans l'ensemble.
sources.push_back(source);
}
}
// recupere les triangles
for(int i= 0; i < mesh->triangleCount(); i++)
{
gk::RTTriangle triangle= mesh->getTriangle(i);
triangle.id= i; // conserver l'indice du triangle dans l'objet
triangles.push_back( triangle );
}
// creer une image resultat
gk::HDRImage *image= new gk::HDRImage(512, 512);
// definir les transformations
gk::Transform model;
gk::Transform view= gk::Translate(
gk::Vector(0.f, 0.f, -50.f) ); // recule la camera pour
observer tout l'objet
gk::Transform projection= gk::Perspective(50.f, 1.f, 1.f, 1000.f); // projection perspective
gk::Transform viewport=
gk::Viewport(image->width(),
image->height()); // transformation
adaptee a la resolution de l'image resultat
// compose les transformations utiles
gk::Transform vpv= viewport * projection * view;
// parcours tous les pixels de l'image
for(int y= 0; y < image->height(); y++)
{
for(int x= 0; x < image->width(); x++)
{
// generer le rayon pour le pixel (x,y) dans le repere de l'image
gk::Point origine(x +.5f, y + .5f, 0.f); // sur le
plan near
gk::Point extremite(x +.5f, y + .5f, 1.f); // sur le
plan far
// transformer le rayon dans le repere de la scene
gk::Point o= {...}; // transformation de origine
, par exemple vpv(origine) ou vpv.inverse(origine)
gk::Point e= {...}; // transformation de extremite
gk::Ray(o, e);
gk::Hit hit(ray);
gk::Color color;
// calculer l'intersection du rayon avec les triangles du mesh
intersect(ray, hit);
// si une intersection existe...
if(hit.object_id != -1)
{
// calculer l'energie reflechie par le point vers la camera
gk::Point p= ray(hit.t);
gk::Normal=
mesh->getTriangleNormal(hit.object_id);
const gk::MeshMaterial& material= mesh->getTriangleMaterial(hit.object_id);
// eclairage direct
color= direct(p, n, material);
// eclairage indirect
color= color + indirect(p, n, material);
}
// ecrire une couleur dans le pixel de l'image
image->setPixel(x, y, color);
}
}
// enregistrer l'image
gk::HDRImageIO::write(image, "render.hdr");
delete image;
return 0;
}