创建一个窗口只是为了获取其帧缓冲区?

问题描述 投票:0回答:1

我一直在用 C/C++ 编写这个小型光线追踪引擎,到目前为止我已经有了基本的功能,即将完成的图像写入图像。但是,我希望能够创建一个窗口并对其帧缓冲区进行低级访问。有没有一种方法可以在 Windows 中执行此操作,而不使用任何外部库(例如 SDL3 或 MiniFB)?

c++ winapi graphics framebuffer raytracing
1个回答
0
投票

在 Windows 上,您可以使用 WinAPI 创建窗口。

这是带有按钮的窗口的示例:

//-----------------------------------------------------------------------------
// Defines

#define WIN32_LEAN_AND_MEAN // no MFC

//-----------------------------------------------------------------------------
// Header

#include <windows.h>
#include <TCHAR.h>

//-----------------------------------------------------------------------------
// Globals
HWND g_hwndButton = 0;

//-----------------------------------------------------------------------------
// Event Handler
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch(msg)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;


        case WM_COMMAND:
            {
                if (HIWORD(wparam) == BN_CLICKED &&
                    (HWND) lparam == g_hwndButton)
                {
                    DestroyWindow(hwnd);
                }
                return 0;
            }
            

        default: break;
    }

    return (DefWindowProc(hwnd, msg, wparam, lparam));
}

//-----------------------------------------------------------------------------
// Main
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdline, int nCmdShow)
{
    WNDCLASSEX winclass;
    winclass.cbSize = sizeof(WNDCLASSEX);
    winclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
    winclass.lpfnWndProc = WindowProc;
    winclass.cbClsExtra = 0; // extra class info space
    winclass.cbWndExtra = 0; // extra window info space
    winclass.hInstance = hInstance; // assign the application instance
    winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    winclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winclass.lpszMenuName = NULL; // the name of the menu to attach
    winclass.lpszClassName = __T("WINCLASS1"); // the name of the class itself
    winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassEx(&winclass);
    HWND hwnd;
    hwnd = CreateWindowEx(  NULL,
                            __T("WINCLASS1"),
                            __T("Window Title"),
                            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                            0,
                            0,
                            200,
                            200,
                            NULL, // handle to parent
                            NULL, // handle to menu
                            hInstance, // instance of this application
                            NULL);

    if(hwnd==NULL)
        return -10;

    g_hwndButton = CreateWindow(__T("BUTTON"), __T("My Button"), WS_CHILD |
    WS_VISIBLE | BS_PUSHBUTTON, 20, 20, 100, 20, hwnd, NULL, hInstance,
    NULL);

    //UpdateWindow();

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        // translate any accelerator keys
        TranslateMessage(&msg);
        // send the message to the window proc
        DispatchMessage(&msg);
    }
    return 0;
}

您需要一种可以绘图的画布,而不是按钮。一种方法是使用 WinAPI + GDI。

UINT                    *BitmapBytes;
BITMAPINFO              BitmapInfo;
BITMAPINFOHEADER        BitmapInfoHeader;
HDC                     BitmapHDC;
HBITMAP                 BitmapHandle;

BitmapInfoHeader.biSize = sizeof(BitmapInfo);
BitmapInfoHeader.biWidth = width;
BitmapInfoHeader.biHeight = height;
BitmapInfoHeader.biCompression = BI_RGB;
BitmapInfoHeader.biBitCount = 32;
BitmapInfoHeader.biPlanes = 1;
BitmapInfoHeader.biSizeImage = 0;
BitmapInfoHeader.biClrImportant = 0;
BitmapInfoHeader.biClrUsed = 0;
BitmapInfoHeader.biSizeImage = 0;
BitmapInfoHeader.biXPelsPerMeter = 0;
BitmapInfoHeader.biYPelsPerMeter = 0;

BitmapInfo.bmiHeader = BitmapInfoHeader;

BitmapHDC    = CreateCompatibleDC(hdc); 
    
BitmapHandle = CreateDIBSection(hdc, &BitmapInfo, 
                                0, (void**)&BitmapBytes, NULL, 0);

SelectObject(BitmapHDC, BitmapHandle);

您的位图类可能如下所示:

class BitmapDIBUncompressed32Bit
{
public:
    BitmapDIBUncompressed32Bit(int width, int height);
    BitmapDIBUncompressed32Bit(HDC hdc, int width, int height);
    int getWidth() const;
    int getHeight() const;

    virtual ~BitmapDIBUncompressed32Bit();

    void Release();
    
