IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Raytracer minimaliste en Haskell : Gestion des lumières


précédentsommairesuivant

IV. Les ombres

Pour commencer, nous déplaçons les fonctions de calcul d'intersection pour qu'elles puissent être importées dans le module Lights.

Main.hs
Sélectionnez
module Intersections (
  computeIntersection,
  computeIntersections,
  ) where

import Types
import Tools
import Objects

--Calcule les intersections du rayon avec un objet.
computeIntersection :: Ray -> Object -> Scene -> [Intersection]
computeIntersection ray object scene = fmap localIntersectionToGlobal intersections
  where
    intersections = intersect ray >>= mapM (distanceToIntersection object ray) $ scene'
    Object objectLocation _ intersect _ = object
    Scene camera objects lights = scene
    Camera cameraLocation distance planSize exposition = camera
    camera' = Camera (cameraLocation -! objectLocation) distance planSize exposition
    scene' = Scene camera' objects lights

--Applique le calcul d'intersections sur chacun des objets
computeIntersections :: Ray -> Scene -> [Intersection]
computeIntersections ray scene = concat (contextualIntersections scene)
  where Scene _ objects _ = scene
        contextualIntersections = sequence $ map (computeIntersection ray) objects

Commençons avec la lumière correctionnelle. Tout objet sur la trajectoire obtenue en remontant le rayon de lumière crée une ombre. Il faut donc détecter si un objet se trouve dans cette direction. Si oui, on ne doit produire aucune lumière. On va donc lancer un rayon depuis l'intersection.

On commence par exporter une constante, signifiant que toute distance inférieure à celle-ci est considérée comme égale à 0. Cela va nous permettre d'éviter de ré-intersecter la position depuis laquelle on lance notre rayon.

Tools.hs
Sélectionnez
epsilon :: RealRep
epsilon = 1e-12

Maintenant, on veut pouvoir relancer un rayon depuis une intersection. On ajoute donc une fonction spécifique dans le module Intersections. On constate ici que cela consiste simplement à modifier la position de la caméra, et de filtrer les intersections obtenues.

Intersections.hs
Sélectionnez
--Recherche les intersections depuis la position location vers la direction ray
lookFrom :: Location -> Ray -> Scene -> [Intersection]
lookFrom location ray scene = filter (\(Intersection _ _ distance _ _) -> distance > epsilon ) $ computeIntersections ray scene'
  where
    Scene camera objects light = scene
    Camera cameraLocation cameraDistance cameraPlanSize cameraExposition = camera
    camera' = Camera location cameraDistance cameraPlanSize cameraExposition
    scene' = Scene camera' objects light

On modifie maintenant le calcul de lumière en rajoutant une instruction conditionnelle. Si la liste obtenue par lookFrom en regardant dans la direction de la source lumineuse est vide, on peut calculer la lumière, sinon on renvoie (0, 0, 0).

Light.hs
Sélectionnez
buildDirectionalLight lightDirection lightColor = Light lightFunction
  where
    unaryLightVector = normalise $ (-1) *! lightDirection
    lightFunction intersection scene = if hidden then (0, 0, 0) else computedLight
      where
        hidden = not . null $ lookFrom intersectionLocation unaryLightVector scene
        computedLight = (diffuseLightFactor + specularLightFactor) *! color
        -- ...
Ombres avec lumière directionnelle

Au tour maintenant de l'ombre du spot. Pour celle-ci, on ne doit pas compter les objets derrière le spot. On filtre donc pour exclure les intersections trop éloignées. Puisqu'on fera exactement la même chose pour la lumière omnidirectionnelle, on ajoute une nouvelle fonction.

Lights.hs
Sélectionnez
isHidden :: Distance -> Location -> UnaryLightVector -> Scene -> Bool
isHidden lightDistance location unaryLightVector scene = not . null $ filteredIntersections
  where
    intersections = lookFrom location unaryLightVector scene
    filteredIntersections = filter (\ (Intersection _ _ distance _ _) -> distance < lightDistance) intersections

buildSpotLight :: LightDirection -> RadianAngle -> Attenuation -> Location -> Color -> Light
buildSpotLight lightDirection coneAngle attenuation lightLocation lightColor = Light lightFunction
  where
    lightDistance = norm lightDirection
    unaryLightDirection = normalise $ (-1) *! lightDirection
    lightFunction intersection scene = if hidden then (0, 0, 0) else computedLight
      where
        hidden = isHidden lightDistance intersectionLocation unaryLightVector scene
        computedLight = factor *! color
Ombres avec spot

Pour ce qui est de la source omnidirectionnelle, c'est identique au précédent. Il nous faut juste obtenir le vecteur pointant vers la source et la distance de la source à l'intersection.

Lights.hs
Sélectionnez
buildOmnidirectionalLight :: Attenuation -> Location -> Color -> Light
buildOmnidirectionalLight attenuation lightLocation lightColor = Light lightFunction
  where
    lightFunction intersection scene = if hidden then (0, 0, 0) else computedLight
      where
        lightDistance = norm lightDirection
        hidden = isHidden lightDistance intersectionLocation unaryLightVector scene
        computedLight = factor *! color
        lightDirection = lightLocation -! intersectionLocation
        unaryLightVector = normalise lightDirection
        -- ...
Ombres avec source omnidirectionnelle

Pour tester les ombres, j'ai rajouté quelques objets à notre scène précédente :

Main.hs
Sélectionnez
objects = [buildSphere 1.0 (0, 1.2, -7) (Material (1.0, 1.0, 1.0) 1 2 6),
               buildSphere 1.0 (0, -1.2, -7) (Material (1.0, 0.0, 1.0) 1 2 50),
               buildSphere 1.0 (1.2, 0, -7) (Material (0.0, 1.0, 1.0) 1 2 12),
               buildSphere 1.0 (-1.5, 0, -7) (Material (0.0, 0.0, 1.0) 1 1 12),
               buildSphere 0.5 (0, 2, -6.6) (Material (1.0, 1.0, 0.0) 1 1 12),
               buildSphere 1.0 (-2, 2, -4) (Material (1.0, 0.0, 0.0) 1 1 12),
               buildSphere 0.1 (-0.5, 0.7, -4.5) (Material (1.0, 0.0, 0.0) 1 1 12)]

précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Zenol. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.