Tout d'abord, vous devez avoir des notions sur les vecteurs :
- un vecteur est un point dans un repère , en gros une position
représenté sous cette forme x,y,z
en purebasic on le represente sous cette forme :
Code : Tout sélectionner
Structure Vector3D
x.d
y.d
z.d
EndStructure
Code : Tout sélectionner
Structure Vector3F
x.f
y.f
z.f
EndStructure
F pour les float , D pour les doubles. on travaillera avec les double.
2 vecteurs dans l'espace représente une droite , ou un segment ,
une demi-droite possède un vecteur , et une direction , la direction est un vecteur unitaire , on verra plus tard comment le calculé.
Le principe du Raytracing est simple , on lance des rayons à partir du point de vue vers le point visé , si il y a une interection avec un objet on calcul la lumière , on ajoute un pixel , etc...
Source de l'image
Dans cette exemple , nous ne verrons pas le calcul de la lumière
Tout d'abord , il faut initialisé notre "camera"
Il lui faut un point d'origine , un point visé , une direction.
L'origine est 0,0,0
le point visé , x,y,distance focale
La distance focale , est importante et il ne faut pas la négligé , si vous mettez une valeur au pif , par exemple 1000 , le rendu ne sera pas pareil selon la résolution.
La distance focale est représenté par "d" sur le schéma plus haut.
Il se calcul de cette façon :
Code : Tout sélectionner
-(ResolutionX / ( 2 * Tan ( FOV / 2 ) ) )
Commençons par poser nos jalons
avec des structures de bases :
Code : Tout sélectionner
Structure Vector3D
x.d
y.d
z.d
EndStructure
Structure Ray
O.Vector3D
D.Vector3D
EndStructure
Structure sphere
position.Vector3D
radius.d
EndStructure
on doit pouvoir les soustraires entre eux , obtenir un produit scalaire ( vector_dot ) , etc...
Code : Tout sélectionner
Procedure vector_normalize(*V.Vector3D)
id.d = 1/Sqr( (*V\x * *V\x) + (*V\y * *V\y) + (*V\z * *V\z) )
*V\x = *V\x * id
*V\y = *V\y * id
*V\z = *V\z * id
EndProcedure
Procedure.d vector_dot(*A.vector3d,*B.vector3d)
ProcedureReturn *A\x * *B\x + *A\y * *B\y + *A\z * *B\z
EndProcedure
Procedure.i vector_copy(*a.vector3d)
*V.vector3d = AllocateMemory(SizeOf(vector3d))
*V\x = *a\x
*V\y = *a\y
*V\z = *a\z
ProcedureReturn *V
EndProcedure
Procedure.i vector_sub(*a.vector3d,*b.vector3d)
*V.vector3d = AllocateMemory(SizeOf(vector3d))
*V\x = *a\x - *b\x
*V\y = *a\y - *b\y
*V\z = *a\z - *b\z
ProcedureReturn *V
EndProcedure
Revenons à notre point de vue ,
Notre point de vue est le 0,0,0
le point visé Pixel_x , Pixel_y , Distance_Focale
ce qui nous donne :
Code : Tout sélectionner
; Point d'origine :
A.Vector3D
A\x = 0
A\y = 0
A\z = 0
For pixel_y = 0 To 480 -1
For pixel_x = 0 To 640 -1
; Point visé
B.Vector3D
B\x = pixel_x - (640/2)
B\y = pixel_y - (480/2)
B\z = -(640 / (2*Tan(30/2)))
; on le normalize
vector_normalize(B)
On va donc pouvoir créer notre rayon ,
il nous faut , l'origine ( on l'a déjà 0,0,0 ) est la direction.
La direction est le vecteur directeur entre Origine et le point visé , il se calcule la la manière suivante :
en code :Direction = B - A
Normalize(Direction)
Code : Tout sélectionner
;Vecteur direction du rayon
Ray_Direction.Vector3D
Ray_Direction\x = B\x - A\x
Ray_Direction\y = B\y - A\y
Ray_Direction\z = B\z - A\z
vector_normalize(Ray_Direction)
Notre rayon est donc :
Code : Tout sélectionner
Rayon.Ray
Rayon\O\x = A\x
Rayon\O\y = A\y
Rayon\O\z = A\z
Rayon\D\x = Ray_Direction\x
Rayon\D\y = Ray_Direction\y
Rayon\D\z = Ray_Direction\z
Notre rayon est créer , il nous faut donc savoir si il rentre en collision avec les objets ( ici la sphère )
j'utilise cette fonction , pour testé la collision :
On lui passe en paramètre une sphere , un rayon , un pointeur sur un coefficient que l'on fixe avant avec une grande valeur ( ex:20000 )
si il y a une intersection, le Coef doit être plus petit ( c'est la distance d'impact entre l'objet et la caméra )
Si la sphere est en Z à 100 avec un diamètre de 20 , et si le rayon tape dans le centre , alors le coef doit être de 90...
Code : Tout sélectionner
Procedure intersection_sphere(*sphere.sphere,*ray.ray,*t)
*dist.vector3d = vector_copy(*sphere\position)
vector_sub(*dist,*ray\o)
B.f = vector_dot(*ray\d,*dist)
D.f = B*B - vector_dot(*dist,*dist) + *sphere\radius * *sphere\radius
If D<0
ProcedureReturn #False
EndIf
T0.f = B - Sqr(D)
T1.f = B + Sqr(D)
result = #False
If (T0>0.1) And (T0 < PeekD(*t))
PokeD(*t,T0)
result = #True
EndIf
If (T1>0.1) And (T1 < PeekD(*t))
PokeD(*t,T1)
result = #True
EndIf
ProcedureReturn result
EndProcedure
l'utilisation de la procédure :
Si intersection <>-1 alors il y a une intersection... simple non ?Coef.d = 20000
Intersection.b = intersection_sphere(Sphere,Rayon,@Coef)
voici la boucle principale :
Code : Tout sélectionner
For pixel_y = 0 To 480 -1
For pixel_x = 0 To 640 -1
; Point visé du rayon
B.Vector3D
B\x = pixel_x - (640/2)
B\y = pixel_y - (480/2)
B\z = -(640 / (2*Tan(30/2))) ; Calcul de la Distance Focale ( Fov = 30 )
; on le normalize
vector_normalize(B)
;Vecteur direction du rayon
Ray_Direction.Vector3D
Ray_Direction\x = B\x - A\x
Ray_Direction\y = B\y - A\y
Ray_Direction\z = B\z - A\z
vector_normalize(Ray_Direction)
; On a le point de départ
; le point visé , on en à donc deduis la direction
; on peut construire notre rayon.
Rayon.Ray
Rayon\O\x = A\x
Rayon\O\y = A\y
Rayon\O\z = A\z
Rayon\D\x = Ray_Direction\x
Rayon\D\y = Ray_Direction\y
Rayon\D\z = Ray_Direction\z
Coef.d = 20000
Intersection.b = intersection_sphere(Sphere,Rayon,@Coef)
If Intersection<>-1 And Coef < 20000
; Affiche un pixel en X,Y ici !!!!!
EndIf
Next
Next
le code complet :
Code : Tout sélectionner
Structure Vector3D
x.d
y.d
z.d
EndStructure
Procedure vector_normalize(*V.Vector3D)
id.d = 1/Sqr( (*V\x * *V\x) + (*V\y * *V\y) + (*V\z * *V\z) )
*V\x = *V\x * id
*V\y = *V\y * id
*V\z = *V\z * id
EndProcedure
Procedure.d vector_dot(*A.vector3d,*B.vector3d)
ProcedureReturn *A\x * *B\x + *A\y * *B\y + *A\z * *B\z
EndProcedure
Procedure.i vector_copy(*a.vector3d)
*V.vector3d = AllocateMemory(SizeOf(vector3d))
*V\x = *a\x
*V\y = *a\y
*V\z = *a\z
ProcedureReturn *V
EndProcedure
Procedure.i vector_sub(*a.vector3d,*b.vector3d)
*V.vector3d = AllocateMemory(SizeOf(vector3d))
*V\x = *a\x - *b\x
*V\y = *a\y - *b\y
*V\z = *a\z - *b\z
ProcedureReturn *V
EndProcedure
Structure Ray
O.Vector3D
D.Vector3D
EndStructure
Structure sphere
position.Vector3D
radius.d
EndStructure
Procedure intersection_sphere(*sphere.sphere,*ray.ray,*t)
*dist.vector3d = vector_copy(*sphere\position)
vector_sub(*dist,*ray\o)
B.f = vector_dot(*ray\d,*dist)
D.f = B*B - vector_dot(*dist,*dist) + *sphere\radius * *sphere\radius
If D<0
ProcedureReturn #False
EndIf
T0.f = B - Sqr(D)
T1.f = B + Sqr(D)
result = #False
If (T0>0.1) And (T0 < PeekD(*t))
PokeD(*t,T0)
result = #True
EndIf
If (T1>0.1) And (T1 < PeekD(*t))
PokeD(*t,T1)
result = #True
EndIf
ProcedureReturn result
EndProcedure
Sphere.sphere
Sphere\position\z = 200
Sphere\radius = 20*2
; Point de départ du rayon
A.Vector3D
A\x = 0
A\y = 0
A\z = -1
CreateImage(0,640,480)
StartDrawing(ImageOutput(0))
For pixel_y = 0 To 480 -1
For pixel_x = 0 To 640 -1
; Point visé du rayon
B.Vector3D
B\x = pixel_x - (640/2)
B\y = pixel_y - (480/2)
B\z = -(640 / (2*Tan(30/2))) ; Calcul de la Distance Focale ( Fov = 30 )
; on le normalize
vector_normalize(B)
;Vecteur direction du rayon
Ray_Direction.Vector3D
Ray_Direction\x = B\x - A\x
Ray_Direction\y = B\y - A\y
Ray_Direction\z = B\z - A\z
vector_normalize(Ray_Direction)
; On a le point de départ
; le point visé , on en à donc deduis la direction
; on peut construire notre rayon.
Rayon.Ray
Rayon\O\x = A\x
Rayon\O\y = A\y
Rayon\O\z = A\z
Rayon\D\x = Ray_Direction\x
Rayon\D\y = Ray_Direction\y
Rayon\D\z = Ray_Direction\z
Coef.d = 20000
Intersection.b = intersection_sphere(Sphere,Rayon,@Coef)
If Intersection<>-1 And Coef < 20000
Plot(pixel_x,pixel_y,$FF)
EndIf
Next
Next
StopDrawing()
SaveImage(0,"Save.bmp")