    HDC getHDC();

    UINT * getBytes() const;

    operator UINT * () { return BitmapBytes; };

private:
    UINT                    *BitmapBytes;
    BITMAPINFO              BitmapInfo;
    BITMAPINFOHEADER        BitmapInfoHeader;
    HDC                     BitmapHDC;
    HBITMAP                 BitmapHandle;
};

然后您的画布就可以使用该位图。画布类可能如下所示:

class Canvas : public Component
{
public:
    Canvas();
    
    void setPixel(int x, int y, int color);


    void paint();

    void clear();

    virtual ~Canvas();

private:    

    void create(HWND hWnd, HINSTANCE hInstance);

    BitmapDIBUncompressed32Bit  Bitmap;
    static int NumOfRegisteredWindows;
};

paint方法的实现:

void Canvas::paint()
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(m_hWnd, &ps);
    HDC hdcWindow = GetWindowDC(m_hWnd);

    BitBlt(hdcWindow, 0, 0, m_Size.getWidth(), m_Size.getHeight(), Bitmap.getHDC(), 0, 0, SRCCOPY);

    ReleaseDC(m_hWnd, hdcWindow);
    EndPaint(m_hWnd, &ps);
}

画布是如何创建的:

void Canvas::create(HWND hWnd, HINSTANCE hInstance)
{
    WNDCLASS wndcls;
    wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wndcls.lpfnWndProc      = (WNDPROC)WindowsManager::windowEventsProcessor;
    wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
    wndcls.hInstance        = hInstance;
    wndcls.hIcon            = NULL;
    wndcls.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1);
    wndcls.lpszMenuName     = NULL;
    
    NumOfRegisteredWindows++;
    string UniqueClassName =  "UniqueClassNameID_Canvas_" + String::IntToString(NumOfRegisteredWindows);
    wndcls.lpszClassName    = UniqueClassName.c_str();
    
    if(RegisterClass(&wndcls) == 0)
        cout<<"shit happens"<<endl;

    m_hWnd = CreateWindowEx(NULL,UniqueClassName.c_str(), "YourCanvas", WS_CHILD |
    WS_VISIBLE, 20, 20, 100, 20, hWnd, NULL, hInstance,
    NULL);
}

设置像素:

void Canvas::setPixel(int x, int y)
{
    Bitmap[x+y*Bitmap.getWidth()] = 0xFFFF0000;
}

也看看PixelToaster

如果您想获得 WinAPI + GDI 方法,您还可以在《Programming Windows: The Definitive Guide To The Win32 Api》一书中找到更多信息

作为替代方案,您可以创建一个 OpenGL/Direct3D/Vulkan 渲染器设备和一个纹理对象,然后写入纹理内存并上传。

您还可以使用tev。它提供了一个网络协议来写入其帧缓冲区。这样您就可以免费获得色调映射。

Qt 也可以是您的一个选择 - 您可以使用 QImage 并将其绘制在小部件上 - 例如:

class RenderWidget : public QWidget
{
    Q_OBJECT

public:
    RenderWidget(const CommandLineArguments& cla, QWidget* parent = nullptr)  : QWidget(parent), render_scene_thread_(cla), render_preview_thread_(&render_scene_thread_) {
        draw_next_frame();

        setFixedSize(100,100);
    }

    virtual ~RenderWidget() {
        if(render_preview_thread_.isRunning()) {
            render_preview_thread_.quit();
        }
        if(render_scene_thread_.isRunning()) {
            render_scene_thread_.quit();
        }
    }

    void paintEvent(QPaintEvent * e) override {
        if(render_preview_thread_.qimage_) {
            QPainter p(this);
            p.drawImage(rect(), *render_preview_thread_.qimage_);
        }
        else {
            LOG(INFO) << "Nothing to draw";
        }
    }
        
public Q_SLOTS:
    void draw_next_frame() {
        if(render_scene_thread_.render_state_ == RenderState::Rendering ||
           render_scene_thread_.render_state_ == RenderState::Done) {
            static Vector2i old_size = Vector2i(-1,-1);
            Vector2i size = render_scene_thread_.scene->sensor()->film()->size();

            if(old_size != size) {
                this->setFixedSize(size.x(), size.y());
                old_size = size;
            }
        }

        repaint();

        QTimer::singleShot(1000, this, SLOT(draw_next_frame()));
    }

public:
    RenderSceneThread render_scene_thread_;
    RenderPreviewThread render_preview_thread_;
};
© www.soinside.com 2019 - 2024. All rights reserved.