Main Menu

Build a basic 3D Render Engine

Started by stevmjon, May 03, 2022, 10:25:50 PM

Previous topic - Next topic

stevmjon

howdy

i wanted to put this up in stages so people can see the progress, and also to see the code when it is small (makes it easier to read).

this is a render engine i built myself. so i am learning different calculations, and understanding how they work, before i use them.
so far i am having fun doing this. and as usual, when it comes to the mathematics, there is choices to make. so i went for the geometry calculations method. another is quadratic method if you choose.

the progress :
> model 'wireframe' geometry    ;    see a wireframe model of the objects in the scene, but these are not used in the render. the spheres are complete circles with no polygons.
> render is diffuse surface    ;    looks at the surface normals and compres the light angle, to give a shade
> render specular highlight    ;    calculate the light reflection on the object surface, and compare to camera view to give a specular highlight (this is scaled down to suit a nice size)

so the main scene wireframe models (camera view) is drawn directly to the screen, using lockbuffer. it seems to work fast. needs checking on other computers.
then the render screen is drawn to an FX image using fastdot, then when complete is drawn to the screen. i have 2 methods of seeing the display.

if this is working correctly, i will then move onto shadow casting, then surface reflections.
suprisingly, the theory and mathmatics is not difficult so far. i guess it gets tricky when adding realistic surfaces...

anyway, i will even add my own editor, so you can select objects and move them around on screen, as well as add lights and position them too.
and kev, i kept the same menu colors because you said you liked them in the other raytracer i ported.

   enjoy, stevmjon
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

kevin


   Amazing..  just amazing..   I'm in the middle of building BB2PB thing but will have a closer sticky nose later !   

stevmjon

try this one out kev.

i worked out how to speed up the 'render each line' render method.
i am still working on shadow casting. i have the spheres working great, just getting the ground plane to work (as it is unlimited size across the ground)

   stevmjon
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

kevin

 There's really nothing wrong with the speed, it's actually fine.  In particular when you think it's running on the PlayBASIC runtime..

There's some stuff you could use built in math functions for stuff like getting the length of a vector you can get use getDistance3D(0,0,0,VectorX#,VectorY#,VectorZ#)    

I can sneak in some things if you like ? - I know a guy :)



stevmjon

ah cool kev, i used sqrt() a lot and this costs time. thanks for the getdistance3D() tip, that would calc faster. i will use this straight away.
i should really look at built in maths. i have a habit of manually calculating most things.

i am drawing the main scene, wireframe models, to the screen. is this ok as its mainly just lines, and gouraud boxes for the menus.
i did draw the render itself to an FX surface using fastdot.

any other tips to improve this is appreciated.

  stevmjon
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

kevin

#5
   Steve,

      Sqrt() functions actually map directly to fpu code.  Ideally we're meant to call such functions (from a machine code level) and then have our code wander off and do something else; while it completes..  Thus interleaving floating point and integer operations side by side.   But this only works if we're not dependent upon the answer; so that doesn't help most of the time.   Which is useless to us, but an interesting factoid :)

     
      In most normal programs the operation cost of a refresh is considerably lower than what your doing;  and for every object you add to the scene we're stacking work load upon work load.   So even small operations will stack up.    

       An expression like;

      dot_length# = Sqrt(rayx#*rayx# + rayy#*rayy# + rayz#*rayz#)

      isn't a big deal at all today; even if called 1000's of times.  But if we're calling it millions of times, then anything we can do to shortcut  it the better.   Just swapping that to a GetDistance3D function to compute the length,  removes the 3 mults and pair of additions from the PB runtime..  even though there small operations compared to the sqrt,  they still fall through the PB runtime every time.  It soon adds up.


      previews: (Drawing LInes)

      When rendering to the 'screen'  or any video image as long as lock /  unlock the buffers at the start and end of the process, rather than for each individual job.  We're generally going to be ok..  

       There are things to consider; such as if the drawing is going to take a long time,  like if you drawing millions of line fragments; then you'd be best to structure the loop(s) so they're doing drawing batches of objects ..  refresh the window and continue..  So there's no dead time for the user and windows doesn't think you app is dead.

     What you could do; which sounds counter intuitive.  is use Shapes to draw the batches of connected line segments.   So for each ring you could dump the screen coordinates of each vert to the shape; where the edge list is already defined to close the list.   Then draw the shape and repeat the process.  

PlayBASIC Code: [Select]
   Count=1000

RingShape = NewShape(Count , Count)

// Set the edge links for this shape.
For lp =0 to Count-2
SetShapeEdge RingShape,lp, lp,lp+1
next
// link the last one back to the first
SetShapeEdge RingShape,lp,lp,0



StartTime=Timer()
do

cls

mx#=mousex()
my#=mousey()
for lp =0 to Count-1
; randomly init the vertex as polar coordinates

