我正在使用LWJGL并尝试绘制纹理,其渲染代码如下:
public static void main(String[] args) {
GLFWErrorCallback.createPrint(System.err).set();
if (!GLFW.glfwInit()) {
throw new IllegalStateException("Unable to initialize GLFW");
}
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE);
window = GLFW.glfwCreateWindow(1280, 720, "Test", 0, 0);
GLFW.glfwMakeContextCurrent(window);
GL.createCapabilities();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 1280, 0, 720, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glViewport(0, 0, 1920, 1200);
GL11.glClearColor(1.0F, 1.0F, 1.0F, 1.0F);
int x = 0, y = 0;
ByteBuffer imageBuffer = readFile(filename);
IntBuffer w = BufferUtils.createIntBuffer(1);
IntBuffer h = BufferUtils.createIntBuffer(1);
IntBuffer comp = BufferUtils.createIntBuffer(1);
ByteBuffer image = STBImage.stbi_load_from_memory(imageBuffer, w, h, comp, 0);
int textureId = GL11.glGenTextures();
int glTarget = GL11.GL_TEXTURE_2D;
GL11.glBindTexture(glTarget, textureId);
glTexParameteri(glTarget, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
glTexParameteri(glTarget, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
glTexParameteri(glTarget, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
glTexParameteri(glTarget, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
int width = w.get(0);
int height = h.get(0);
/** Send texel data to OpenGL if texture is 2d texture */
if (glTarget == GL11.GL_TEXTURE_2D) {
if(comp.get(0) == 3){
GL11.glTexImage2D(glTarget, 0, GL11.GL_RGB, width, height, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, image);
}
else{
GL11.glTexImage2D(glTarget, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, image);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
}
}
while (Sgl.window.isAlive()) {
GLFW.glfwPollEvents();
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
/* Texture display part */
bind();
GL11.glEnable(glTarget);
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(0,0);
GL11.glVertex2f(x,y);
GL11.glTexCoord2f(1,0);
GL11.glVertex2f(x+width,y);
GL11.glTexCoord2f(1,1);
GL11.glVertex2f(x+width,y+height);
GL11.glTexCoord2f(0,1);
GL11.glVertex2f(x,y+height);
GL11.glEnd();
GL11.glDisable(glTarget);
/*End texture display part*/
GLFW.glfwSwapBuffers(window);
}
}
问题是窗口大1280x720,图像只有392x69,但显示方式如下:
因此,它处于颠倒状态,比预期的要大得多,并且位置错误。
我究竟做错了什么?
编辑:由于代码的大小,我删除了一些if子句。
最佳答案
逐一处理您的问题
1.所以,它是颠倒的,
OpenGL的纹理坐标(不仅是GL,对于所有常见的渲染API都是如此)的定义方式是,原点将在上载数据时指定的第一个像素处为b。定义和加载图像时最先考虑的是从左到右,从上到下的约定-因此,要分配(0,0)texcoords的顶点将显示图像的右上角。
现在,GL的窗口空间(至少默认情况下)是根据数学约定定义的,原点位于左下角。您会发现一些投影矩阵:
GL11.glOrtho(0,1280,0,720,1,-1);
这会将x=0
映射到x_ndc=-1
,将x=1280
映射到x_ndc=1
,并且将y=0
映射到y_ndc=-1
,将y=720
映射到y_ndc=1
。 (它将映射刚相对于您指定的范围z
翻转的[1,-1]
坐标,因此z=-1
到z_ndc=-1
以及z=1
到z_ndc=1
,但这在这里无关紧要。)
NDC是normalized device coordinates
,其中(-1,-1)
是视口的左下角,而(1,1)
是右上角。
因此,当您现在执行以下操作时:
GL11.glVertex2f(x,y); // NOTE: x and y are just 0 here
GL11.glTexCoord2f(1,0);
将应用上述变换,并且具有左上角texel的顶点将最终在视口的左下角。
2.比预期大得多
您可以如下设置视口转换:
GL11.glViewport(0, 0, 1920, 1200);
现在,这只是定义了另一个转换,现在是从NDC到窗口空间。只是
x_ndc=-1
分别映射到x_win=0
x_ndc=1
到x_win=1920
和y
。因此,输入coordiante
(0,0)
在NDC中映射为(-1,1)
,在窗口空间(仍为左下角)中进一步映射为(0,0)
。 (392,93)
将被映射到NDC中的~(0.3,0.129)
,并且将被映射到窗口空间中的(588,115)
-这比您的图像实际大得多。它仍然应该适合您的窗口,但是从屏幕截图看来,它似乎不合适。可能有几种解释:
您的代码并不能完全重现问题,并且
由于代码的大小,我删除了一些if子句。
可能与此无关。
您正在使用某些“高DPI缩放比例”(如Microsoft所说)或操作系统的类似功能。您在GLFW中指定的窗口大小不是以像素为单位,而是以某些特定于系统和平台的单位。 GLFW提供means to query the actual pixel sizes,您应使用该来为窗口设置适当的视口。
...
3.并且在错误的位置
这也是您不匹配OpenGL坐标约定的结果。
4.我做错了什么?
您编写的代码使用了不推荐使用的OpenGL。您所依赖的是十年前已经不推荐使用的固定功能管道。这些功能已从OpenGL的现代核心配置文件中完全删除。甚至在此之前,
glBegin()/glEnd()
的即时模式渲染基本上早在20年前就已被顶点数组取代。如果您现在正在学习OpenGL,则应该真正避免学习那些旧知识,而从一个干净的核心配置文件OpenGL上下文开始。