Cornell University ECE4760
Polygon Rendering
for 640x480 VGA 16-color
Pi Pico RP2040

3D rendering
The code implements vector and matrix operations so that a full geometric transformation sequence produces a triangle list for rendering.  You can specify vertices and face-lists to define objects, and combine them. There are modeling tranforms to move, scale, and rotate objects. After objects are built and positioned, a camera transform converts to 3D camera coordinates, followed by a perspective projection. Back-face culling is implemented and depth ordering is done by painters algorithm, because there is not enough memory for a z-buffer. Clipping is not yet implemented, so scenes have to be conservatively designed to fit in the view volume. Two pixel intensive steps: frame clear, and draw triangle, were accelerated using low-level memset operations to draw horizontal lines.
The image below is a frame from animation sequence running at 30 fps. About half the draw-time is spent on vertex arithmetic and about half on triangle fill rasterization. For this image the compute time was around 4 mSec using stock cpu clock rate.

Some of the code (matrix multiply, triangle scan conversion) was copied from a student project:
https://ece4760.github.io/Projects/Fall2023/av522_dy245/index.html
by Alix Virgo and Demian Yutin.

Quicksort is from
https://stackoverflow.com/questions/55976487/get-the-sorted-indices-of-an-array-using-quicksort

The low level VGA graphics routines were modified to use memset for drawHLine
Code, Project ZIP, vga_graphics.c, vga_graphics.h


Details of VGA on RP2040
Here we use Hunter Adam's VGA driver, specifically this example, but slightly modified for 4-bit rather than 3-bit color. The extra bit is used to make four green levels. Of course, the extra bit means that a 2-bit DAC is required for the green channel. The rgb.pio assembler program had to be slightly modified, and the drawPixel routine had to be recoded. A few routines were modified in VGA_graphics.c and VG_graphics.h. The new routines enable fast frame erase using memset and a modified drawHLine using memset. Refer to the VGA16_graphics.h for a complete list of all graphics routines.

-- This VGA version is compatable with LWIP for tcp/ip networking.
-- It also uses Protothreads 1.3 with options for roundrobin or priority scheduler.

The VGA implementation uses:
- PIO state machines 0, 1, and 2 on PIO 0
- DMA channels determined by the dma_claim_unused_channel routine
(this is to conform to the protocol ued by LWIP for DMA channels)
- 153.6 kBytes of RAM (for 640x480 pixel, 4-bit, color data)

The blue and red VGA lines are driven directly through 330 ohm resistors, as in Hunter's implementation.
The VGA-green connection requires two resistors. If you don't like my color balance for bits on the green channel, try varying the 470 ohm resistor.
- GPIO 16 ---> VGA Hsync
- GPIO 17 ---> VGA Vsync
- GPIO 18 ---> VGA Green lo-bit --> 470 ohm resistor --> VGA_Green
- GPIO 19 ---> VGA Green hi_bit --> 330 ohm resistor --> VGA_Green
- GPIO 20 ---> 330 ohm resistor ---> VGA-Blue
- GPIO 21 ---> 330 ohm resistor ---> VGA-Red
- RP2040 GND ---> VGA-GND
Performance is good. The drawPixel routine takes about 0.43 uSec. So you can draw about 2.3 million pixels/sec, or around 38,000 pixels per 60 Hz frame.
Using memset for drawHLine to scan-convert triangles makes the pixel rate much higher.


Copyright Cornell University October 1, 2024