Cel-shading - Best Method For Outlines?

Blitz3D Forums/Blitz3D Programming/Cel-shading - Best Method For Outlines?

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

I've just been playing around with the idea of drawing lines. . .

So far I've just been drawing all edges on front facing triangles and so haven't needed to compile the list of edges etc. There does seem to be a perfomance hit, but only when you start drawing more than a few hundred lines.

It seems that the number of lines actually drawn is the main factor on performance, not the size of the mesh you perform the operation to (commenting out the Line statements proves this)

Here's the code:
Graphics3D 800, 600


Global mshSphere% = CreateSphere(12)
UpdateNormals mshSphere
EntityFX mshSphere, 4


Global GlobalLight% = CreateLight()
RotateEntity GlobalLight, 45, 45, 45
Global GlobalCamera% = CreateCamera()

PositionEntity GlobalCamera, 4, 2, 4
PointEntity GlobalCamera, mshSphere

CameraClsColor GlobalCamera, 180,180,180

Local fps%
Local fpscount%
Local millistop%
Local millis%

While Not KeyHit(1)
	Navigate_World_With_MouseAndKeys(0.5, 0.1)
	
	TurnEntity mshSphere, 0, 0.5, 0
	SetBuffer BackBuffer()
	Cls
	
	RenderWorld()
	DrawFrontFacingLines(mshSphere, GlobalCamera, 0, 0)
	Color 255, 0, 0
	Text 1, 1, "FPS=" + fps	
	Text 1, 12, "TrisRendered=" + TrisRendered()	
	
	Flip
	
	fpscount = fpscount + 1
	millis% = MilliSecs()
	If millistop < millis - 1000 Then
		millistop = millis
		fps = fpscount
		fpscount = 0
	End If
Wend
End


