如何防止opengl绘图拉伸到窗口大小?

时间:2017-08-24 08:36:08

标签: c++ opengl

我在Windows中编写了一个关于在窗口上显示纹理的OpenGL程序。但事实证明,无论OpenGL命令中没有对窗口大小的引用,最终结果都会延伸到窗口的初始大小。我想要实现的是即使初始窗口尺寸小于纹理尺寸也不需要拉伸,即可绘制完整纹理。我错过了什么?

以下代码:

#include "stdafx.h"
#include <wingdi.h>
#include <gl\gl.h>
#include <stdio.h>
#include <assert.h>
#pragma comment(lib, "opengl32.lib")

#define MAX_LOADSTRING 100

HINSTANCE hInst;

TCHAR szWindowClass[MAX_LOADSTRING];

HANDLE g_hEvent;

static HWND wgl_Wnd;


ATOM                MyRegisterClass(HINSTANCE hInstance);
HWND                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

DWORD WINAPI
WindowThread(LPVOID lpParam)
{
    MSG msg;
    HINSTANCE hInstance = (HINSTANCE)lpParam;

    HWND hWnd = InitInstance(hInstance, SW_SHOW);
    if (!hWnd) {
        return 0;
    }

    wgl_Wnd = hWnd;
    SetEvent(g_hEvent);

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

char *g_data = NULL;

#define IWIDTH  752
#define IHEIGHT 1334
#define FWIDTH  752.0f
#define FHEIGHT 1334.0f

#define GLSL(version, shader)  "#version " #version "\n" #shader

static const char* SIMPLE_VS = GLSL(120,
    attribute vec4 a_pos;
    attribute vec2 a_tex;
    varying vec2 v_tex;
    uniform mat4 u_pm;
    uniform mat4 u_mm;
    void main() {
        gl_Position = u_pm * u_mm * a_pos;
        v_tex = a_tex;
    }
);

static const char* SIMPLE_FS = GLSL(120,
    uniform sampler2DRect u_tex;
    varying vec2 v_tex;
    void main() {
        gl_FragColor.a = 1.0;
        gl_FragColor.rgb = texture2DRect(u_tex, v_tex).rgb;
    }
);

static void print_shader_compile_info(GLuint shader) {
    GLint status = 0;
    GLint count = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if (!status) {
        assert(0);
    }
}

static GLuint create_shader(GLenum type, const char* src) {
    GLuint s = glCreateShader(type);
    glShaderSource(s, 1, &src, NULL);
    glCompileShader(s);
    print_shader_compile_info(s);
    return s;
}

GLuint g_tex;
GLuint g_glbuf;
GLuint g_prog;
GLuint g_vao;
GLfloat g_pm[16];
GLint g_u_mm;
GLint g_u_pm;
GLint g_u_tex;

static VOID loadData()
{
    FILE *file = fopen("f:\\tmp\\test001.raw", "rb");
    long fsize;
    fseek(file, 0, SEEK_END);
    fsize = ftell(file);
    fseek(file, 0, SEEK_SET);

    g_data = new char[fsize];
    fread(g_data, 1, fsize, file);

    fclose(file);
}



static VOID wglRender(HDC hdc)
{
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, g_glbuf);
    void *glptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);

    memcpy(glptr, g_data, IWIDTH * IHEIGHT * 4);
    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);

    glBindTexture(GL_TEXTURE_RECTANGLE, g_tex);
    glTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, 0, 0, IWIDTH, IHEIGHT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);

    GLfloat mm[16] = {
        1.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, -5.0f, 1.0f
    };

    glBindVertexArray(g_vao);
    glUseProgram(g_prog);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_RECTANGLE, g_tex);
    glUniform1i(g_u_tex, 0);

    glUniformMatrix4fv(g_u_mm, 1, GL_FALSE, mm);
    glUniformMatrix4fv(g_u_pm, 1, GL_FALSE, g_pm);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    SwapBuffers(hdc);
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    MyRegisterClass(hInstance);

    // custom code starts here.

    g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    CloseHandle(CreateThread(NULL, 0, WindowThread, (LPVOID)hInstance, 0, NULL));
    WaitForSingleObject(g_hEvent, INFINITE);
    CloseHandle(g_hEvent);

    PIXELFORMATDESCRIPTOR pfd = {0};
    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 = 0x20;
    pfd.cRedBits = 8;
    pfd.cGreenBits = 8;
    pfd.cBlueBits = 8;
    pfd.cDepthBits = 0x18;

    HDC hdc = GetDC(wgl_Wnd);
    int pf = ChoosePixelFormat(hdc, &pfd);
    SetPixelFormat(hdc, pf, &pfd);

    HGLRC hGlrc = wglCreateContext(hdc);
    wglMakeCurrent(hdc, hGlrc);

    loadData();

    GLuint vert = create_shader(GL_VERTEX_SHADER, SIMPLE_VS);
    GLuint frag = create_shader(GL_FRAGMENT_SHADER, SIMPLE_FS);
    g_prog = glCreateProgram();
    glAttachShader(g_prog, vert);
    glAttachShader(g_prog, frag);
    glLinkProgram(g_prog);

    g_u_mm = glGetUniformLocation(g_prog, "u_mm");
    g_u_pm = glGetUniformLocation(g_prog, "u_pm");
    g_u_tex = glGetUniformLocation(g_prog, "u_tex");

    float n = 0.0f;
    float f = 10.0f;
    float ww = FWIDTH;
    float hh = FHEIGHT;
    float fmn = f - n;

    for (int i = 0; i < 16; i++) {
        g_pm[i] = 0.0f;
    }


    g_pm[0] = 2.0f / ww;
    g_pm[5] = 2.0f / -hh;
    g_pm[10] = -2.0f / fmn;
    g_pm[12] = -(ww) / ww;
    g_pm[13] = -(hh) / -hh;
    g_pm[14] = -(f + n) / fmn;
    g_pm[15] = 1.0f;
    //

    glBindTexture(GL_TEXTURE_RECTANGLE, 0);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
    glUseProgram(0);

    glGenTextures(1, &g_tex);
    glBindTexture(GL_TEXTURE_RECTANGLE, g_tex);
    glGenBuffers(1, &g_glbuf);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, g_glbuf);
    glBufferData(GL_PIXEL_UNPACK_BUFFER, IWIDTH * IHEIGHT* 4, NULL, GL_DYNAMIC_DRAW);
    glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, IWIDTH, IHEIGHT, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
    glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glGenVertexArrays(1, &g_vao);
    glBindVertexArray(g_vao);
    GLfloat vertices[] = {
        0.0f, 0.0f, 0.0f, 0.0f,
        IWIDTH, 0.0f, IWIDTH, 0.0f,
        IWIDTH, IHEIGHT, IWIDTH, IHEIGHT,

        0.0f, 0.0f, 0.0f, 0.0f,
        IWIDTH, IHEIGHT, IWIDTH, IHEIGHT,
        0.0f, IHEIGHT, 0.0f, IHEIGHT
    };


    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0); // pos
    glEnableVertexAttribArray(1); // tex
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (GLvoid*)0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (GLvoid*)8);


    while(TRUE) {
        if(!IsWindow(wgl_Wnd)) {
            break;
        }
        Sleep(1);

        wglRender(hdc);
    }

    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(hGlrc);
    ReleaseDC(wgl_Wnd, hdc);
    DestroyWindow(wgl_Wnd);

    return 0;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon      = NULL;
    wcex.hCursor        = NULL;
    wcex.hbrBackground  = NULL;
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = _T("MyClassName");
    wcex.hIconSm        = NULL;

    return RegisterClassEx(&wcex);
}

HWND InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;

    hInst = hInstance;

    RECT rect = { 0, 0, 600, 600 };
    AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_CLIENTEDGE);

    hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("MyClassName"), _T("MyTitle"), WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, 0, NULL);

    if (!hWnd)
    {
        return NULL;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return hWnd;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hWnd, message, wParam, lParam);
}

1 个答案:

答案 0 :(得分:3)

您永远不会调用glViewport,因此初始视口大小设置为首次在窗口上使OpenGL上下文变为当前时的窗口尺寸。

要正确反映窗口大小的变化,您必须调用glViewport(用于设置NDC空间和窗口空间之间的映射),并且在顶点着色器中应用从顶点位置空间到剪辑空间的适当变换(从剪辑空间到NDC是硬连线的)。如果不应用透视分割,则剪辑空间坐标范围[-1,1]将映射到窗口空间视口的范围。