I really recommend a raytracer, especially to anyone interested in graphics. It's straightforward, powerful, infinitely expandable with optional features, and opens up a ton of discussion about performance, code complexity, and general organization. Plus it's fun, in an instant gratification kind of way.
Every now and then I get interested in demoscene programming. I've never even been able to get a triangle to render on the screen - except with something like XNA.
Do you think there's any value in going back to say, DOS based VGA programming? People in #osdev thought I was a bit strange for wanting to write a bootable kernel that only put pixels on the screen, but I really enjoy the idea of starting with plotting pixels, then moving on to more complex effects.
Are you more interested in actually writing to PC VGA registers, or just the idea of writing a pixel-by-pixel renderer? There are bindings for SDL and SFML in a lot of languages, and the web technology option mentioned by the sibling comment.
If you like the idea of "DOS-based VGA programming" (taken literally), you could also find a DOS compiler or assembler and run it in DosBox. All of IBM's hardware-level documentation can still be found online: http://www.mcamafia.de/pdf/pdfref.htm
Or with different desktop GUI toolkits for different languages: C++ w/Qt, C++ w/ wxWidgets, wxPython, Perl/Tk, Python +Tkinter, Delphi, Lazarus, or many other lang/toolkit combos. Even many BASICs have that ability built-in (from the early days of personal computers).
The value of DOS based VGA programming is that, although hard things are damn near impossible, easy things are easy. https://www.mail-archive.com/kragen-hacks@canonical.org/msg0... is a 64-byte .COM file I wrote in assembly that draws circles on the screen; you can get to mode 13h with two instructions, and then you have a linear framebuffer with 64000 bytes, one byte per pixel, at your disposal located at A0000h.
So there are some awesome things about this:
- It's really easy to get started. You can literally take the code on that page, assemble it, verify that you get the right hexadecimal output, and run it in dosbox.
- Just about anything you do in the segment you point at the framebuffer will make something appear on the screen and not crash your program. So your bugs will be harder to debug, but they will also be cool.
- For example, running a pointer off the bottom of the screen puts you back on top of the screen instead of in the middle of your stack or heap or something. There are 4 lines of pixels that aren't shown, but often you don't care.
- The pixels are more or less square.
- Since each pixel is one byte, pixels correspond nicely to addressable memory locations.
- The first few colors in the default VGA palette are primary colors.
- Once you draw something, you can get awesome effects by changing the palette. Color-cycling, for example. This was really cool when actually redrawing the screen sixty times a second was computationally infeasible.
There are also a lot of shitty things about it:
- 320×200 is not very much resolution. Nothing will look sharp, except possibly the corners of your pixels.
- Palettes make it easy to draw something, and they even provide a kind of high-level interface that lets you kind of search and replace colors, in a way. (It's really more like macro-expanding your colors I guess.) But they make gradients and alpha-blending really hard to achieve, unless you stick to grayscale or sepia or something. TrueColor video modes are much better for those.
As for triangles, I'm sure you can figure out how to do them, but keep in mind that geometric algorithms easily turn into a rat's-nest of special cases. I've written polygon-filling routines a few times, and they can easily have the same algorithmic complexity as an entire ray tracer, which looks a hell of a lot cooler.
having written one as a slightly larger python thingy, i can fully attest to that.
now that it is kind of done, i want to make it faster :) for example, having a home-grown vector_3d class kind of sucks (performance wise). it might be better to have vector_3d be actually based on, say, numpy.array ? once that is done, and i start seeing some real improvement, it might be possible to go the other route as well i.e. write the hot-spots in c++/c, and interface that with python.
or go with lua all the way ? or maybe try a hybrid approach (which would allow you to see how embeddable the language really is)
possibilities are endless, and as you so rightly said, gratification is instantaneous :)
Simply replacing your own vector_3d class with numpy.array() won't actually speed you up that much as the overhead of creating all the tiny 3 element arrays kills you (I think I got only a 2x speed up from going from a pure python vector_3d to numpy arrays). Numpy is optimised for the creation of a small number of big arrays.
The massive enormous speed ups come from creating massive arrays and implicitly working on them in parallel. So instead of iterating through every pixel and creating a origin and direction vector for each one you create a Total_number_of_pixels X 3 numpy array and pass that to your ray tracing function. Due to the way numpy broadcasts arrays the amount of rewriting you need to do is incredibly minimal and the speed ups over pure python are astronomical.
> The massive enormous speed ups come from creating massive arrays and implicitly working on them in parallel. So instead of iterating through every pixel and creating a origin and direction vector for each one you create a Total_number_of_pixels X 3 numpy array and pass that to your ray tracing function. Due to the way numpy broadcasts arrays the amount of rewriting you need to do is incredibly minimal and the speed ups over pure python are astronomical.
thank you for your insight! this is very useful indeed.
Just to give an idea of the speed up from going from processing individual arrays to batch processing - an Image I was rendering went from 2 hours to 20 seconds (and that was with further obvious speed ups left on the table as well).
Oh, and make sure you're using 64-bit version of python as you'll be slinging multi-gigabyte arrays around all over the place.