There are a few ways to manipulate individual pixels in 3D graphics APIs.
The simplest is to render a 1x1 primitive. Contrary to what you are assuming, this is not that inefficient. Rendering primitives (triangles) is what the 3D graphics API and hardware are designed to do. If you're already rendering a bunch of geometry anyway, a few hundred extra triangles isn't going to cause a significant impact. You can use simplified vertex formats and shaders to further ameliorate what little impact there is.
If you have a relatively few number of pixels to manipulate and each pixel could be manipulated more-or-less independently, rendering small quads or triangles is a reasonable approach.
If you have many pixels to manipulate, or the manipulation requires extensive access to neighboring pixel data or other CPU-side data, the next most-typical approach is usually to render the scene normally first, into a render target texture, and then map that texture on the CPU and manipulate the appropriate pixels. Finally, you render a full-screen quad containing the manipulated texture to the screen. There are several variations to this technique. For example, if you don't need to read the existing contents of the framebuffer you can eschew that step and just manipulate CPU-side bits of a fullscreen texture, rendering that over top your normal scene using alpha blending. This approach or some slight variation thereof is what you may want to consider if you have complicated needs.
There are APIs, particularly in OpenGL, that let you "directly" manipulate the pixels of the frame buffer (glDrawPixels
). These aren't generally a good idea, because unlike rendering 1x1 triangles, this is not an operation the GPU is optimized for, and reading back the framebuffer incurs not only the usual performance hit for reading data from the GPU, but a huge pipeline stall because the GPU can't concurrently manipulate the framebuffer while you are reading it and writing it back.
I would recommend you start with the 1x1 triangle approach, since unless you're already doing framebuffer readbacks or fullscreen quad passes, it will likely integrate into your rendering flow very easily. You can then verify if the performance is acceptable before possibly moving on to one of the more complicated options.