我在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);
}
答案 0 :(得分:3)
您永远不会调用glViewport
,因此初始视口大小设置为首次在窗口上使OpenGL上下文变为当前时的窗口尺寸。
要正确反映窗口大小的变化,您必须调用glViewport
(用于设置NDC空间和窗口空间之间的映射),并且在顶点着色器中应用从顶点位置空间到剪辑空间的适当变换(从剪辑空间到NDC是硬连线的)。如果不应用透视分割,则剪辑空间坐标范围[-1,1]将映射到窗口空间视口的范围。