1
$\begingroup$

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.

$\endgroup$
  • $\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$ – user1118321 Feb 2 '17 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$ – PaulHK Feb 2 '17 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 kushwah Feb 2 '17 at 9:47
  • 1
    $\begingroup$ On win32 you can get away with GDI, flushing memory to screen is relatively fast. Look for CreateDIBSection $\endgroup$ – Raxvan Feb 2 '17 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$ – PaulHK Feb 3 '17 at 4:17
2
$\begingroup$

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.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.