Raytracing in Blitz3d - example code and image inside

Started by Matty, January 12, 2025, 14:54:20

Previous topic - Next topic

PixelOutlaw

#30
Excellent work! You even got the shadows and bloom working. :D

Funny timing, I just ordered all 5 volumes of Graphics Gems. (I prefer dead tree versions of books, I can read em on the can)
Software rendering still comes up from time to time.
For example if you're working on Arduino using uLisp or similar you still need to know how to paint an LCD etc.
Software rendering still has a place on systems which don't provide dedicated hardware.
Ubuntu MATE 20.04: i5-3570K CPU @ 3.40GHz, 8GB RAM, GeForce GTX 1060 3GB

One DEFUN to rule them all, One DEFUN to find them, One DEFUN to RETURN them all, and in the darkness MULTIPLE-VALUE-BIND them.

Matty

Thanks, here's another video this time with it in motion.

Re: shadows - shadows appear naturally during raytracing. However this is not real time and as I've said before - the same or better effects could be achieved in realtime with a more modern engine like unreal, unity or godot.

An explanation of the apparent glitch in the render: There's a black shadow that often appears on the left. It's not an error. The light source is simply outside the mesh, and the mesh is one sided, so it's calculating correctly but my error is that I've let the light source exit the bounds of the mesh. It wouldn't happen if the light source remained within the bounds of the mesh. It's because I simply position the light slightly to the left and rear of the camera and ignore the fact that this means sometimes it clips outside of the mesh itself.



Matty

Greetings folks....

Here is some sample code showing how to do an ambient occlusion render in Blitz3d (non realtime) as well as a picture using a free model found on cgtrader.com

Pseudocode / Algorithm:
For each pixel that is visible of a mesh, using a pick command, find its x/y/z coordinate and then fire off a tonne of linepicks a short distance in random directions and set the brightness of the pixel depending on how few of the picks hit anything...the less picks hit the brighter. 

There's no texture information in this model with this method - it is a useful technique to shade a model and you can apply it to a colored mesh render afterward with multiply blending to apply some nice shading.

Code: BASIC
;This is a sample program to show how to calculate ambient occlusion
;using a very simple method. It is not designed for realtime use.

Const ww = 1920 ;you can change this
Const hh = 1080 ;you can change this
Const numambientrays = 1000 ;you can change this, higher reduces the graininess
Const raylength# = 3.25 ;adjust this for your mesh/scene - the scale of your meshes will determine what this should be...could be smaller or larger

Graphics3D ww,hh,0,2
SetBuffer BackBuffer()

;load/create our scene and position our elements
camera = CreateCamera()
mesh = LoadMesh("girl.b3d")
TranslateEntity mesh,0,-MeshHeight(mesh)*0.5,MeshDepth(mesh)*3
CameraZoom camera,1.8
TranslateEntity camera,40,200,0
TurnEntity camera,20,0,0

EntityFX mesh,1 ;not really necessary...
EntityPickMode mesh,2 ;we need our scene elements to be pickable

ambientrender = CreateImage(ww,hh) ;the ambient occlusion render
colorrender = CreateImage(ww,hh) ;a simple blitz3d render of the model

RenderWorld 
CopyRect 0,0,ww,hh,0,0,BackBuffer(),ImageBuffer(colorrender)

;now let us do our ambient occlusion render

For x = 0 To ww - 1
	For y = 0 To hh - 1
		;for each pixel do the following:
		LockBuffer ImageBuffer(colorrender)
		SetBuffer ImageBuffer(colorrender)
		rgb = ReadPixelFast(x,y)
		rr = (rgb Shr 16) And 255
		gg = (rgb Shr 8) And 255
		bb = (rgb And 255)
		SetBuffer BackBuffer()
		UnlockBuffer ImageBuffer(colorrender)
		;if we're looking at black empty space then skip this x/y pixel
		If rr = 0 And gg = 0 And bb = 0 Then Goto skipthis
		CameraPick camera,x,y
		If PickedEntity()<>0 Then 
			;we've hit our object 
			xx# = PickedX()
			yy# = PickedY()
			zz# = PickedZ()
			;now let's fire out numambientrays of length 1 in random directions from this point...
			;and see if they hit anything...the fewer times they hit something
			;the brighter the pixel...the more times they hit something the darker
			numhit = 0
			For n = 1 To numambientrays
				dd# = 0
				its = 0
				While dd = 0 
					dx# = Rnd(-100,100)
					dy# = Rnd(-100,100)
					dz# = Rnd(-100,100)
					dd# = Sqr(dx*dx+dy*dy+dz*dz)
					its = its + 1
					If its > 10 Then Exit
				Wend 
				If dd<>0 Then 
					dx = raylength * dx / dd
					dy = raylength * dy / dd
					dz = raylength * dz / dd
					dd = raylength
					
					LinePick xx+dx*0.01,yy+dy*0.01,zz+dz*0.01,dx,dy,dz
					If PickedEntity()<>0 Then ;hit something
						numhit = numhit + 1
					EndIf
				EndIf 
			Next
			pcthit# = (Float(numhit) / Float(numambientrays)) / 0.5
			;we divide by 0.5 because for a flat surface half the rays
			;we should expect to end up colliding with a surface really....
			SetBuffer ImageBuffer(ambientrender)
			LockBuffer
			If pcthit < 1 Then
				c = 255.0 - Int(255.0 * pcthit)
				WritePixelFast x,y,c Shl 16 Or c Shl 8 Or c
			Else
				WritePixelFast x,y,0 Shl 16 Or 0 Shl 8 Or 0
			EndIf 
			UnlockBuffer
			SetBuffer BackBuffer()
		Else
			;empty space...make it black
			SetBuffer ImageBuffer(ambientrender)
			LockBuffer
			WritePixelFast x,y,0 Shl 16 Or 0 Shl 8 Or 0 
			UnlockBuffer
			SetBuffer BackBuffer()
			
		EndIf 	
		.skipthis
	Next
	If x Mod 5 = 0 Then ;this is just to show render progress
		If KeyHit(1) Then End 		
		Cls
		DrawImage ambientrender,0,0
		Text 0,0,x
		Text 0,15,"Press Esc to Quit Early"
		Flip
	EndIf 
