Vector graphics - how to make the lines 'glow'?

Blitz3D Forums/Blitz3D Programming/Vector graphics - how to make the lines 'glow'?

I'm currently dabbling with the source code to a game coded by someone else, namely 'Vecteroids' (2D game) by Jim Pishlo.

I'm curious if it's possible to make the vector lines 'glow' and look as smooth as possible as they would
on an original vector monitor.

Naturally the monitors we are using on our PCs (ie raster) operate very differently to a true vector monitor, but even so, with todays' 2D/3D graphics cards and programming techniques there must be a way to simulate the smooth, bright and *glowing* lines that you'd normally see on a vector monitor.

Please note: I'm a relative beginner at this. I could have posted this in the beginners' area but I thought more people would see it here. :)


Thanks

A vector monitor? I don't think such a thing exists, except for the ones to trace waveforms, which are simply a light-beam moving over phosphor..

which is exactly what a CRT is, except at much higher resolution..

Okay then, an XY monitor to be precise. :)

They do exist, just not for use with PCs (I do though have one right behind me ....... in my Asteroids cab :-)

The effect would probably be easier to recreate using B3D and drawing the lines with polyons. The 'glow' could be drawn into the texture. Also the texture could be rendered in 'additive' mode.

Like this :)

Personally I would use a single surface system instead of sprites and use Tris instead of whole quads. This is just a quick one I knocked up for you.

[EDIT]Or Use this texture for the line : [/EDIT]

Graphics3D 640,480,32,2


LineTex = CreateTexture(32,32)
SetBuffer TextureBuffer(LineTex)
Line 16,0,16,32
Line 17,0,17,32
SetBuffer BackBuffer()


cam = CreateCamera()


pivot = CreatePivot()
PositionEntity pivot,0,0,20

LineSprite1 = CreateSprite(Pivot)
EntityTexture LineSprite1,LineTex
ScaleSprite Linesprite1,1,2
EntityBlend LineSprite1,3
RotateSprite LineSprite1,-20
SpriteViewMode LineSprite1,2
PositionEntity LineSprite1,-.7,0,0
EntityColor LineSprite1,0,255,255

LineSprite2 = CopyEntity(LineSprite1,pivot)
PositionEntity Linesprite2,.7,0,0
RotateSprite LineSprite2,20


LineSprite3 = CopyEntity(LineSprite1,pivot)
RotateSprite LineSprite3,100
ScaleSprite LineSprite3,1,0.75
PositionEntity LineSprite3,-0.60,-1.8,0

LineSprite4 = CopyEntity(LineSprite1,pivot)
RotateSprite LineSprite4,80
ScaleSprite LineSprite4,1,0.75
PositionEntity LineSprite4,0.75,-1.8,0

While Not KeyHit(1)

    TurnEntity pivot,0,0,1
	MoveEntity pivot,0,0,(Sin(MilliSecs()/2))
	
	RenderWorld
	Flip

Wend


Indiepath.T - thanks very much for that, looks really nice. :-)

Is it possible to make it look even 'brighter'?

Also, given the ignorance of this here beginner, how would I go about using the texture you provided for the line?


Many thanks

Save the Texture as line.png and put it into the same directory as the code.

This is about as bright as you're gonna get without using some kind of filter. It would look cool with a bloom filter :)

Use the following code to utilise the texture :-

Graphics3D 640,480,32,2


LineTex = LoadTexture("line.png",2)

cam = CreateCamera()


pivot = CreatePivot()
PositionEntity pivot,0,0,30

LineSprite1 = CreateSprite(Pivot)
EntityTexture LineSprite1,LineTex
ScaleSprite Linesprite1,1,2
EntityBlend LineSprite1,3
RotateSprite LineSprite1,-20
SpriteViewMode LineSprite1,2
PositionEntity LineSprite1,-.7,0,0
EntityColor linesprite1,0,255,255
EntityFX linesprite1,1

LineSprite2 = CopyEntity(LineSprite1,pivot)
PositionEntity Linesprite2,.7,0,0
RotateSprite LineSprite2,20


LineSprite3 = CopyEntity(LineSprite1,pivot)
RotateSprite LineSprite3,100
ScaleSprite LineSprite3,1,0.75
PositionEntity LineSprite3,-0.60,-1.8,0

