I like to learn about BSP rendering

Started by
31 comments, last by the incredible smoker 6 years, 6 months ago

Division by Z is necessery for perspective correct texture mapping or any other interpolation for a scanline of a polygon.

But Z is constant at walls (horizontal scanline) and floors (vertical scanline), if you can't look up and down.

Quake did the z divide with floating point instruction every 16 pixels and interpolated the results in between. On Pentium this divide was free this way because it did integer instructions while FPU was busy with the divide.

DDA is just a way to get all intersections of a ray traveling throgh a grid quickly and in order. Notice that using this limits you to right angled walls. Doom had walls at any angle, which was the major improvement against Wolfenstein along textured floors / ceilings. It might be easier / worth to reinvent DDA yourself than reading about it, it's a really simple thing but with many usecases.

Advertisement

I looked at the wolfenstein code, i did not find something to start with,

many asm code in there also,

 

i saw this weird thing : #define TILESHIFT 161   in the file : wl_def.h

If you shift something 161 positions you might as well set the value to zero.

 

I,m going to search for more code examples if there are any more raycast examples.

 

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

btw : Do i need to cast a ray for every horizontal pixel ? ( x axis ).

Does wolfenstein cast a ray for every pixel ?

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

2 minutes ago, the incredible smoker said:

Do i need to cast a ray for every horizontal pixel ? ( x axis ).

For Wolfenstein: Not for each pixel, but for each vertical span (so for 320x200 display, you need to raycast 320 times - probably you meant that already and that's right.).

If this sounds inefficient, i agree. Comparing this with a BSP of (optionally angled) wall segments we get something faster:

Sorting segments front to back is just a traversal of a binary tree, you decide which child to visit first by a simple test determinating on which side of the splitting line the camera is. So while traversing the tree we draw walls as they appear and track occlusion:

The first wall covers a horizontal range, say from 0 to 100.

The next wall to draw then has 50-150. Clipping that against already drawn space gives just 100-150.

Next wall has 20-70. We clip it away completely and draw nothing.

Next wall has 70-320, we draw 150 - 320. Now we know the screen is completely full and any wall coming after this must be occluded - wer're done.

 

So this is less work than 320 traced rays, similar to rasterizing triangles is usually faster than raytracing.

Tracking occlusion can be done by a linked list of full/empty spaces, the list can have 320 entries at maximum, so we can just use an array of that size. (''Span list")

Additionally you can chack interior nodes against the span list to reject full branches of the BSP tree early.

This is quite simple. In my case it got more complex because i supported brushes of different height and multiple brushes on top of each other with empty space in between. But for a Wolfenstein restricted world it sould be almost as easy to implement as raycasting. A little more extra work is to write editor and BSP compiler.

 

Thank you Joe, edittor wont be a problem, i also made a 3D edittor already it looks like 3D max.

 

12 minutes ago, JoeJ said:

For Wolfenstein: Not for each pixel, but for each vertical span (so for 320x200 display, you need to raycast 320 times - probably you meant that already and that's right.).

If this sounds inefficient, i agree. Comparing this with a BSP of (optionally angled) wall segments we get something faster:

Yes that is what i mean for all X pixels.

 

What you are saying is that it would be the best to make wolfenstein with a BSP ?, sounds intresting to save CPU.

I,m reading your post carefully now.

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

I copyd the raycast code from http://lodev.org/cgtutor/raycasting.html

Its all working so far for the first example, only its done with doubles, i need to get rid of that.

I,m also looking to the wolfenstein code, see what works best for me to convert to all integer.

 

The problem is the lodev example uses many math functions,

the wolfenstein also uses tang function many times per frame, why did they not use the finetangent lookuptable for setting projection ?

 

What i wanto do now : learn about camera field of view, about matrices how it exactly make things faster ?,  and i need to learn about aspect.

The thing with the camera plane makes it more complicated, once i have it no problem.

 

Its more CPU intensive the what can be good i think, maybe i need faster chips, they are only in small packages ( smd ),

if i find a super fast smd chip with accurate internal clock i will use that, something like 200MHz.

 

How is the difference ?, dont the doom engine cast a ray to get distance for rendering a wall at the right size ?

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

1 hour ago, the incredible smoker said:

What i wanto do now : learn about camera field of view, about matrices how it exactly make things faster ?,  and i need to learn about aspect.

The thing with the camera plane makes it more complicated, once i have it no problem.

This stuff is a bit tedious. Although math is easy, i got confused often here and forgot anything.

I attach some code i still use in a software rasterizer for HSR. But i doupt it will help you much, and a raycaster should be much simpler anyways. (I never implemeted a raycaster or looked at any id source really close)

For polygons it definitively helps to have conversions between World, Eye and Screenspace. Then you need to calculate clipping planes from fov and all this kind of stuff. And very important is that you can interpolate 1/z linear in screenspace when drawing a scanline but not z directly.

I never used a projection matrix in a specific format like OpenGL or DX does, instead i handled perspective divide in Eye to Screen space convertions and stored fov and pov variables in camera struct along eye orientation matrix and position.

Basically you mostly need to know how eye space to screen space works, and how to clip against frustum.

If anything works, there comes the fun to port it robustly to fixed point, avoid to draw a pixel outside the screen etc...

Abrashs book covers it all for polygons IIRC.

 

 

 

 

fov = field of fiew; not sure if i use cos or sin of angle here

povx/y = point of view; usually in the center of the screen


	inline void EyeToWorld (v3f &d, v3f &s)
	{
		d = pos + orn3x3.TinvVec (s);	
	}

	inline void WorldToEye (v3f &e, v3f &w)
	{
		e = orn3x3.TrnsVec (w) - pos;
	}
	
	inline void EyeToScreen (v3f &d, const v3f &s)
	{
		d.z = fov / s.z;
		d.x = s.x * d.z + povx;
		d.y = s.y * d.z + povy;
	}

	float ZRange (float z)
	{
		z = (z-clipfarS) * rangeMult;
		return z;
	}

	float ZScale (float z)
	{
		z = z * rangeMult;
		return z;
	}

	inline void EyeToScreenZRange (v3f &d, const v3f &s)
	{
		EyeToScreen (d, s);
		d.z = ZRange (d.z);
	}

	inline void ScreenToEye (v3f &d, const v3f &s)
	{
		float z = 1.0f / s.z;
		d.x = (s.x - povx) * z; 
		d.y = (s.y - povy) * z; 
		d.z = fov * z;
	}



	inline float EyeZToScreenZRange (float ez) 
	{
		return ZRange (fov / ez);
	}

	inline float EyeZToScreenZ (float ez) 
	{
		return fov / ez;
	}

	inline float ScreenZToEyeZ (float sz) 
	{
		return fov / sz;
	}

 

Thank you very much for the code, i also look at this book online, very nice info.

i will copy the code and look how i can use it.

 

btw : what is


orn3x3.TinvVec

in your code ?

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

4 minutes ago, the incredible smoker said:

Thank you very much for the code, i also look at this book online, very nice info.

i will copy the code and look how i can use it.

 

btw : what is



orn3x3.TinvVec

in your code ?

Inverse transform, same as multiplying with the transpose of the matrix.

A better name would be 'Unrotate' for TinvVec and 'Rotate' for TrnsVec

I think i can make the whole example integer now, only 1 thing left : calculate the distance.

the example uses sqrt() with floats, so i need to work around that to use 32 bit integers only,

any idea someone ?

 

S T O P C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70 Working on : LevelContainer class & LevelEditor

This topic is closed to new replies.

Advertisement