Code archives/3D Graphics - Mesh/Procedural Grass Pattern for texture

This code has been declared by its author to be Public Domain code.

Download source code

Procedural Grass Pattern for texture by Bobysait2017
This code produices a large floor of grass using meshes.

The original idea comes from this youtube tutorial (this is just a way to get a similar result in code)

https://www.youtube.com/watch?v=cqn_jryEXLw
Const LEAF_MAX_RES% = 24
Function RandomLeaf(NIter%=-1, LeafW#=0.2, LeafVarX#=0.10, LeafVarY#=0.25, Parent=0)
	Local mesh = CreateMesh(Parent)
	EntityFX mesh, 1+2+8+32
	
	Local surf = CreateSurface(mesh)
	
	If (NIter<4) Then NIter = Rand(4,LEAF_MAX_RES-1)
	If (NIter>=LEAF_MAX_RES) Then NIter = LEAF_MAX_RES-1
	
	Local lx#[LEAF_MAX_RES]
	Local ly#[LEAF_MAX_RES]
	Local lw#[LEAF_MAX_RES]
	Local x# = 0
	Local y# = 0
	Local w# = 1
	Local i% = 0
	Local xSens# = -1+2*Rand(0,1)
	Local RndXAng# = Rand(170,320)
	For i = 0 To NIter
		lx[i] = x*xSens
		ly[i] = y
		Local ang0 = Rand(-90,-80)
		lw[i] = LeafW * Cos(ang0+(90-ang0)*i/NIter)+LeafW*Rnd(-0.1,0.1)
		Local y0# = Float(1+i)/NIter
		Local y1# = Float(2+i)/NIter
		y = y + LeafVarY*Rnd(y0,y1)
		x = x + LeafVarX*Cos(i*RndXAng / NIter)
	Next
	
	Local v0%
	Local NV% = 9
	Local AO# = LeafW*1.2
	Local AOy# = -AO
	For i = 0 To NIter
		Local DI# = lw[i]*0.001
		v0	=	AddVertex ( surf, lx[i]-lw[i]-AO,-0.001,ly[i]+AOy ) : VertexColor surf, v0+0, 0,0,0, 0.0
				AddVertex ( surf, lx[i]-lw[i]+DI,-0.001,ly[i]+AOy ) : VertexColor surf, v0+1, 0,0,0, 0.3
				AddVertex ( surf, lx[i]+lw[i]-DI,-0.001,ly[i]+AOy ) : VertexColor surf, v0+2, 0,0,0, 0.3
				AddVertex ( surf, lx[i]+lw[i]+AO,-0.001,ly[i]+AOy ) : VertexColor surf, v0+3, 0,0,0, 0.0
		If i>0
				AddTriangle surf, v0-4, v0+1, v0-3
				AddTriangle surf, v0-4, v0  , v0+1
				AddTriangle surf, v0-3, v0+2, v0-2
				AddTriangle surf, v0-3, v0+1, v0+2
				AddTriangle surf, v0-2, v0+3, v0-1
				AddTriangle surf, v0-2, v0+2, v0+3
		EndIf
		If i=0
			AOy = 0
		ElseIf i=NIter-1
			AOy = AO
		EndIf
	Next
	
	Local vS% = CountVertices(surf)
	Local v_%
	AOy# = -AO
	For i = 0 To NIter
		v_ =	AddVertex	( surf, lx[i]-lw[i]*1.2,0,ly[i]+AOy )
				AddVertex	( surf, lx[i]-lw[i]    ,0,ly[i] )
				AddVertex	( surf, lx[i]-lw[i]*.6 ,0,ly[i] )
		If i>NIter/6 And i <NIter*7/8
				AddVertex	( surf, lx[i]-lw[i]*Rnd(.2,0.4) ,0,ly[i] )
				AddVertex	( surf, lx[i]          ,0,ly[i] )
				AddVertex	( surf, lx[i]+lw[i]*Rnd(.2,0.4) ,0,ly[i] )
		Else
				AddVertex	( surf, lx[i]          ,0,ly[i] )
				AddVertex	( surf, lx[i]          ,0,ly[i] )
				AddVertex	( surf, lx[i]          ,0,ly[i] )
		EndIf
				AddVertex	( surf, lx[i]+lw[i]*.6 ,0,ly[i] )
				AddVertex	( surf, lx[i]+lw[i]    ,0,ly[i] )
				AddVertex	( surf, lx[i]+lw[i]*1.2,0,ly[i]+AOy )
		If i=0
			AOy = 0
		ElseIf i=NIter-1
			AOy = AO
		EndIf
		
		If i>0
			For j = 0 To NV-2
				v0 = v_+j
				AddTriangle surf, v0-NV,v0+1,v0-NV+1
				AddTriangle surf, v0-NV,v0,v0+1
			Next
		EndIf
	Next
	
	Local r0 = 255, g0=225, b0=060
	Local r1 = 055, g1=160, b1=010
	Local r2 = 040, g2=110, b2=005
	
	Local c0# = 0.2
	Local c1# = 1 - c0
	For i = 0 To NIter
		Local c# = c0+c1*ly[i]/ly[NIter]
		Local rl = c*(r1+(r0-r1)*c)
		Local gl = c*(g1+(g0-g1)*c)
		Local bl = c*(b1+(b0-b1)*c)
		Local rm = c*(r1+(r0-r1)*c*0.7)
		Local gm = c*(g1+(g0-g1)*c*0.7)
		Local bm = c*(b1+(b0-b1)*c*0.7)
		Local rn = c*(r1+(r0-r1)*c*0.95)
		Local gn = c*(g1+(g0-g1)*c*0.95)
		Local bn = c*(b1+(b0-b1)*c*0.95)
		VertexColor surf, vS+i*NV+0, rl,gl,bl, 0.0
		VertexColor surf, vS+i*NV+1, rl,gl,bl
		VertexColor surf, vS+i*NV+2, rm,gm,bm
		VertexColor surf, vS+i*NV+3, rn,gn,bn
		VertexColor surf, vS+i*NV+4, c*r2,c*g2,c*b2
		VertexColor surf, vS+i*NV+5, rn,gn,bn
		VertexColor surf, vS+i*NV+6, rm,gm,bm
		VertexColor surf, vS+i*NV+7, rl,gl,bl
		VertexColor surf, vS+i*NV+8, rl,gl,bl, 0.0
	Next
	Return mesh
End Function

Graphics3D 1024,768,0,2

Local cam = CreateCamera()
CameraProjMode cam, 2
CameraZoom cam, 0.075
CameraClsColor cam, 255,000,255


Local Leaf_Model_Pivot = CreatePivot()
HideEntity Leaf_Model_Pivot
Local Leaf_Brush_Pivot = CreatePivot()
HideEntity Leaf_Brush_Pivot

Local Leaf_Copies_Pivot = CreatePivot()

Const NLeaves% = 8
Local Leaves[NLeaves+1]
For i = 0 To NLeaves
	Leaves[i] = RandomLeaf(6+i*2, 0.3, 0.1, 0.3, Leaf_Model_Pivot)
Next

Local LeavesBrush[10]
For i = 0 To 6
	LeavesBrush[i] = CreatePivot(Leaf_Brush_Pivot)
	Local l_NLeaves% = Rand(8,12)
	For j = 0 To l_NLeaves
		Local f = CopyEntity(Leaves[Rand(0, NLeaves)], LeavesBrush[i])
		ScaleEntity f, Rnd(.9,1.1), Rnd(.9,1.1), Rnd(.9,1.1)
		PositionEntity f, Rnd(-0.1,0.1)*Float(j)/l_NLeaves, -0.01*Float(j+1)/l_NLeaves,Rnd(-0.1,0.1) + j*0.15
		RotateEntity f, 0,Rand(-60,60),0
	Next
Next

For a = - 16 To 16 Step 4
For b = - 16 To 16 Step 4
	Local l_BrushPiv	=	CreatePivot(Leaf_Copies_Pivot)
							PositionEntity l_BrushPiv, a+Rnd(-1,1), -.1+Float(0.3)*Sqr(a*a+b*b)/(16*16+16*16), b+Rnd(-1,1)
							RotateEntity l_BrushPiv, 0,Rand(360),0
	For i = 0 To 12
		Local l_FBrush = CopyEntity(LeavesBrush[Rand(0,6)], l_BrushPiv)
						RotateEntity l_FBrush, 0,(i*360)/13,0
						MoveEntity l_FBrush, 0,0,Rnd(0.0,1.5)
		ScaleEntity l_FBrush, Rnd(.9,1.1), Rnd(.9,1.1), Rnd(.9,1.1)
	Next
Next
Next

PositionEntity cam, 0,16,0
RotateEntity cam, 90,0,0

RenderWorld
Flip True
FreeEntity Leaf_Model_Pivot
FreeEntity Leaf_Copies_Pivot
FreeEntity Leaf_Brush_Pivot

WaitKey()
End

Comments

Bobysait2017
It would look really better with hilights on the leaves, but ... you know ... I'm lazzy :)

I think a simple texture on the leaves could do the trick without too much effort.


Pakz2017
Looks great!


steve_ancell2017
That's pretty neat. ;)


BlitzSupport2017
Really nice.


Rick Nasher2017
Mighty purdy. Amazing what some people can do with procedural gfx.


Bobysait2017
Glad you like it.

Procedural is my god, I beleive in procedural ;)


