【问题标题】:Rotating Pixel Art with 2x Pixels in OpenGL在 OpenGL 中使用 2x 像素旋转像素艺术
【发布时间】:2023-03-30 06:32:01
【问题描述】:

我有一款使用像素艺术风格的游戏,将其放大,因此 1 个像素等于屏幕上的 2x2 区域。然而,当我在 OpenGL 中旋转一个精灵时,它会使用屏幕像素来绘制它,因此它打破了低分辨率风格游戏的错觉:

如何旋转精灵并使用较大的像素进行绘制?现在精灵是使用一个名为 Draw 的 Sprite 类绘制的,代码如下所示:

void Sprite::Draw(int x, int y, int w, int h, int tx = 0, int ty = 0, int tw = 1, int th = 1, int rotation = 0, int rx = 0, int ry = 0, int sr = 0, float r = 1, float g = 1, float b = 1) {
glEnable(GL_TEXTURE_2D);

glTranslatef(x+(w/2), y+(h/2), 0);
glRotatef((float) sr, 0.0f, 0.0f, 1.0f);
glTranslatef(-x-(w/2), -y-(h/2), 0);

glTranslatef(x+(w/2)+rx, y+(h/2)+ry, 0);
glRotatef((float) rotation, 0.0f, 0.0f, 1.0f);
glTranslatef(-(w/2)-rx, -(h/2)-ry, 0);
glColor3f(r, g, b);
glBindTexture(GL_TEXTURE_2D, texture);

const float verts[] = {
    0, (float) h,
    (float) w, (float) h,
    0, 0,
    (float) w, 0
};

const float tVerts[] = {
    (float)tx/(float)width, ((float)ty+(float)th)/(float)height,
    ((float)tx+(float)tw)/(float)width, ((float)ty+(float)th)/(float)height,
    (float)tx/(float)width, (float)ty/(float)height,
    ((float)tx+(float)tw)/(float)width, (float)ty/(float)height
};

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glVertexPointer(2, GL_FLOAT, 0, verts);
glTexCoordPointer(2, GL_FLOAT, 0, tVerts);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glLoadIdentity();

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_TEXTURE_2D);
}

谢谢!

编辑:我想我应该提到我正在使用 SDL2 进行窗口管理。

【问题讨论】:

  • 所以你想旋转,然后高档?我认为这可能最终看起来很可怕。传统的像素艺术游戏不进行旋转,或者它们具有用于一小部分方向的手绘精灵。
  • @Thomas 可能,我不知道。我可以手绘精灵旋转,但这个游戏有一些轨道机制,所以能够非常精确地指向船很重要,这意味着它可能需要 100 个左右的旋转精灵。
  • 如果问题是您看到“旋转像素”,则考虑缩小到“实际”像素,旋转,然后再次放大。
  • @Jongware Scaling 似乎不起作用,因为 OpenGL 将形状视为矩阵。缩放矩阵只是缩放大小,而不是纹理本身。

标签: c++ opengl


【解决方案1】:

您可以以一半的分辨率渲染整个场景,然后将其放大。为此,您使用 FBO(帧缓冲区对象)进行主要渲染,然后将其 blit 到默认帧缓冲区。

在设置过程中,创建您的 FBO 和渲染目标,并将渲染目标附加到 FBO:

GLuint fboId = 0;
glGenFramebuffers(1, &fboId);

GLuint rbId = 0;
glGenRenderbuffers(1, &rbId);
// Need to bind this once so object is created.
glBindRenderbuffer(GL_RENDERBUFFER, rbId);
glBindRenderbuffer(GL_RENDERBUFFER, 0);

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbId);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

在每次调整窗口大小时,将窗口大小调整为width/height,将渲染目标分配为窗口大小的一半。如果您的窗口不可调整大小,您可以将其与设置代码结合使用:

glBindRenderbuffer(GL_RENDERBUFFER, rbId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width / 2, height / 2);
glBindRenderbuffer(GL_RENDERBUFFER, 0);

在每次重绘时,渲染到 FBO,并将结果 blit 到默认帧缓冲区:

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
glViewport(0, 0, width / 2, height / 2);

// draw content

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glBlitFramebuffer(0, 0, width / 2, height / 2,
                  0, 0, width, height,
                  GL_COLOR_BUFFER_BIT, GL_NEAREST);

glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);

以上假设widthheight 是偶数。如果它们很奇怪,您可能需要稍微调整一下。

【讨论】:

  • 看起来很有希望......但是一旦我生成一个帧缓冲区,它就会出现段错误。
  • 大部分是 GL 3.x 级别的功能。您可能没有支持此版本的上下文。
  • 如何在不摆脱老式非着色器 GL 代码的情况下使用 OpenGL 3 的东西?
  • 实际上我现在正在链接 GLEW,它没有段错误。进步!
  • 如果您的平台支持兼容性配置文件,您可以将其与旧功能一起使用。请参阅wiki.libsdl.org/SDL_GL_SetAttribute,了解如何使用 SDL 配置上下文,尤其是 SDL_GL_CONTEXT_* 属性。
猜你喜欢
  • 2016-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-23
相关资源
最近更新 更多