2

需要明确的是,我已经广泛测试了这段代码,发现问题出在 WGL 代码之前编写的代码的某个地方。正是在 WIN32 代码中。现在我认为这可能部分是由调用 gluOrth2D 引起的,但即便如此,据我所知,这不应该是主要原因。我可能会自己弄乱一些东西来解决这个问题,但考虑到这个问题(发生在一个更大的项目中)已经占用了我很多时间,我认为值得发布这个包裹,以防其他人遇到同样的问题。我希望得到解释以及修复/更正。

#include <GL/glew.h>
#include <glfw3.h>
#define GLFW_EXPOSE_NATIVE_WGL
#define GLFW_EXPOSE_NATIVE_WIN32
#include <glfw3native.h>

constexpr int width = 1000, height = 1000;

bool running = true;

LRESULT CALLBACK WIN32_processMessage(HWND hwnd, UINT msg,
    WPARAM wparam, LPARAM lparam)
{
    return DefWindowProcA(hwnd, msg, wparam, lparam);
}

int main()
{
    HINSTANCE hinst = GetModuleHandleA(NULL);

    HICON icon = LoadIcon(hinst, IDI_APPLICATION);
    
    WNDCLASSA* wc = (WNDCLASSA*)memset(malloc(sizeof(WNDCLASSA)), 0, sizeof(WNDCLASSA));
    if (wc == NULL)
    {
        return -1;
    }

    wc->style = CS_DBLCLKS;
    wc->lpfnWndProc = WIN32_processMessage;
    wc->cbClsExtra = 0;
    wc->cbWndExtra = 0;
    wc->hInstance = hinst;
    wc->hIcon = icon;
    wc->hCursor = LoadCursor(NULL, IDC_ARROW);
    wc->hbrBackground = NULL;
    wc->lpszClassName = "toast_window_class";

    if (!RegisterClassA(wc))
    {
        return 1;
    }

    UINT32 windowX = 100;
    UINT32 windowY = 100;
    UINT32 windowWidth = width;
    UINT32 windowHeight = height;

    UINT32 windowStyle = WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION;
    UINT32 windowExStyle = WS_EX_APPWINDOW;

    windowStyle |= WS_MAXIMIZEBOX;
    windowStyle |= WS_MINIMIZEBOX;
    windowStyle |= WS_THICKFRAME;

    RECT borderRect = { 0,0,0,0 };
    AdjustWindowRectEx(&borderRect, windowStyle, 0, windowExStyle);

    windowX += borderRect.left;
    windowY += borderRect.top;

    windowWidth += borderRect.right - borderRect.left;
    windowWidth += borderRect.bottom - borderRect.top;

    HWND window = CreateWindowExA(windowExStyle, "toast_window_class", (LPCSTR)"test",
        windowStyle, windowX, windowY, windowWidth, windowHeight, 0, 0, hinst, 0);

    if (window == 0)
    {
        return 2;
    }

    bool shouldActivate = true;
    const int showWindowCommandFlags = shouldActivate ? SW_SHOW : SW_SHOWNOACTIVATE;

    ShowWindow(window, showWindowCommandFlags);

    // WGL -----------------------------------------------------------
    HDC device = GetDC(window);

    PIXELFORMATDESCRIPTOR pfd;
    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cAlphaBits = 0;
    pfd.cAccumBits = 0;
    pfd.cDepthBits = 0;
    pfd.cStencilBits = 0;
    pfd.cAuxBuffers = 0;
    pfd.iLayerType = PFD_MAIN_PLANE;

    SetPixelFormat(device, ChoosePixelFormat(device, &pfd), &pfd);

    HGLRC render = wglCreateContext(device);

    wglMakeCurrent(device, render);

    // GLEW ---------------------------------------------------------

    const GLint res = glewInit();

    if (GLEW_OK != res)
    {
        return 1;
    }

    // setup OpenGL --------------------------------------------------
    gluOrtho2D(0, width, 0, height);

    // main loop -----------------------------------------------------
    while (running)
    {
        glClear(GL_COLOR_BUFFER_BIT);

        // rendering
        glBegin(GL_POINTS);
        for (int x = 10; x < width - 10; ++x)
        {
            for (int y = 10; y < height - 10; ++y)
            {
                glVertex2f(x, y);
            }
        }
        glEnd();

        SwapBuffers(device);
    }

    return 0;
}
4

2 回答 2

5

不要GL_POINTS用于逐像素绘制图像。

一方面,由于 OpenGL、它的实现和现代 GPU 的工作方式,它可能是最低效的方式(在任何系统上,在任何配置中)。相反 - 如果使用 OpenGL - 创建所需大小的 2D 数组,根据需要填充像素值,然后将图片加载到纹理中并绘制带纹理的四边形,或者更好的是使用裁剪的矩形覆盖三角形glScissor

回到您的点对点绘图问题。您正在运行的问题本质上是舍入错误以及 OpenGL 在模型视图空间、投影空间到 NDC 以及最终视口之间转换的方式。顺便说一句,您没有调用glViewport,因此不考虑窗口大小的任何调整。

调用glOrtho2D将设置转换矩阵(在您的情况下是模型视图,因为您没有费心切换到投影矩阵),以便范围内的坐标[0; width]×[0; height]将映射到 NDC 坐标[-1; 1]×[-1; 1]。这些范围是封闭的,即它们确实包括它们的极限值。然而,像素网格中的像素在整数范围内寻址,范围的末端是开放的(即ℤ²∩[0; width(×[0; height()。这意味着,如果您将视口范围用于未经调整的正交投影,则像素索引和 OpenGL 坐标之间将存在如此微小的差异。作为练习,我将把它留给您,以计算出要进行哪种调整的数学运算(提示,您必须将视口范围的某个小数值减去某些 glOrtho 参数)。

现在对于某些窗口大小,您不会注意到这个问题,因为对于您放入 OpenGL 中的所有“像素”位置,四舍五入将恰好强制它们朝向您想要的像素位置。但是对于其他窗口大小,对于某些坐标值,四舍五入是另一种方式,并且您的列和/或行保持不变。这会导致您有时看到的线条。

于 2022-02-06T10:39:59.537 回答
-1

所以我设法修复它。他们的关键是在第 49 和 54 行。我已经注释掉了导致此示例中问题的部分:

UINT32 windowStyle = WS_OVERLAPPED | WS_SYSMENU; //| WS_CAPTION;
UINT32 windowExStyle = WS_EX_APPWINDOW;

windowStyle |= WS_MAXIMIZEBOX;
windowStyle |= WS_MINIMIZEBOX;
//windowStyle |= WS_THICKFRAME;

我对 WIN32 窗口代码不是很熟悉。这真的是我第一次尝试。因此,如果有人可以添加一些评论,我将欢迎任何评论。

于 2022-02-06T10:26:22.660 回答