Angle#=(360.0/Count)*lp
Radius# =80+ Cos(WobbleAngle#+(Angle#*Scaler#))*50
setshapevertex RingShape,lp,Cosradius(Angle#,Radius#) ,Sinradius(Angle#,Radius#)
next

WobbleAngle#-=2.25

lockbuffer
drawshape RingShape,mx#,my#,1
unlockbuffer


Scaler#=wrapvalue(scaler#+0.01,1,50)
;(Timer()-StartTime)/100000.0

sync
loop








      Data Statement support evaluation of constant expressions...  


      so this;

      Data 50,100,200,0,0  ;  red,green,blue,specular,reflection

      Could just be

      Data Rgb(50,100,200) ,0,0  ;  red,green,blue,specular,reflection


PlayBASIC Code: [Select]
      ThisRgb=ReadData()
print rgbR(ThisRGB)
print rgbG(ThisRGB)
print rgbB(ThisRGB)

print Readdata()

print readdata()


sync
waitkey

Data Rgb(50,100,200),1111,2222 ; red,green,blue,specular,reflection




       

stevmjon

#6
thanks for the tips kev. i didn't know you could do so much with the SetShapeEdge command. handy. i thought it was straight lines from only point to point, lol.

so Sqrt() uses the gpu? i you pre-calculated the maths then used a variable inside this command will that make it faster?
what was interesting with my computer, i swapped out every Sqrt() with GetDistance3D() and it wasn't any faster... could be my computer though.

good tip with the ReadData() statements. didn't know you could do that.

just a question with drawing lines/shapes to the screen, is there any advantage between drawing to video surface or FX surface?

  thanks stevmjon
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

kevin


Quotei didn't know you could do so much with the SetShapeEdge command. handy. i thought it was straight lines from only point to point, lol.

  The edges are straight,  but as you know;  if you make the steps between vertex smallest enough you can better approximate curves.

Quote
so Sqrt() uses the gpu? i you pre-calculated the maths then used a variable inside this command will that make it faster?
what was interesting with my computer, i swapped out every Sqrt() with GetDistance3D() and it wasn't any faster... could be my computer though.

    nahh...  Sqrt and ALL floating point maths execute on the CPU  but within it's FPU (Floating point unit)  which is just a small subset of math instructions that work purely with floats.   Normal CPU instructions don't;  they're integer only.   You actually can't mix and match them.   One of the nice things about the FPU instructions is they include various functions built right into the chip.   Such as Sqrt / Cos/ Sin and bunch of others.

   

Quotejust a question with drawing lines/shapes to the screen, is there any advantage between drawing to video surface or FX surface?

     It all depends on much writing your doing to the target surface.   If the surface is in video memory then the speed at which the cpu can write to video memory is the bottleneck.   In modern systems this is less of an issue,  but in older systems this can have a significant impact.   

     Generally I tend to use render to an FX image and never use any video surfaces.   So if i'm drawing lots of stuff to the 'screen' then we're bound by the cpu and its bus to memory.    Rather than the Cpu's bus to video memory with is external and variable. 


stevmjon

ah, lets see if i have this correct:
> if i draw lines, shapes, dots etc to the screen 'back buffer', that is cpu.
> if i draw a video image to the screen 'back buffer' that is gpu.

so graphics commands (excluding images) are cpu driven?
also, if you used openGL, are graphics commands gpu driven instead?

sorry for so many questions, i am getting more interested in how things work.
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

kevin

#9
Quote
so graphics commands (excluding images) are cpu driven?

 Sort of.   The target surface type (Video/ FX) and type of rendering required do.    So rendering a Video Image or drawing a Rectangle/Box onto a Video surface.  Than those are done by the GPU.    It's limited to Images and rectangle fills as that's all direct draw supports.  

 This is fine if you want opaque rendering of only those two primitives, anything else and you have to do it yourself.

  So in terms of FX images & primitives Dots/Lines/Circles/ Shapes etc etc they're all CPU drawn.     In general there's 8 or more versions of every draw mode in the back end of the libraries.     Different draw modes handle different pixel formats (15bit/16bit/24bit/32bit) with blend mode combinations.  


Quote
also, if you used openGL, are graphics commands gpu driven instead?

  Sort of..   Older editions  OpenGL / Direct3D don't include built in support or all render types or primitives.    Such apis normally give you DOT/LINE and POlygon rendering that's it.    
 
  For Blend modes old versions only really include Alpha Addition / Alpha Multiplication (We call it tint in PB) and Alpha Blending.    The upsides is that data stored on GPU can be drawn / processed by the GPU.    So you get bi-linear texture filtering or free..  The down side is that old GPU's have a tiny amount of memory.    

   - G2D - 2D OpenGL library for PlayBASIC V1.64P (login required)
   - PBFX 2D Core
    - PlayBasicFX (PBFX) Screen Shot Updates from 2007

   - Search for old conversation about building a PlayBASICFX / PBFX back end..   Such fun :)

   

stevmjon

another update.
this version adds shadow casting.

i thought i would upload with each part i add to the code, in case people want to compare versions, and can look at minimum code to begin with, and see a bit more code added with each update.
easier to learn from. the code has a lot of comments to explain what is going on.

  enjoy, stevmjon
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

kevin


  Only got round to testing this last night; but it's all looking good.

stevmjon

update time

this is version 0.4 and adds reflections to the ground plane.
i just wanted to get the routine working, as i needed to modify the routines so i could re-use them for the main ray casting and reflections too.

i will add reflections to the spheres next, so every surface has reflections on.
the reflection value is currently 60%.

   enjoy, stevmjon
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

stevmjon

this time i added reflections on the spheres.
--- only 1 reflection bounce ---

i also turned up the ambient value to 0.15 . this brightens up the shadows a little bit.

next time i will make multiple 'reflection' bounces so you can see a reflection on a reflection. looks more natural.

   have fun, stevmjon
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

stevmjon

here is v0.6

i have added reflections to every surface in the scene, and allowed multiple bounces until there is no more colllisions.
what do you guys think?

i am considering adding the ability to modify the scene yourself to change size and position of the objects and the light(s).
and also to modify the surface settings.

   have fun, stevmjon
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.