Next
Cls
DrawImage ambientrender,0,0
;if you want...save the render...
SaveImage ambientrender,"ambient_occlusion_render.bmp"
Text 0,0,"Press a key to end"
Flip
WaitKey
End



Matty

One more...rendered/raytraced in Blitz3d (not realtime render)


Matty

Hello again,

I created an .obj loader for Blitz3d so that I could load the objects I use in my Conflict3049 game ( https://matty77.itch.io/conflict-3049 ) into Blitz3d and use them in Blitz without having to export everything again from milkshape as b3d files. The obj loader code is shown below.

I then created an export routine in my Conflict3049 game (press F5 to export) which exports a dump of the game's current objects and effects into a text file while you're playing, it looks a little like this: 


And then I created a scene loader in Blitz3d, using the objloader I wrote and the raytracer to then be able to fly around in the Conflict3049 raylib game scene and raytrace the scene which looks like this:

Note - because the render took 8 hours with my current code method to do this image it means I can't tell if the lighting is off or such until several hours in by which time I may as well just let it render out the whole frame.  The orange glowy circle thingies are unfinished effects that are areas where the world is lit by laser blasts, except I haven't rendered the laser bolts into the scene yet....this was done in blitz3d...I'll include that source code too....

Scene: (original render at 800x450, scaled up in paint.net for easier viewing)


objloader source code:
Code: BASIC
;very simple .obj loader suitable for loading .obj files
;that have a single surface for the whole mesh
;that means my game Conflict3049 I can load the obj files in that
;game into blitz. You'd have to make some changes to the loadobj
;function to be able to load more complex .obj files, this is 
;deliberately just a really simple .obj loader so I can load in
;my game 3d models into blitz.

;call loadobj(filename$) and it returns a mesh handle or 0 if no mesh created.
;you might need to make the meshes double sided with entityfx 16 since
;I'm not sure if I have the triangle vertex order correct.

Type obj_vert
	Field id
	Field x#,y#,z#
End Type

Type obj_uv
	Field id
	Field u#,v#
End Type

Type obj_tri
	Field vert_id0,uv_id0,vert_id1,uv_id1,vert_id2,uv_id2
End Type
Function loadobj(file$)
Delete Each obj_vert
Delete Each obj_uv
Delete Each obj_tri
;program for loading very simple obj files
;these obj files must have only 1 surface and 1 group
;it also doesn't load material files.
;so it's useful for very simple object meshes
;more complexity can be added later
infile = ReadFile(file$)
If infile<>0 Then 
	vid = 0
	uvid = 0
	triid = 0
	
	While(Not(Eof(infile)))
		val$ = ReadLine(infile)
		If Left(val,1) = "f" Then 
			txt$ = ""
			tri = 0
			
			objt.obj_tri = New obj_tri
			triid = 0
			For i = 3 To Len(val)
				txt = txt + Mid(val,i,1)
				If Mid(val,i,1) = " " Or i = Len(val) Then
					triid = triid + 1
					txt2$ = ""
					tri = tri + 1
					el = 0
					For j = 1 To Len(txt)
						txt2 = txt2 + Mid(txt,j,1)
						If Mid(txt,j,1) = "/" Then
							idtxt$ = Left(txt2,Len(txt2)-1)
							el = el + 1
							If el = 1 Then 
								;vert
								If triid = 1 Then
									objt\vert_id0 = Int(idtxt)
								EndIf
								If triid = 2 Then
									objt\vert_id1 = Int(idtxt)
								EndIf
								If triid = 3 Then
									objt\vert_id2 = Int(idtxt)
								EndIf 
							
							EndIf
							If el = 2
								;uv	
								If triid = 1 Then
									objt\uv_id0 = Int(idtxt)
								EndIf
								If triid = 2 Then
									objt\uv_id1 = Int(idtxt)
								EndIf
								If triid = 3 Then
									objt\uv_id2 = Int(idtxt)
								EndIf 
							
							EndIf 
							txt2 = ""
						EndIf
					Next
					txt = ""
				EndIf 
			Next
		EndIf 
		If Left(val,2) = "vt" Then
			uvid = uvid + 1
			objuv.obj_uv = New obj_uv
			txt$ = ""
			index = 0
			For i = 4 To Len(val)
				txt = txt + Mid(val,i,1)
				If Mid(val,i,1) = " " Then
					;u
					objuv\id = uvid
					txt = Left(txt,Len(txt)-1)
					objuv\u = Float(txt)
					txt = ""
				EndIf 
				If i = Len(val)
					objuv\v = Float(txt)
				EndIf 
			Next
		EndIf 
		If Left(val,2) = "v " Then
			vid = vid + 1
			objv.obj_vert = New obj_vert
			txt$ = ""
			index = 0
			For i = 3 To Len(val)
				txt = txt + Mid(val,i,1)
				If i = Len(val)
						vv# = Float(txt)
						objv\z = vv
				EndIf 
				If Mid(val,i,1) = " " Then
					txt = Left(txt,Len(txt)-1)
					vv# = Float(txt)
					txt = ""
					index = index + 1
					If index = 1 Then 
						objv\id = vid
						objv\x = vv
					EndIf
					If index = 2 Then
						objv\y = vv
					EndIf 
				EndIf 
			Next
		EndIf 
	Wend 
	CloseFile infile
Else
	Return 0
EndIf 

mesh = CreateMesh()
surf = CreateSurface(mesh)
For objt.obj_tri = Each obj_tri
	v0.obj_vert = Null
	v1.obj_vert = Null
	v2.obj_vert = Null
	uv0.obj_uv = Null
	uv1.obj_uv = Null
	uv2.obj_uv = Null
	For objv.obj_vert = Each obj_vert
		If objv\id = objt\vert_id0 Then
			v0 = objv
		EndIf
		If objv\id = objt\vert_id1 Then
			v1 = objv
		EndIf
		If objv\id = objt\vert_id2 Then
			v2 = objv
		EndIf
	Next

	For objuv.obj_uv = Each obj_uv
		If objuv\id = objt\uv_id0 Then
			uv0 = objuv
		EndIf 
		If objuv\id = objt\uv_id1 Then
			uv1 = objuv
		EndIf 
		If objuv\id = objt\uv_id2 Then
			uv2 = objuv
		EndIf 
	Next
		sv0 = AddVertex(surf,v0\x,v0\y,v0\z,uv0\u,1.0-uv0\v)
		sv1 = AddVertex(surf,v1\x,v1\y,v1\z,uv1\u,1.0-uv1\v)
		sv2 = AddVertex(surf,v2\x,v2\y,v2\z,uv2\u,1.0-uv2\v)
		AddTriangle surf,sv0,sv1,sv2
		c = c + 1
Next
UpdateNormals mesh
Return mesh
End Function 


And the code for loading the scene into blitz3d and displaying, rendering it:

Code: BASIC
AppTitle "Raytrace Scene From Conflict 3049"
Const cpucount = 1
;cpuindex = Int(Input("CpuIndex:0-2:"))
;If cpuindex<0 Then cpuindex = 0
;If cpuindex>3 Then cpuindex = 3
Const ww = 800
Const hh = 450
Const windowed = 2
Graphics3D ww,hh,0,windowed
Const dodiffuse = 1;1;1
Const dospecular = 1;1;1
Global doambient = 1;1
Const dolinepick = 1;1;1;0;1
Const fudgediffuse# = 6.0
Const fudgespecular# = 5.0
Const picktolerance# = 0.15
Global lastframetime = 0
Global stepx = 5
Global stepy = 5
Const doscatter = 0
Global frame = 0
Global camera = CreateCamera()
CameraClsColor camera,8,8,16

SetBuffer BackBuffer()
Include "objloader.bb"

Type rlight
	Field x#,y#,z#
	Field r,g,b
	Field radius#
	Field noshadow
End Type

;a fill light above the scene and far away
For i = 1 To 4
rl.rlight = New rlight
rl\x = 500.0 * Cos(i * 90)
rl\y = 1000
rl\z = 500.0 * Sin(i * 90)
rl\r = 18500.0
rl\g = 18500.0
rl\b = 18500.0
rl\radius = 10000.0
Next

Type objectlist
	Field ent
	Field tex
	Field spectex
	Field objfile$
	Field texfile$
	Field specfile$
	Field x#,y#,z#
	Field yaw#
	Field scx#,scy#,scz#
	Field minx#,maxx#,miny#,maxy#
End Type

PositionEntity camera,0,50,0

AmbientLight 0,0,0

light = CreateLight(1)
AlignToVector light,-1,-1,-1,3,1
light = CreateLight(1)
AlignToVector light,1,-1,-1,3,1
light = CreateLight(1)
AlignToVector light,1,-1,1,3,1
light = CreateLight(1)
AlignToVector light,-1,-1,1,3,1

loadscene()
zoom# = 1

infile = ReadFile("camsettings.txt")
If infile<>0 Then
	xxx# = ReadLine(infile)
	yyy# = ReadLine(infile)
	zzz# = ReadLine(infile)
	pit# = ReadLine(infile)
	yaw# = ReadLine(infile)
	rol# = ReadLine(infile)
	xx# = ReadLine(infile)
	zz# = ReadLine(infile)
	zoom# = ReadLine(infile)
	kp = ReadLine(infile)
	PositionEntity camera,xxx,yyy,zzz
	RotateEntity camera,pit,yaw,rol
	CloseFile infile
EndIf 	
FlushKeys
Repeat
	Cls
	If KeyDown(200) Then MoveEntity camera,0,0,5
	If KeyDown(208) Then MoveEntity camera,0,0,-5
	If KeyDown(203) Then TurnEntity camera,0,3,0
	If KeyDown(205) Then TurnEntity camera,0,-3,0	
	If KeyDown(30) Then TranslateEntity camera,0,1,0
	If KeyDown(44) Then TranslateEntity camera,0,-1,0
	If KeyDown(16) Then zz# = zz# - 0.01:kp=1
	If KeyDown(17) Then zz# = zz# + 0.01:kp=1
	If KeyDown(18) Then xx# = xx# - 0.01:kp=1
	If KeyDown(19) Then xx# = xx# + 0.01:kp=1
	If KeyDown(20) Then zoom# = zoom# + 0.05
	If KeyDown(21) Then zoom# = zoom# - 0.05

	If zoom < 0.5 Then zoom = 0.5
	
;	If kp<>0 Then 
		TFormVector xx,0,zz,camera,0
		AlignToVector camera,TFormedX(),5,TFormedZ(),2,1
; EndIf 
	CameraZoom camera,zoom
	setbounds()	
	RenderWorld
	If KeyHit(57) Then ;full res
		lastframetime = MilliSecs()
		stepx = 1
		stepy = 1
		raytracescene()
	EndIf 
	If KeyHit(2) Then ;extremely low res
		lastframetime = MilliSecs()
		stepx = 5
		stepy = 5
		raytracescene()
	EndIf 
	If KeyHit(3) Then ;really really low res
		lastframetime = MilliSecs()
		stepx = 10
		stepy = 10
		raytracescene()
	EndIf 
	If KeyHit(4) Then ;really really low res so low in fact I can't even remember their name
		lastframetime = MilliSecs()
		stepx = 15
		stepy = 15
		raytracescene()
	EndIf 

	Flip True
Until KeyHit(1)
outfile = WriteFile("camsettings.txt")
WriteLine outfile,EntityX(camera)
WriteLine outfile,EntityY(camera)
WriteLine outfile,EntityZ(camera)
WriteLine outfile,EntityPitch(camera)
WriteLine outfile,EntityYaw(camera)
WriteLine outfile,EntityRoll(camera)
WriteLine outfile,xx
WriteLine outfile,zz
WriteLine outfile,zoom
WriteLine outfile,kp
CloseFile outfile
End

Function loadscene()
	infile = ReadFile("frame_objects_list.txt")
	If infile<>0 Then
		While(Not(Eof(infile)))
			val$ = ReadLine(infile)
			If val = "LASERBEGIN" Then ;haven't done these yet
				xx# = ReadLine(infile)
				yy# = ReadLine(infile)
				zz# = ReadLine(infile)
				dx# = ReadLine(infile)
				dy# = ReadLine(infile)
				dz# = ReadLine(infile)
				rl.rlight = New rlight
				rl\x = xx
				rl\y = yy
				rl\z = zz
				rl\r = 5000
				rl\g = 2000
				rl\b = 1000
				rl\radius = 25
				rl\noshadow = 1
				val = ReadLine(infile)
				If val<>"LASEREND" Then RuntimeError("Borked File!")			
			EndIf
			If val = "EXPLOSIONBEGIN" Then ;or these
			
			EndIf
			If val = "SMOKEBEGIN" Then ;or these
			
			EndIf
			If val = "SPARKBEGIN" Then ;or these either
			
			EndIf 
			If val = "CAMERABEGIN" Then
				xx# = ReadLine(infile)
				yy# = ReadLine(infile)
				zz# = ReadLine(infile)
				tx# = ReadLine(infile)
				ty# = ReadLine(infile)
				tz# = ReadLine(infile)
				pivot = CreatePivot()
				PositionEntity pivot,tx,0,tz
				PositionEntity camera,xx,yy,zz
				PointEntity camera,pivot
				CameraZoom camera,1
				FreeEntity pivot
				val$ = ReadLine(infile)
				If val<>"CAMERAEND" Then
					RuntimeError "Borked File!"
				EndIf 
			EndIf 
			If val = "BEGIN" Then 
				obj.objectlist = New objectlist
				obj\objfile = "c:\mattiesgames\conflict3049_dev\"+ReadLine(infile)
				obj\texfile = "c:\mattiesgames\conflict3049_dev\"+ReadLine(infile)
				obj\specfile = "c:\mattiesgames\conflict3049_dev\"+ReadLine(infile)
				If Instr(obj\objfile,"tree01aclump.b3d") Then
					q = Rand(1,3)
					If q = 2 Then
						obj\objfile = Replace(obj\objfile,"tree01","tree02")
					EndIf
					If q = 3 Then
						obj\objfile = Replace(obj\objfile,"tree01","tree03")
					EndIf 
				EndIf 	
				obj\x = ReadLine(infile)
				obj\y = ReadLine(infile)
				obj\z = ReadLine(infile)
				obj\yaw = Float(ReadLine(infile))*180.0 / 3.14159
				obj\scx = ReadLine(infile)
				obj\scy = ReadLine(infile)
				obj\scz = ReadLine(infile)
				icount = icount + 1
				val$ = ReadLine(infile)
				If val<>"END" Then RuntimeError("borked file!")


			EndIf
		Wend  
		CloseFile infile
	EndIf 
	For obj.objectlist = Each objectlist
		If Instr(obj\objfile,"crater")>0 Then Delete obj
	Next
	For obj.objectlist = Each objectlist
		found = 0
		icount = icount - 1
		If icount Mod 10 = 0 Then 
			Cls
			Text 0,0,icount
			Flip
		EndIf 
		For tmp.objectlist = Each objectlist
			If tmp\ent<>0 And tmp\objfile=obj\objfile Then 
				count = count + 1
				If count > 100 Then 
					count = 0
					obj\ent = loadobj(obj\objfile)	
					If Instr(obj\texfile,"atlasfoliage")>0 Then 
					obj\tex = LoadTexture(obj\texfile,1+2)
					obj\spectex = LoadTexture(obj\spectex,1+2)
					Else
					obj\tex = LoadTexture(obj\texfile,1)
					obj\spectex = LoadTexture(obj\spectex,1)
					
					EndIf 
					
				Else
					obj\tex = tmp\tex
					obj\spectex = tmp\spectex
					obj\ent = CopyEntity(tmp\ent)
				EndIf 
				If obj\tex<>0 And obj\ent<>0 Then 
					EntityTexture obj\ent,obj\tex
				EndIf 
				found = 1
				Exit
			EndIf 
		Next
		If found = 0 Then 
				obj\ent = loadobj(obj\objfile)
				
					If Instr(obj\texfile,"atlasfoliage")>0 Then 
					obj\tex = LoadTexture(obj\texfile,1+2)
					obj\spectex = LoadTexture(obj\spectex,1+2)
					Else
					obj\tex = LoadTexture(obj\texfile,1)
					obj\spectex = LoadTexture(obj\spectex,1)
					
					EndIf 
				
				If obj\tex<>0 And obj\ent<>0 Then 
					EntityTexture obj\ent,obj\tex
				EndIf 
		EndIf 
		PositionEntity obj\ent,obj\x*1.0,obj\y*1.0,obj\z*1.0
		RotateEntity obj\ent,0,obj\yaw,0
		ScaleEntity obj\ent,obj\scx,obj\scy,obj\scz
		EntityPickMode obj\ent,2
	Next
	
	ground = CreatePlane()
	groundtex = LoadTexture("c:\mattiesgames\conflict3049_dev\media\ground\altdiffuse1.png",1)
	EntityTexture ground,groundtex
	ScaleEntity ground,30,1,30
	EntityPickMode ground,2
End Function 

Function raytracescene()
Local screen[1920*1080]
;backbuffer contains the base unlit (full bright) scene

baserender = CreateImage(ww,hh)
finalrender = CreateImage(ww,hh)
CopyRect 0,0,ww,hh,0,0,BackBuffer(),ImageBuffer(baserender)
LockBuffer ImageBuffer(baserender)
LockBuffer ImageBuffer(finalrender)

;add our diffuse and specular to the finalrender for each light
rgbbk = 8 Shl 16 Or 8 Shl 8 Or 16
camx# = EntityX(camera)
camy# = EntityY(camera)
camz# = EntityZ(camera)
		
setbounds()
	
x = 0
quit = 0
While x < ww		
;For x = 0 To ww - 1 Step 1
	y = 0
	If quit = 1 Then Exit
	While y < hh
		If quit = 1 Then Exit 
;	For y = 0 To hh - 1 Step 1 ;debugging
		ox = x
		oy = y
		If doscatter<>0 Then 
			x = Rand(0,ww-1)
			y = Rand(0,hh-1)
			x = x - x Mod stepx
			y = y - y Mod stepy
			its = 0
			While screen
 = 1 
				x = x + stepx
				If x >= ww Then 
					x = 0
					y = y + stepy
					If y >= hh Then
						y = 0
						its = its + 1
						If its > 1 Then Exit 
					EndIf 
				EndIf 
			Wend 
			screen = 1
		EndIf 
		For obj.objectlist = Each objectlist
			If x >= obj\minx And x <= obj\maxx And y >= obj\miny And y <= obj\maxy Then
				ShowEntity obj\ent
			Else
				HideEntity obj\ent
			EndIf 
		Next
;		For mo.mobj = Each mobj
;			CameraProject camera,EntityX(mo\ent,True),EntityY(mo\ent,True),EntityZ(mo\ent,True)
;			If Abs(ProjectedX()-x)<ww/7 And Abs(ProjectedY()-y)<hh/6 Then ShowEntity mo\ent Else HideEntity mo\ent
;;			If EntityDistance(camera,mo\ent)>350 Then HideEntity mo\ent
;			If ProjectedZ()<0 Then HideEntity mo\ent
;			If ProjectedX()<-20 Or ProjectedY()<-20 Or ProjectedX()>ww+20 Or ProjectedY()>hh+20 Then HideEntity mo\ent
			
;		Next
;		For pvx = 0 To 10
	;		For pvz = 0 To 10
		;		CameraProject camera,EntityX(pivotarray(pvx,pvz)),EntityY(pivotarray(pvx,pvz)),EntityZ(pivotarray(pvx,pvz))
			;	If Abs(ProjectedX()-x)<Float(ww)*3.0 And Abs(ProjectedY()-y)<Float(hh)*3.0 Then
				;	ShowEntity pivotarray(pvx,pvz)
;				Else
	;				HideEntity pivotarray(pvx,pvz)
		;		EndIf
			;Next
	;	Next
		CameraPick camera,x,y
		If PickedEntity()<>0 Then 
			xx# = PickedX()
			yy# = PickedY()
			zz# = PickedZ()
			nx# = PickedNX()
			ny# = PickedNY()
			nz# = PickedNZ()
			rr# = 0
			gg# = 0
			bb# = 0
			;add our diffuse components for each light
			For rl.rlight = Each rlight
				If KeyHit(1) Then quit=1
				If quit = 1 Then Exit 
				lx# = rl\x
				ly# = rl\y
				lz# = rl\z
				;check distance to point
				ll# = ((lx-xx)*(lx-xx)+(ly-yy)*(ly-yy)+(lz-zz)*(lz-zz))
				If ll <= rl\radius * rl\radius ;And camdd < 350 Then 
					;inside light range
					;check if visible to light
					If dolinepick>0 And rl\noshadow = 0 Then LinePick lx,ly,lz,xx-lx,yy-ly,zz-lz,picktolerance*0.01
					If PickedEntity()<>0 Or dolinepick<=0 Or rl\noshadow = 1 Then 
						;
						dx# = PickedX() - xx
						dy# = PickedY() - yy
						dz# = PickedZ() - zz
						dd# = (dx*dx+dy*dy+dz*dz)
						If dd < picktolerance*picktolerance Or dolinepick<=0 Or rl\noshadow = 1 Then 
							;not obstructed by an object so now do dot product
							dlx# = lx - xx
							dly# = ly - yy
							dlz# = lz - zz
							ddd# = Sqr(dlx*dlx+dly*dly+dlz*dlz)
							If ddd> 0 And dodiffuse > 0 Then ;it should
								dlx = dlx / ddd
								dly = dly / ddd
								dlz = dlz / ddd
								dot# = dlx * nx + dly * ny + dlz * nz
							;if our normal dotted with our light direction vector from the surface
							;is less than zero it's in shadow...else 
								If dot > 0 Then 
									;this is our diffuse lighting brightness * light brightness 
									;but we need to factor in our range..which is 20 units in this case...
									dot = dot / ddd
								EndIf
								dot = dot * fudgediffuse
								rr = rr + dot * Float(rl\r)
								gg = gg + dot * Float(rl\g)
								bb = bb + dot * Float(rl\b)
							EndIf
							;now let's do our specular component....
							;we're going to cheat a little bit....
							;we're NOT going to use our specular textures....
							;we're just going to assume a uniform specularity....
							;I know it's wrong, but it should be okay....
							dx# = camx - rl\x
							dy# = camy - rl\y
							dz# = camz - rl\z
							ddd# = Sqr(dx*dx+dy*dy+dz*dz)
							lvx# = rl\x - xx
							lvy# = rl\y - yy
							lvz# = rl\z - zz
							lvx0# = lvx
							lvy0# = lvy
							lvz0# = lvz
							ll# = Sqr(lvx*lvx + lvy*lvy + lvz*lvz)
							If dospecular>0 Then 
								bright# = (Float(rl\g+rl\b+rl\r)*0.333) / ll
								dot# = (lvx/ll) * nx + (lvy/ll) * ny + (lvz/ll) * nz
								If dot > 0 Then 
									cx# = camx - xx
									cy# = camy - yy
									cz# = camz - zz
									cc# = Sqr(cx*cx+cy*cy+cz*cz)
									If cc>0 ;which it will be
										cnx# = cx/cc
										cny# = cy/cc
										cnz# = cz/cc
										dot# = 0.0
										If ll>0 Then ;which it should be
											px# = lvx0*2.0 - ll * nx
											py# = lvy0*2.0 - ll * ny
											pz# = lvz0*2.0 - ll * nz
											pdd# = Sqr(px*px+py*py+pz*pz)
											If pdd>0 Then 
												px = px/pdd
												py = py/pdd
												pz = pz/pdd
												dot = px * cnx + py * cny + pz * cnz
											EndIf 
											;reflection vector, normalised
											;camera vector, normalised
											;find dot product to get angle
											;dot# = -1.0 * dot
											If dot > 0 Then ;should be between 0 and 1 for dot
												dot = dot ^ 20.0
												dot = dot / ddd
												dot = dot * fudgespecular
												rr = rr + dot * Float(rl\r)
												gg = gg + dot * Float(rl\g)
												bb = bb + dot * Float(rl\b)
											EndIf
										EndIf
									EndIf
								EndIf
							EndIf
						EndIf
					EndIf
				EndIf
			Next
			;now we need to do the ambient occlusion.....
			numhit = 0
			numambientrays = 120
			raylength# = 1.0
			odoambient = doambient
			;If camdd > 50 Or (rr+gg+bb)<20 Then doambient = 0
			If doambient <=0 Then numambientrays = 0
			For n = 1 To numambientrays
				dd# = 0
				its = 0
				While dd = 0 
					dx# = Rnd(-100,100)
					dy# = Rnd(-100,100)
					dz# = Rnd(-100,100)
					dd# = Sqr(dx*dx+dy*dy+dz*dz)
					its = its + 1
					If its > 10 Then Exit
				Wend 
				If dd<>0 Then 
					dx = raylength * dx / dd
					dy = raylength * dy / dd
					dz = raylength * dz / dd
					dd = raylength
					LinePick xx+dx*0.01,yy+dy*0.01,zz+dz*0.01,dx,dy,dz
					If PickedEntity()<>0 Then ;hit something
						numhit = numhit + 1
					EndIf
				EndIf 
			Next
			If numambientrays > 0 Then 
				pcthit# = (Float(numhit) / Float(numambientrays)) / 0.5
				;we divide by 0.5 because for a flat surface half the rays
				;we should expect to end up colliding with a surface really....
				If pcthit < 1 Then
					cc# = 255.0 - Int(255.0 * pcthit)
				Else
					cc# = 0
				EndIf
				If dodiffuse = 0 And dospecular = 0 Then 
					rr = 255
					gg = 255
					bb = 255
				EndIf 
				rr = rr * cc / 255.0
				gg = gg * cc / 255.0
				bb = bb * cc / 255.0
			EndIf 
			doambient = odoambient
			rr0# = rr
			gg0# = gg
			bb0# = bb
			x0 = x
			While x0 < x + stepx
				y0 = y
				While y0 < y + stepy
					If x0>=0 And y0>=0 And x0<ww And y0<hh Then rgbbase = ReadPixelFast(x0,y0,ImageBuffer(baserender))
					rr = rr0#
					gg = gg0#
					bb = bb0#
					r1 = (rgbbase Shr 16) And 255
					g1 = (rgbbase Shr 8) And 255
					b1 = (rgbbase And 255)
					rr = rr * Float(r1) / 255.0
					gg = gg * Float(g1) / 255.0
					bb = bb * Float(b1) / 255.0
					If rr > 255 Then rr = 255
					If rr < 0 Then rr = 0
					If gg > 255 Then gg = 255
					If gg < 0 Then gg = 0
					If bb > 255 Then bb = 255
					If bb < 0 Then bb = 0
					If x0>=0 And y0>=0 And x0<ww And y0<hh Then WritePixelFast x0,y0,Int(rr) Shl 16 Or Int(gg) Shl 8 Or Int(bb),ImageBuffer(finalrender)
					y0 = y0 + 1
			Wend
			x0 = x0 + 1
		Wend
		Else
			;paint the night sky
			x0 = x
			While x0<x+stepx
				y0 = y
				While y0<y + stepy
					If x0 < ww And y0 < hh And x0 >=0 And y0>=0 Then WritePixelFast x0,y0,ReadPixelFast(x0,y0,ImageBuffer(baserender)),ImageBuffer(finalrender)
;					If x0 < ww And y0 < hh And x0 >=0 And y0>=0 Then WritePixelFast x0,y0,0,ImageBuffer(finalrender)
					y0 = y0 + 1
				Wend
				x0 = x0 + 1
			Wend
		EndIf 
		If y Mod 50 = 0 Then 
			UnlockBuffer ImageBuffer(finalrender)
			If KeyHit(1) Then quit=1
			If quit =1  Then Exit
			DrawBlock finalrender,0,0
			Text 0,0,frame + " " + y + " " + x
			ltime = Abs(MilliSecs()-lastframetime)
			Color 255,255,255
			Text 0,30,"Render Time:"+Str(ltime/1000)
			Flip
			LockBuffer ImageBuffer(finalrender)
		EndIf 
		y = oy
		y = y + stepy
	Wend	
	;we will draw to the screen our finalrender
	;every x value
	;and put an indicator of where we're up to
	UnlockBuffer ImageBuffer(finalrender)
	If KeyHit(1) Then quit=1
	If quit = 1 Then Exit
	DrawBlock finalrender,0,0
	Text 0,0,frame + " " + x
	ltime = Abs(MilliSecs()-lastframetime)
	Color 255,255,255
	Text 0,30,"Render Time:"+Str(ltime/1000)
	Flip
	LockBuffer ImageBuffer(finalrender)
	x = ox
	x = x + stepx
Wend
UnlockBuffer ImageBuffer(finalrender)
UnlockBuffer ImageBuffer(baserender)
SaveImage finalrender,"bmp\scene_"+zstr(frame)+".bmp"
FreeImage baserender
FreeImage finalrender
;For mo.mobj = Each mobj
;	ShowEntity mo\ent
;Next	
For obj.objectlist = Each objectlist
	ShowEntity obj\ent
Next
FlushKeys
End Function
Function zstr$(i)
txt$ = Str(i)
While Len(txt)<8
	txt = "0" + txt
Wend
Return txt
End Function 
Function setbounds()
;set the object screen boundaries....
For obj.objectlist = Each objectlist
	xx# = Sqr(MeshWidth(obj\ent)*MeshWidth(obj\ent) + MeshDepth(obj\ent)*MeshDepth(obj\ent))
	If obj\scx > obj\scz Then 
		xx = xx * obj\scx
	Else
		xx = xx * obj\scz
	EndIf 
	xx = xx * 2.0
	yy# = MeshHeight(obj\ent) * obj\scy
	yy = yy * 2.0
	;we scale it up by three to allow for shadows to fall on surrounding
	;objects...3 should be large enough to allow for this....
	
	CameraProject camera,EntityX(obj\ent)-xx,EntityY(obj\ent)+yy,EntityZ(obj\ent)-xx
	minx# = ProjectedX()
	miny# = ProjectedY()
	CameraProject camera,EntityX(obj\ent)+xx,EntityY(obj\ent)-yy,EntityZ(obj\ent)+xx
	maxx# = ProjectedX()
	maxy# = ProjectedY()
	If minx > maxx Then 
		tmpx# = minx
		minx = maxx
		maxx = tmpx
	EndIf 	
	If miny > maxy Then
		tmpx# = miny
		miny = maxy
		maxy = tmpy
	EndIf 
	obj\minx = minx
	obj\maxx = maxx
	obj\miny = miny
	obj\maxy = maxy
	ShowEntity obj\ent
	If obj\minx < 0 And obj\maxx < 0 Then HideEntity obj\ent
	If obj\maxx > ww And obj\minx > ww Then HideEntity obj\ent
	If obj\miny < 0 And obj\maxy < 0 Then HideEntity obj\ent
	If obj\miny > hh And obj\maxy > hh Then HideEntity obj\ent
Next		
End Function 

A full game scene text file is about 700kb in size.

Matty

This is NOT raytraced, but just a standard blitz3d render with vertex lighting, nothing fancy, it just is a bunch of meshes, some animation, some camera fly through, and a bit of fog. But it's fun to mess around with... :-)


Matty

Another quick render from blitz3d using blitz's standard vertex lighting...nothing complicated here, just vanilla blitz3d doing its thing: