旧版OpenGL纹理渲染异常求助:自定义3x3 RGBA纹理无法正确显示
各位好,我正在迁移一个依赖旧版OpenGL的程序,为了复现下遇到的纹理渲染问题写了这个测试示例(请原谅用了老风格的OpenGL,暂时这样能简化迁移流程)。
我定义了一个3x3的RGBA纹理图案,预期能在窗口中显示对应的彩色四边形,但实际运行后只有左上角的红色纹理像素是对的,其他像素都像是被跳过或者错位了。我一开始怀疑是非2的幂尺寸的问题,但调整后问题依然存在,实在找不到原因,麻烦大家帮忙看看哪里出错了?
测试代码(main.c)
#include <SDL_opengl.h> #include <stdio.h> #define INITIAL_SCREEN_WIDTH 640 #define INITIAL_SCREEN_HEIGHT 480 #define PATTERN_WIDTH 3 #define PATTERN_HEIGHT 3 int screenWidth, screenHeight; const float quadWidth = 300.0f; const float quadHeight = 300.0f; float quadX = (INITIAL_SCREEN_WIDTH / 2) - (quadWidth / 2); float quadY = (INITIAL_SCREEN_HEIGHT / 2) - (quadHeight / 2); GLuint texture; #define RED 0xff0000ff #define GREEN 0xff00ff00 #define BLUE 0xffff0000 static unsigned long pattern[] = { RED, GREEN, BLUE, GREEN, BLUE, RED, BLUE, RED, GREEN }; void setupOpenGL(int width, int height) { screenWidth = width; screenHeight = height; glViewport(0, 0, screenWidth, screenHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, INITIAL_SCREEN_WIDTH, INITIAL_SCREEN_HEIGHT, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PATTERN_WIDTH, PATTERN_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, pattern); } void drawQuad() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_QUADS); glColor3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0, 0.0); glVertex2f(quadX, quadY); glTexCoord2f(1.0, 0.0); glVertex2f(quadX + quadWidth, quadY); glTexCoord2f(1.0, 1.0); glVertex2f(quadX + quadWidth, quadY + quadHeight); glTexCoord2f(0.0, 1.0); glVertex2f(quadX, quadY + quadHeight); glEnd(); SDL_GL_SwapWindow(SDL_GL_GetCurrentWindow()); } int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); return 1; } SDL_Window* window = SDL_CreateWindow("OpenGL Quad Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, INITIAL_SCREEN_WIDTH, INITIAL_SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!window) { fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } SDL_GLContext glContext = SDL_GL_CreateContext(window); if (!glContext) { fprintf(stderr, "OpenGL context could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } setupOpenGL(INITIAL_SCREEN_WIDTH, INITIAL_SCREEN_HEIGHT); int quit = 0; while (!quit) { SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = 1; } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { int newWidth = event.window.data1; int newHeight = event.window.data2; setupOpenGL(newWidth, newHeight); } } drawQuad(); } SDL_GL_DeleteContext(glContext); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
编译用Makefile
CFLAGS = -Wall -Wextra -std=c99 INCLUDES = $(shell pkg-config --cflags sdl2) LIBS = $(shell pkg-config --libs sdl2) -lGL OUTPUT = main SRC = main.c all: $(OUTPUT) $(OUTPUT): $(SRC) $(CC) $(CFLAGS) $(INCLUDES) -o $(OUTPUT) $(SRC) $(LIBS) clean: rm -f $(OUTPUT) .PHONY: all clean
问题根源与修复方案
你遇到的核心问题是纹理数据数组的类型不匹配:
在64位Ubuntu系统中,unsigned long是8字节长度,但你的纹理每个像素是4字节的RGBA数据,而glTexImage2D指定了GL_UNSIGNED_BYTE(每个分量1字节,每个像素共4字节),这导致OpenGL读取纹理数据时发生了错位:第一个像素读取了RED的前4字节(正确显示红色),但第二个像素读取的是RED的后4字节(全0,透明黑色),后续像素的读取全部错位,自然显示异常。
修复步骤:
修正纹理数据数组类型:
把unsigned long改成uint32_t(需要包含<stdint.h>头文件)或者unsigned int(32位,和你的RGBA宏尺寸匹配):#include <stdint.h> // ... static const uint32_t pattern[] = { RED, GREEN, BLUE, GREEN, BLUE, RED, BLUE, RED, GREEN };同步
glTexImage2D的类型参数:
因为现在每个元素是32位整数,需要告诉OpenGL正确的数据解析方式,把glTexImage2D的最后两个参数调整为:glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PATTERN_WIDTH, PATTERN_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, pattern);这里用
GL_UNSIGNED_INT_8_8_8_8是为了匹配你定义的AARRGGBB格式宏,确保颜色通道顺序正确。如果你想更直观避免字节顺序问题,也可以直接用
unsigned char数组定义每个像素的分量:static const unsigned char pattern[] = { // 第一行:RED, GREEN, BLUE 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, // 第二行:GREEN, BLUE, RED 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, // 第三行:BLUE, RED, GREEN 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF };这种方式下,
glTexImage2D保持原来的GL_UNSIGNED_BYTE参数即可,完全不会有类型错位问题。额外优化:修正窗口resize时的投影矩阵
目前你的setupOpenGL中,不管窗口实际尺寸是多少,glOrtho都用固定的初始尺寸,这会导致窗口resize后画面拉伸。建议改成用当前窗口的尺寸:glOrtho(0, screenWidth, screenHeight, 0, -1, 1);
做完这些修改后,纹理应该就能正确显示你定义的3x3图案了。
备注:内容来源于stack exchange,提问作者denormal