Function DrawFrontFacingLines(m%, cam, thick%, sketchy#)
	Color 30, 30, 50
	
	Local v3X = CreateVector(3)
	Local v3Y = CreateVector(3)		
	For is% = 1 To CountSurfaces(m)
		s% = GetSurface(m, is)
		For it% = 0 To CountTriangles(s) - 1
			If FrontFacing(cam, s, it, m) Then
				For i% = 0 To 2
					vx# = VertexX(s, TriangleVertex(s, it, i))
					vy# = VertexY(s, TriangleVertex(s, it, i))
					vz# = VertexZ(s, TriangleVertex(s, it, i))
					TFormPoint vx, vy, vz, m, 0
					vx = TFormedX()
					vy = TFormedY()
					vz = TFormedZ()
											
					CameraProject(cam, vx, vy, vz)
					VctPut v3X, i, ProjectedX()
					VctPut v3Y, i, ProjectedY()
				Next
				
				c0 = (VctGet(v3X,0)=0) And (VctGet(v3Y,0)=0)
				c1 = (VctGet(v3X,1)=0) And (VctGet(v3Y,1)=0)
				c2 = (VctGet(v3X,2)=0) And (VctGet(v3Y,2)=0)
				For n% = -thick To thick
					r# = sketchy
					If Not (c0 Or c1) Then Line VctGet(v3X,0)+n+Rnd(-r, r), VctGet(v3Y,0)+Rnd(-r, r), VctGet(v3X,1)+Rnd(-r, r), VctGet(v3Y,1)+Rnd(-r, r)
					If Not (c1 Or c2) Then Line VctGet(v3X,1)+n+Rnd(-r, r), VctGet(v3Y,1)+Rnd(-r, r), VctGet(v3X,2)+n+Rnd(-r, r), VctGet(v3Y,2)+Rnd(-r, r)
					If Not (c0 Or c2) Then Line VctGet(v3X,0)+n+Rnd(-r, r), VctGet(v3Y,0)+Rnd(-r, r), VctGet(v3X,2)+n+Rnd(-r, r), VctGet(v3Y,2)+Rnd(-r, r)
				Next

			End If
		Next
	Next
	DeleteVector v3X
	DeleteVector v3Y
End Function



Function Navigate_World_With_MouseAndKeys(turnSpeed#=1, moveSpeed#=1)
	Local dY# = EntityPitch(GlobalCamera)+MouseYSpeed()/2*turnSpeed
	If dY > 89 Then dY = 89
	If dY < -89 Then dY = -89
	
	Local dz# = (KeyDown(200)-KeyDown(208)) * moveSpeed
	Local dx# = (KeyDown(205)-KeyDown(203)) * moveSpeed
	If dz <> 0 And dx <> 0 Then 
		dx=dx * 0.707
		dz=dz * 0.707
	End If
	RotateEntity GlobalCamera, dY, EntityYaw(GlobalCamera)-(MouseXSpeed()/2)*turnSpeed, 0
	MoveEntity GlobalCamera, dx, 0, dz
	MoveMouse GraphicsWidth()/2, GraphicsHeight()/2
	
	If KeyHit(4) Then 
		CameraZoom GlobalCamera, 4.0
	End If
	If KeyHit(5) Then
		CameraZoom GlobalCamera, 1.0
	End If
End Function



Function FrontFacing%(cam%, s%, t%, m%)	
	Local v1 = CreateVector(3)
	Local v2 = CreateVector(3)
	Local v3 = CreateVector(3)
	
	
	x# = VertexX(s, TriangleVertex(s, t, 0))
	y# = VertexY(s, TriangleVertex(s, t, 0))
	z# = VertexZ(s, TriangleVertex(s, t, 0))
	TFormPoint x, y, z, m, cam
	VctPut v1, 0, TFormedX()
	VctPut v1, 1, TFormedY()
	VctPut v1, 2, TFormedZ()
	
	x# = VertexX(s, TriangleVertex(s, t, 1))
	y# = VertexY(s, TriangleVertex(s, t, 1))
	z# = VertexZ(s, TriangleVertex(s, t, 1))
	TFormPoint x, y, z, m, cam
	VctPut v2, 0, TFormedX()
	VctPut v2, 1, TFormedY()
	VctPut v2, 2, TFormedZ()
	
	x# = VertexX(s, TriangleVertex(s, t, 2))
	y# = VertexY(s, TriangleVertex(s, t, 2))
	z# = VertexZ(s, TriangleVertex(s, t, 2))
	TFormPoint x, y, z, m, cam
	VctPut v3, 0, TFormedX()
	VctPut v3, 1, TFormedY()
	VctPut v3, 2, TFormedZ()
	
	
	Local vEdge1 = V3dSubtract(v3, v1)
	Local vEdge2 = V3dSubtract(v3, v2)
	
	Local vFNormal = V3dCross(vEdge1, vEdge2)
	
	Local bFrontFacing% = (V3dDot(v1, vFNormal) < 0)
	
	DeleteVector v1
	DeleteVector v2
	DeleteVector v3
	DeleteVector vEdge1
	DeleteVector vEdge2
	DeleteVector vFNormal
	
	Return bFrontFacing
End Function



Function V3dAdd(v1, v2)
	Local v3 = CreateVector(3)
	
	VctPut v3, 0, VctGet(v1,0) + VctGet(v2,0)
	VctPut v3, 1, VctGet(v1,1) + VctGet(v2,1)
	VctPut v3, 2, VctGet(v1,2) + VctGet(v2,2)
	
	Return v3
End Function


Function V3dSubtract(v1, v2)
	Local v3 = CreateVector(3)
	
	VctPut v3, 0, VctGet(v1,0) - VctGet(v2,0)
	VctPut v3, 1, VctGet(v1,1) - VctGet(v2,1)
	VctPut v3, 2, VctGet(v1,2) - VctGet(v2,2)
	
	Return v3
End Function


Function V3dCross(v1, v2)
	Local v3 = CreateVector(3)
	
	VctPut v3, 0, VctGet(v1,1)*VctGet(v2,2) - VctGet(v1,2)*VctGet(v2,1)
	VctPut v3, 1, VctGet(v1,2)*VctGet(v2,0) - VctGet(v1,0)*VctGet(v2,2)
	VctPut v3, 2, VctGet(v1,0)*VctGet(v2,1) - VctGet(v1,1)*VctGet(v2,0)
	
	Return v3
End Function


Function V3dDot#(v1, v2)
	Return VctGet(v1,0)*VctGet(v2,0) + VctGet(v1,1)*VctGet(v2,1) + VctGet(v1,2)*VctGet(v2,2)
End Function



Function VctGet#(v, i%)
	Return PeekFloat(v, i Shl 2)
End Function




Function VctPut(v, i%, val#)
	PokeFloat v, i Shl 2, val
End Function


Function CreateVector(nDim%)
	v% = CreateBank(nDim Shl 2)
	Return v
End Function


Function DeleteVector(v)
	FreeBank v
End Function





There is no way that drawing lines would be quick enough.


Yup, I think after experimenting I'm inclined to agree. However, I think that some more interesting effects might be achievable with line drawing. . . for example, try modifying the code above to pass in a value of 1 or 2 to the sketchy parameter in the DrawFrontFacingLines function to see what I mean. . .


So far I'm still not sure which way is best, but I'm starting to see the pros and cons of each. Also, I still have no idea how to solve the problem of deciding whether or not to draw lines based on whether an edge is hidden behind something or not.

Thanks people. . . keep the ideas coming :O)