I was trying to make a CPU-based ray tracer but I know after all the calculation I will end up with an array of pixels. I don't know how to paint it on the screen. What are the various ways to do that? I am using C++ and I am working on Windows 10. I know OpenGL(useless since I am not gonna use my GPU), Allegro 5 game library and I am also familiar with win32 C++ API.
-
$\begingroup$ What platform? What libraries are you familiar with? Do you have any requirements or limitations in doing this? Your question as posed seems a bit too broad. Can you narrow it down a bit? $\endgroup$– user1118321Feb 2, 2017 at 6:20
-
1$\begingroup$ You could try SDL as that supports software rendering / direct FB access and is in some ways simpler to use. Otherwise you could stick with openGL and use texImage2D to upload your software framebuffer to the GPU. $\endgroup$– PaulHKFeb 2, 2017 at 9:38
-
$\begingroup$ Thanks for mentioning about the SDL. And yes texImage2D can be used to put image in gpu but then what to do? Is there any function in OpenGL so that i can copy the image data directly to default framebuffer? $\endgroup$– Ankit singh kushwahFeb 2, 2017 at 9:47
-
1$\begingroup$ On win32 you can get away with GDI, flushing memory to screen is relatively fast. Look for CreateDIBSection $\endgroup$– RaxvanFeb 2, 2017 at 11:20
-
$\begingroup$ To supplement my answer: You will need to draw a full screen quad with texture mapping enabled, you can follow a simple texture mapping tutorial for this, it's fairly straight forward. $\endgroup$– PaulHKFeb 3, 2017 at 4:17
1 Answer
This answer uses GDI and a little bit of MFC. The MFC bits aren't necessary and just there for my convenience. You can hit the Windows API directly if you need to.
In your window class have some data members for the bitmap. I currently use MFC's CBitmap for convenience:
CBitmap bitmap_;
unsigned char* bitmapBits_;
bitmapBits_
is a pointer to the bitmap data that you can write directly into.
Before rendering, you'll need to create the bitmap using CreateDIBSection()
and get the pointer to the bits of the bitmap:
BITMAPINFO bmi;
memset(&bmi.bmiHeader,0,sizeof(bmi.bmiHeader));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = widthOfTheImage;
bmi.bmiHeader.biHeight = heightOfTheImage;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
void* bitmapBits;
bitmap_.Attach(CreateDIBSection(0,&bmi,DIB_RGB_COLORS,&bitmapBits,0,0));
if(bitmap_.m_hObject == NULL)
AfxMessageBox("CreateDIBSection() FAILED!\n");
bitmapBits_ = reinterpret_cast<unsigned char*>(bitmapBits);
When writing to the bitmap bits, you first need to determine the row size (see here).
// Compute the row size of a DIB.
// (See https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage and ComputePitch() from atlimage.h)
const unsigned int rowSize = ((24 * imageWidth + 31) / 32) * 4;
Then you can write the colour information into the bitmap's data. Note this is a bottom-up DIB; memory earlier in the buffer represents scanlines lower in the image. Another way of putting it is that the the first element of the bitdata relates to the bottom-left. Colour components are in BGR order.
unsigned char* dibPtr = &bitmapBits_[pixelY * rowSize + pixelX * 3];
dibPtr[0] = ???; // blue between 0 and 255
dibPtr[1] = ???; // green between 0 and 255
dibPtr[2] = ???; // red between 0 and 255
Sorry if the previous part is already known to you, as you asked simply how to get it painted to the screen. Here's my simplified paint routine from my MFC application:
void RendererWnd::OnPaint()
{
CPaintDC dc(this);
CDC dcMemory;
dcMemory.CreateCompatibleDC(&dc);
CBitmap* oldBmp = dcMemory.SelectObject(&bitmap_);
dc.BitBlt(0,0,imageWidth,imageHeight,&dcMemory,0,0,SRCCOPY);
dcMemory.SelectObject(oldBmp);
}
There are going to be many other ways of doing this. If you're doing a long render and want to update the window periodically then you'll probably want to have a timer in your window. When this timer fires you want to request a repaint of your window using InvalidateRect()
. You'll need to synchronise access to the bitmap data between your rendering thread(s) and the thread that's doing the painting. This will ensure the painter can access the bitmap when it's not being written to. I use a CRITICAL_SECTION
for this.