Mighty purdy.


I had to google it to know it means :) (never heard this expression before)


RemiD2017
nice !


Rick Nasher2017

Mighty purdy


It's hillbilly/red neck talk.

XD


RemiD2017
@Bobysait>>can you explain why you need vertex alpha to make this kind of leaf ? (i see how to create the same result without any alpha...)


Bobysait2017
(I'll answer with the Paul Taylor' style)

My name is Bobysait, I used to be a graphist but now I build everything with procedural stuff.

One of the thing I've never understood is the generation of procedural stuff using only mesh and surfaces when it's easier to create models with a dedicated software.

WTF Procedural ?!

There are many ways to get the job done with 3dsmax or blender or [...] and a texture, so ...
"Why is it better to use the procedural way using vertex alpha Jean-Pierre ?"
I'M GLAD YOU FUC**NG ASK !

I use vertex alpha for 3 reasons.

Number One :
It's used to blend the edges so it makes a kind of low-cost anti-aliasing

Number 2 !
At the same time it generates some ambient occlusion on the leaves behind.

And Number 3 ?
I just didn't want to use a texture to do it, so i use Vertex color+alpha.

That's it :)


RemiD2017
there was a shorter answer :
"because i want to" or "i like to use vertexalpha, that's my thing." ;)


to blend the edges so it makes a kind of low-cost anti-aliasing
it generates some ambient occlusion on the leaves behind


ok!


Krischan2017
It's really nice and technically very interestung, but... the Triscount is 3.644.476. So you can only use it as a texture and not in 3D with the original meshes, unfortunately.


Bobysait2017
Well, the demo is only for example, but it's absolutely not made to be used as a mesh
It's just a starting point for something else. (see the video of the tutorial posted above, it shows how the guy do it with photoshop, I only rebuilt the shapes so we can do the same with procedural stuff)

The theory behind :
- Create random patterns and copyrect each of them to a separate texture
- Apply each texture to a quad (with equal width and height)
offset each texture (PositionTexture) to fit the viewport (it will wrap the borders)
- Finally copyrect the viewport to a texture

So you end with a seamless texture.

That's how it's supposed to be used :)


Code Archives Forum