Je vais tenté via un code d'expliquer la base du raytracing en purebasic.
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:
Structure Vector3D
x.d
y.d
z.d
EndStructure
Code:
Structure Vector3F
x.f
y.f
z.f
EndStructure
3D ou 3F , c'est la notation hongroise , 3 pour les dimension , suivi du type
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'imageDans cette exemple , nous ne verrons pas le calcul de la lumièreTout d'abord , il faut initialisé notre "camera"
Il lui faut un point d'origine , un point visé , une direction.
Citation:
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:
-(ResolutionX / ( 2 * Tan ( FOV / 2 ) ) )
Plus le FOV est grand , plus la distance focale est petite , et inversement , un FOV de 30 est correct.
Commençons par poser nos jalons
avec des structures de bases :
Code:
Structure Vector3D
x.d
y.d
z.d
EndStructure
Structure Ray
O.Vector3D
D.Vector3D
EndStructure
Structure sphere
position.Vector3D
radius.d
EndStructure
Il va nous falloir aussi quelques fonction pour nos vecteurs
on doit pouvoir les soustraires entre eux , obtenir un produit scalaire ( vector_dot ) , etc...
Code:
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:
; 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 :
Citation:
Direction = B - A
Normalize(Direction)
en code :
Code:
;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:
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:
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 :
Citation:
Coef.d = 20000
Intersection.b = intersection_sphere(Sphere,Rayon,@Coef)
Si intersection <>-1 alors il y a une intersection... simple non ?
voici la boucle principale :
Code:
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
C'est simple non ?
le code complet :
Code:
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")
l'output du programme , pixar n'a plus qu'a bien se tenir !