LineSprite4 = CopyEntity(LineSprite1,pivot)
RotateSprite LineSprite4,80
ScaleSprite LineSprite4,1,0.75
PositionEntity LineSprite4,0.75,-1.8,0

While Not KeyHit(1)

    TurnEntity pivot,0,0,1
	;MoveEntity pivot,0,0,(Sin(MilliSecs()/2))
	
	RenderWorld
	Flip

Wend


Now that's looking very nice. Thanks very much for all your help so far. :-)

Of course, now you've mentioned that it would look even better with a bloom filter, so I just HAVE to ask the inevitable question - how would a bloom filter be added? :)

And would it make the lines look brighter/more intense?

Here is the bloom code - thanks to Bouncer.

;//////////////////////////////////////////////////////////////////////////////
;//	Bouncers Bloom Filter
;//////////////////////////////////////////////////////////////////////////////
;Bloom / Glow post-process filter by Bouncer
;Using SSwift's excellent texture blurring routine
;Use at your own risk :) may contain bugs 
;Warning ... this filter uses LOT of fillrate so it's not for the old gfx-cards 

;V1.2
;Fixed camera bug...

;---------------------------------------------------
;GLOW VARIABLES
Global GL_cam
Global GL_scenecam					;This stores the pointer to your scene camera
Global GL_offset = 65536			;Offset for the glow camera
Global GL_texsize = 256			;Glow texture resolution (128 or 256 recommended)
Global GL_camzoom# = 1.0		;Change this value if your camzoom differs from the default

Global GL_sprite
Global GL_tex,GL_texbuffer

Global GL_sprite_scene
Global GL_timer=1

;SSwifts blur routine ---------------------
; This is the location in the world where the camera will reside.
; This location should be far from any other geometry you have in your world.
Const BLUR_CAM_X# = 65536.0
Const BLUR_CAM_Y# = 65536.0
Const BLUR_CAM_Z# = 0.0

