jay
jay

Reputation: 1071

How to prevent opengl drawing stretching to window size?

I wrote an OpenGL program in windows about showing a texture on window. But it turns out the final result stretching to the initial size of window regardless there is no reference to window size in OpenGL commands. What I want to achieve is to draw the full texture even if the initial window size is smaller than the size of texture, no stretching is wanted. What did I miss?

code below:

#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);
}

Upvotes: 0

Views: 1344

Answers (1)

datenwolf
datenwolf

Reputation: 162367

You never call glViewport and thus the initial viewport size is set to the window dimensions at the moment of making OpenGL context current on the window for the first time.

To properly reflect window size changes you must call glViewport (for setting the mapping between NDC space and window space) and also in the vertex shader apply an appropriate transformation from vertex position space into clip space (the transformation from clip space to NDC is hardwired). If you don't apply perspective divide then the clip space coordinate range [-1,1] maps to the extents of the window space viewport.

Upvotes: 3

Related Questions