Générateur de perlin noise

Généralités sur la programmation 3D
crisot
Messages : 98
Inscription : lun. 30/août/2004 21:03

Générateur de perlin noise

Message par crisot »

Petit générateur de terrain basé sur un générateur de nombres pseudo aléatoire et un bruit de perlin.

Code : Tout sélectionner

Procedure interpolate (c1.f, c2.f, len, delta)
	If len=0
		c.f=c1
	Else
		a.f=delta/len
 		v1.f=3*Pow(1-a,2)-2*Pow(1-a,3)
		v2.f=3*Pow(a, 2)-2*Pow(a,3)
      c=c1*v1+c2*v2
	EndIf
	ProcedureReturn c
EndProcedure

Procedure GenerateTerrain(size.l, seed.l, pers.f, oct.b)
	*buffer=AllocateMemory(size*size*SizeOf(float), #PB_Memory_NoClear)	
	Dim *buf(256)
	For i=0 To oct
		*buf(i)=AllocateMemory(size*Size, #PB_Memory_NoClear)
	Next
	*buf=*buf(0)
   
   ; Générateur pseudo aléatoire
	x=seed
   a=4274196681
   m=Pow(10,9)-9
   For j=0 To size-1
   	For i=0 To size-1
   		x=(a*x)%m
   		PokeA(*buf,x)
         *buf+1
      Next
   Next
   
	; Interpolation à différents octaves
	If oct>1
   	For k=1 To oct-1
	   	freq=Pow(2,k)
   		For j=0 To (size-1)/freq
   			For i=0 To (size-1)/freq
   				x1=freq*i
   				x2=freq*(i+1) : If x2>=size : x2=size-1 : EndIf
					y1=freq*j
					y2=freq*(j+1) : If y2>=size : y2=size-1 : EndIf
					c11.a=PeekA(*buf(0)+x1+y1*size)
					c21.a=PeekA(*buf(0)+x2+y1*size)	
					c12.a=PeekA(*buf(0)+x1+y2*size)
					c22.a=PeekA(*buf(0)+x2+y2*size)
					For u=y1 To y2
						c1=interpolate(c11,c12,y2-y1,u-y1)
						c2=interpolate(c21,c22,y2-y1,u-y1)	
						For n=x1 To x2
							c3=interpolate(c1,c2,x2-x1,n-x1)	
							PokeA(*buf(k)+n+u*size,c3)	
						Next
					Next
				Next
			Next
		Next
	EndIf

   For j=0 To size-1
   	For i=0 To size-1
   		c.f=PeekA(*buf(0)+i+j*size)
   		d.f=1
			If oct>1
   			For k=1 To oct-1
	   			c=c*pers.f+PeekA(*buf(k)+i+j*size)
   				d=d*pers.f+1
   			Next  
			EndIf	
   		c=c/d
   		PokeF(*buffer+(i+j*size)*SizeOf(float),c)
		Next
   Next
   
  	For i=0 To oct : FreeMemory(*buf(i)) : Next
	ProcedureReturn *buffer
EndProcedure

Procedure GenerateTerrainMesh(*buffer, size.l)
; Creation d'un material par défaut
; Une texture à la con
	texture=CreateTexture(#PB_Any,2,2)
	StartDrawing(TextureOutput(texture))
	Box(0,0,2,2,$ffffff)
	StopDrawing()
; Le material
	mat=CreateMaterial(#PB_Any,TextureID(texture))


	terrain=CreateMesh(#PB_Any, #PB_Mesh_TriangleList, #PB_Mesh_Dynamic)
	For j=0 To size-1
		For i=0 To size-1
			s.f=PeekF(*buffer+(i+j*size)*SizeOf(float))
			MeshVertexPosition((i-size/2)*256/size,s/3,(j-size/2)*256/size)
			MeshVertexTextureCoordinate(0,0)
			MeshVertexNormal(0,0,0)
		Next
	Next
	For j=0 To size-2
		For i=0 To size-2
			MeshFace(i+size*j, i+size*(j+1), i+1+size*(j+1))
			MeshFace(i+size*j, i+1+size*(j+1), i+1+size*j)
		Next
	Next
	FinishMesh(#True)
	NormalizeMesh(terrain)
	CreateEntity(0,MeshID(terrain),MaterialID(mat),0,0,0)
EndProcedure


terrainsize.l=256
; Ces 3 valeurs permettent... pas mal de choses.
seed.l=66887
persistance.f=0.4
octave.b=7
;***

AngX.f : AngY.f : AngZ.f
WindowWidth  = 1024
WindowHeight = WindowWidth*9/16

InitEngine3D()
InitKeyboard()
InitSprite()

win=OpenWindow(#PB_Any, 32, 32, WindowWidth, WindowHeight, "Perlin-test")
OpenWindowedScreen (WindowID(win) ,0 ,0 ,WindowWidth ,WindowHeight , 0, 0, 0, #PB_Screen_WaitSynchronization)

cam=CreateCamera(#PB_Any, 0, 0, 100, 100)
MoveCamera(cam, 0, 128, 200, #PB_Absolute)
RotateCamera(cam, -30, 0, 0, #PB_Absolute)
CameraFOV(cam, 80)
CreateLight(0, $aaffff, 10000, 10000, 0)
AmbientColor($554444)

*buffer=GenerateTerrain(terrainsize, seed, persistance, octave)
GenerateTerrainMesh(*buffer, terrainsize)

Repeat
	AngX+0 : AngY+0.5 : AngZ+0
	RotateEntity(0,AngX, AngY, AngZ)
	
	ClearScreen(RGB(0,0,0))
	RenderWorld()
	FlipBuffers()
   
   ExamineKeyboard()
   If KeyboardPushed(#PB_Key_Escape) : Quit=1 : EndIf	

	Repeat
      Event = WindowEvent()
      Select Event
         Case #PB_Event_CloseWindow
        Quit = 1
   EndSelect
   Until Event=0
Until Quit = 1

FreeMemory(*buffer)
Les curieux pourront aller bricoler du coté de:

Code : Tout sélectionner

terrainsize.l=256
; Ces 3 valeurs permettent... pas mal de choses.
seed.l=66887
persistance.f=0.4
octave.b=7
;***
C'est encore très perfectible, il est possible de voir des répétitions de motif. Mais au moins j'ai ça sous le coude. :)
Fred
Site Admin
Messages : 2651
Inscription : mer. 21/janv./2004 11:03

Re: Générateur de perlin noise

Message par Fred »

Tres sympa ! Si tu veux avoir toujours les meme valeurs avec les fonctions internes de PB, tu peux utiliser RandomSeed():

Code : Tout sélectionner

   ; Générateur pseudo aléatoire
   RandomSeed(seed)
   For j=0 To size-1
      For i=0 To size-1
         PokeA(*buf,Random(255))
         *buf+1
      Next
   Next
Avatar de l’utilisateur
Kwai chang caine
Messages : 6962
Inscription : sam. 23/sept./2006 18:32
Localisation : Isere

Re: Générateur de perlin noise

Message par Kwai chang caine »

Trés joli, marche nickel
Merci pour le partage 8)
ImageLe bonheur est une route...
Pas une destination

PureBasic Forum Officiel - Site PureBasic
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: Générateur de perlin noise

Message par blendman »

Si tu utilises le perlin noise, ne peux-tu pas générer une texture avec ça aussi ?
par exemple, en mettant en vert ce qui est sombre (<rgb(40,40,40) par exemple), ce qui est moyen en marron et ce qui clair en marron clair (ou en blanc pour les montagnes).

ça doit être possible, non ?
G-Rom
Messages : 3627
Inscription : dim. 10/janv./2010 5:29

Re: Générateur de perlin noise

Message par G-Rom »

@Blendman, tu peu le faire en 2D sans problème, j'avais filé un include à kelebrindae pour son mineclone : http://www.purebasic.fr/french/viewtopi ... lit=perlin , tu as 2 fonctions : PERLIN_initialize() & PERLIN_generateNoise2D() , pratique pour les textures , tu as la version 1D ( pour les "courbes") et la version 3D ( que kelebrindae utilisait pour la genération de son monde )
crisot
Messages : 98
Inscription : lun. 30/août/2004 21:03

Re: Générateur de perlin noise

Message par crisot »

Blendman: Pour le moment j'ai juste besoin du générateur, pas d'un rendu. La solution que tu donnes se pratique, mais le jour où j'aurais besoin du rendu, il faut aussi utiliser les normales, pas seulement l'altitude. Exemple, les parties "plates" sont enneigées, et les parties en pente sont à nue (la neige ne tiens pas). Bien plus sympa qu'uniquement l'altitude. :)
Répondre