;Modified to additive blend in the same time as blur.
;Also modified the copyrecting... so that multiple passes don't cause texture drifting.
;this is just a quick fix... don't actually get why tex texture drifts...
Function BlurTexture(Texture, Blur_Quality, Blur_Radius#)

; -------------------------------------------------------------------------------------------------------------------
; This function blurs a texture using a technique that takes advantage of 3D acceleration.  
;
; * You MUST hide all other cameras before calling this function!
; * You MUST reset your texture's blending mode, scale, and position after calling this function!
;
; Texture is the texture you want blurred.
;
; Blur_Quality defines the quality of the blur.  1 = 4 passes, 2 = 8 passes, 3 = 12 passes, etc.
;
; (The reason that the passes are in multiples of four is because interference artifacts are created when
; the number of passes is not a multiple of four... meaning that ten passes will actually look far worse
; than eight.)
;
; Blur_Radius# defines the radius of the blur, in pixels, assuming a map size of 256x256.
;
;(Ie, a radius of 16 will be the same width regardless of whether the texture is 16x16 or 512x512.  It will
; only be exactly 16 pixels wide if the map is 256x256.)
; -------------------------------------------------------------------------------------------------------------------

; This is used for temporary storage of the meshes used for soft shadow blurring.
Local BlurMesh[16*4]


; If blurring is enabled...
If Blur_Quality > 0

Blur_Cam = CreateCamera()

; Set the camera's range to be very small so as to reduce the possiblity of extra objects making it into the scene.
CameraRange Blur_Cam, 0.1, 100

; Set the camera to zoom in on the object to reduce perspective error from the object being too close to the camera.
CameraZoom Blur_Cam, 16.0

; Aim camera straight down.
RotateEntity Blur_Cam, 90, 0, 0, True

; Set the camera viewport to the same size as the texture.
CameraViewport Blur_Cam, 0, 0, TextureWidth(Texture), TextureHeight(Texture)

; Set the camera so it clears the color buffer before rendering the texture.
CameraClsColor Blur_Cam, 0,0,0
CameraClsMode  Blur_Cam, True, True

; Position the blur camera far from other entities in the world.
PositionEntity Blur_Cam, BLUR_CAM_X#, BLUR_CAM_Y#, BLUR_CAM_Z#

; Create the sprites to use for blurring the shadow maps.
For Loop = 0 To (Blur_Quality*4)-1
BlurMesh[Loop] = CreateSprite()
EntityBlend BlurMesh[Loop],3
Next

; Scale the texture down because we scale the sprites up so they fill a larger area of the
; screen.  (Otherwise the edges of the texture are darker than the middle because they don't
; get covered.
ScaleTexture    Texture, 0.5, 0.5
PositionTexture Texture, 0.5, 0.5

; Blur texture by blitting semi-transparent copies of it on top of it.
BlurRadius# = Blur_Radius# * (1.0 / 256.0)
BlurAngleStep# = 360.0 / Float(Blur_Quality*4)

; Normally we would just divide 255 by the number of passes so that adding all the passes
; together would not exceed 256.  However, if we did that, then we could not have a number of
; passes which does not divide 256 evenly, or else the error would result in the white part of
; the image being slightly less than white.  So we round partial values up to ensure that
; white will always be white, even if it ends up being a little whiter than white as a result
; when all the colors are added, since going higher than white just clamps to white.
BlurShade = Ceil(255.0 / Float(Blur_Quality*4))

; Place each of the blur objects around a circle of radius blur_radius.
For Loop = 0 To (Blur_Quality*4)-1

EntityTexture BlurMesh[Loop], Texture
EntityFX BlurMesh[Loop], 1+8
EntityAlpha BlurMesh[Loop], 1.0 / Float(Loop+1)
ScaleSprite BlurMesh[Loop], 2, 2

BlurAngle# = BlurAngleStep# * Float(Loop) + 180.0*(Loop Mod 2)

Xoff# = BlurRadius# * Cos(BlurAngle#)
Yoff# = BlurRadius# * Sin(BlurAngle#)

PositionEntity BlurMesh[Loop], BLUR_CAM_X# + Xoff#, BLUR_CAM_Y# - 16.0, BLUR_CAM_Z# + Yoff#, True

Next

; Render the new texture.
RenderWorld

; Copy the new texture from the screen buffer to the texture buffer.
CopyRect 1, 1, TextureWidth(Texture)-1, TextureHeight(Texture)-1, 0, 0, BackBuffer(), TextureBuffer(Texture)

; Free the blur entities.
For Loop = 0 To (Blur_Quality*4)-1
FreeEntity BlurMesh[Loop]
Next

; Free the blur camera.
FreeEntity Blur_Cam

EndIf

End Function


;Initializes the filter
;--------------------------

;camera - your main scene camera
;NOTE - do not change camera rotation before calling this init function.
Function InitGlow(camera)
GL_scenecam = camera

;SET GLOW CAMERA
GL_cam = CreateCamera()
PositionEntity GL_cam,GL_offset,0,0
CameraProjMode GL_cam,2
CameraRange GL_Cam, 1, 100
CameraClsMode GL_cam,0,1
CameraViewport GL_cam,0,0,GL_texsize,GL_texsize
HideEntity GL_cam

;SET GLOW SPRITE 1 (FOR DARKENING PASSES)
GL_sprite = CreateSprite()
GL_tex = CreateTexture(GL_texsize,GL_texsize,256+16+32)
GL_texbuffer = TextureBuffer(GL_tex)
EntityTexture GL_sprite,GL_tex
PositionEntity GL_sprite,GL_offset,0,1
EntityOrder GL_sprite,-999
EntityFX GL_sprite,1

;SET GLOW SPRITE 2  (FINAL  OVERLAY)
GL_sprite_scene = CreateSprite()
EntityTexture GL_sprite_scene,GL_tex
ScaleSprite GL_sprite_scene,2*(1.0/GL_camzoom),2*(1.0/GL_camzoom)
PositionEntity GL_sprite_scene,EntityX(GL_scenecam),EntityY(GL_scenecam),EntityZ(GL_scenecam)+2
EntityParent GL_sprite_scene,GL_scenecam
EntityOrder GL_sprite_scene,-999
EntityFX GL_sprite_scene,1
End Function



;Renders the filter
;--------------------------

;fade - initial fade value for the scene
;dark_passes - how many multiply passes (darkening)
;glow_passes - how many blur / glow passes
;glare_size - blur radius

;just play with the values until you find a good combination.


;IMPORTANT NOTES
;* 		The scene camera will be hidden automatically...
;		but you must hide every other camera you might be using Before calling this Function.

;*		Also if you want to turn the blurring off you MUST HIDE the GL_sprite_scene object and stop calling this function (obviously :)

;* 		The more dark_passes and glow_passes - the slower the function is ... so beware.
;		Also switching to smaller texture size will boost performance.

Function RenderGlow(fade#=0.15,dark_passes=2,glow_passes=3,glare_size#=4)
;RENDER THE SCENE WITH MAIN CAMERA AND COPY TO TEXTURE
;Also apply the fade filter
HideEntity GL_sprite
EntityBlend GL_sprite_scene,1
EntityColor GL_sprite_scene,0,0,0
EntityAlpha GL_sprite_scene,fade

CameraViewport GL_scenecam,0,0,GL_texsize,GL_texsize
RenderWorld
CopyRect 1,1,GL_texsize-1,GL_texsize-1,0,0,BackBuffer(),GL_texbuffer
;CameraViewport GL_scenecam,0,0,GraphicsWidth(),GraphicsHeight()
CameraViewport GL_scenecam,0,GraphicsHeight()/8,GraphicsWidth(),GraphicsHeight()-((GraphicsHeight()/8)*2)

ShowEntity GL_sprite
EntityColor GL_sprite_scene,255,255,255
EntityAlpha GL_sprite_scene,1


;SWITCH TO GLOW CAMERA AND HIDE MAIN CAMERA
HideEntity GL_scenecam
ShowEntity GL_cam


;MULTIPLY BLEND WITH BACKBUFFER TO MAKE THE CONTRAST HIGHER
;(RESERVE THE LUMINATED AREAS)
EntityBlend GL_sprite,2		
For i=1 To dark_passes
	RenderWorld 
Next
CopyRect 1,0,GL_texsize-1,GL_texsize,0,0,BackBuffer(),GL_texbuffer
EntityBlend GL_sprite,1
HideEntity GL_sprite

;BLUR AND PUT SOME GLOW ON THE TEXTURE
;MORE PASSES - MORE GLOW
HideEntity GL_cam
For i=1 To glow_passes
blurtexture(GL_tex,1,glare_size)
Next
ScaleTexture GL_tex, 1,1
PositionTexture GL_tex,0,0
TextureBlend GL_tex, 2;2

;SWITCH BACK TO NORMAL CAMERA
ShowEntity GL_scenecam

;BLEND WITH ADDITIVE BLENDING WITH THE SCENE
EntityBlend GL_sprite_scene,3
End Function


and here is the code to use it >>>>>>>>>>>

BEWARE this will slow everything down.

Graphics3D 640,480,32,2

Include "bbloom.bb"

LineTex = LoadTexture("line.png",2)

cam = CreateCamera()

initglow(cam)

pivot = CreatePivot()
PositionEntity pivot,0,0,20

LineSprite1 = CreateSprite(Pivot)
EntityTexture LineSprite1,LineTex
ScaleSprite Linesprite1,1,2
EntityBlend LineSprite1,3
RotateSprite LineSprite1,-20
SpriteViewMode LineSprite1,2
PositionEntity LineSprite1,-.7,0,0
EntityColor linesprite1,0,255,255
EntityFX linesprite1,1

LineSprite2 = CopyEntity(LineSprite1,pivot)
PositionEntity Linesprite2,.7,0,0
RotateSprite LineSprite2,20


LineSprite3 = CopyEntity(LineSprite1,pivot)
RotateSprite LineSprite3,100
ScaleSprite LineSprite3,1,0.75
PositionEntity LineSprite3,-0.60,-1.8,0

LineSprite4 = CopyEntity(LineSprite1,pivot)
RotateSprite LineSprite4,80
ScaleSprite LineSprite4,1,0.75
PositionEntity LineSprite4,0.75,-1.8,0

While Not KeyHit(1)

    TurnEntity pivot,0,0,1
	;MoveEntity pivot,0,0,(Sin(MilliSecs()/2))
	renderglow(0.3,1,4,3)			; Play with these Numbers to alter effect

	RenderWorld
	Flip

Wend


Neat Eh?

Wow, that IS nice. Lovely stuff - thanks. :-)

As you say, it does slow things down (understandably so) - if incorporated into a game like Asteroids, would there likely be any problems running it on your 'below average' PC ('below average' being perhaps 1GHz) but perhaps coupled with, say, an older video card (older being a year old perhaps).

I would include it in the game and give the user the option to turn it on or off.

Alos, bear in mind that brightness is kinda relative to the rest of the screen. Don't ever use pure white (255 255 255) as a colour on screen. (Unless I suppose if you have a nuclear-explosion white-out!)Have a dark background, keep colours light but not bright so that lights will really show!

Hey I can feel a remake of the old battlezone coming on...

Indiepath.T - agreed, an option to turn it on and off does sound like the best solution.

Have you see the Vectoroids source code? I ask as I'm curious how easy (or hard) it would be to incorporate not only the 'bloom' function but also the very nice smooth lines and brightness in the way that you showed me. Remember that I'm a relative beginner - is it likely to be beyond me?


Malice - I heed your warning, however, as Asteroids is purely bright white vectors on a black background then presumably it will look 'right' if implemented as per the above code examples?

IronGiant - Can't say I have seen the source. It should be possible to convert it to use similar code to the stuff I have posted. If the program uses the 2D line command you could convert it using the following snippet. (from Fredborg)
Function Line3D(mesh,x0#,y0#,z0#,x1#,y1#,z1#) 

	If mesh = 0 
		mesh = CreateMesh() 
		surf = CreateSurface(mesh) 
		EntityFX mesh,1+16 
	Else 
		lastsurf = CountSurfaces(mesh)
		surf = GetSurface(mesh,lastsurf)
		If CountVertices(surf)>30000
			surf = CreateSurface(mesh)
		EndIf 
	End If 

	v0 = AddVertex(surf,x0,y0,z0) 
	v1 = AddVertex(surf,x1,y1,z1)  
	v2 = AddVertex(surf,(x0+x1)*0.5,(y0+y1)*0.5,(z0+z1)*0.5) 
	AddTriangle surf,v0,v1,v2 

	Return mesh 

End Function



Malice - I heed your warning, however, as Asteroids is purely bright white vectors on a black background then presumably it will look 'right' if implemented as per the above code examples?



I hope so! In theory yes, but I think that your problem mainly comes into that of hardware. Newer monitors (especially flat-screen types) have a much sharper definition now, so the luminance/refractive stuff that you'd get from a CRT screen is greatly reduced. So instead of having a bright 'light', you would get the actual, sharp, defined line.

I'd certain recommend the Faded Texture, the bloom stuff, if you can (bit techy for me!) and good luck!

Indiepath.T - It does indeed appear to be using the Line command, at least in this portion of the code:

Function render_object(obj.vector_obj)
Color 255,255,255
If obj\visible Then
an# = obj\angle
For i = 1 To obj\vector_count - 1
ox# = obj\x
oy# = obj\y
x1# = ox + Sin((obj\vectors[i-1]\angle) + an) * ((obj\vectors[i-1]\radius))
y1# = oy + Cos((obj\vectors[i-1]\angle) + an) * ((obj\vectors[i-1]\radius))
x2# = ox + Sin((obj\vectors[i]\angle) + an) * ((obj\vectors[i]\radius))
y2# = oy + Cos((obj\vectors[i]\angle) + an) * ((obj\vectors[i]\radius))
Line x1,y1,x2,y2
Next
End If
End Function


BTW, how do I make inserted code (ie inserted into a message here) appear as scrollable green text on a black background)?

[ code ]
[ /code ]

(remove spaces)

Malice - thanks, yes, I see what you mean. No doubt some experimentation will be called for. :-)

Then it SHOULD be pretty easy to convert to 3d. BUT without seeing the rest of the code I could be wrong.

Email me the code it you like. I'd like to have a go some time.

Thanks Cygnus.

Indiepath.T - YHM.