OpenGL ES 像素艺术 - 缩放
OpenGL ES Pixel Art - Scaling
我在 iPhones 上的 OpenGL Es 1.1 上显示基于像素的艺术(想想复古瓷砖和艺术)时遇到问题。
图块使用 8 个字节(每行 1 个字节)表示,每个位表示一个像素是否被设置。
例如数字为 8 的方块:
0 0 0 0 0 0 0 0 ->
0 0 1 1 1 0 0 0 -> xxx
0 1 0 0 0 1 0 0 -> x x
0 1 0 0 0 1 0 0 -> x x
0 0 1 1 1 0 0 0 -> xxx
0 1 0 0 0 1 0 0 -> x x
0 1 0 0 0 1 0 0 -> x x
0 0 1 1 1 0 0 0 -> xxx
使用
在 iPhone 上将其转换为 OpenGL
glDrawArrays(GL_POINTS, 0, 64);
逻辑是对的,问题是没有复古效果。我正在寻找更多 blocky/retro 风格。我读到我可以关闭像素平滑,这会导致像素显示为正方形。(我认为它是 GL_Disable(POINT_SMOOTH),但不确定这是否会影响 ES,因为没有任何变化。)
我发现的相关问题的可能解决方案:
- 使用帧缓冲区渲染到较小的分辨率,然后在渲染缓冲区中将其放大。我不知道这是怎么做到的,也不知道它是否有效。
- 从像素创建图像,从该图像创建纹理,最后渲染该纹理。
我想到的可能的解决方案:
- 对于每个像素,水平和垂直绘制两个像素。
- 使用三角形将每个像素绘制为正方形。
- 使用 GLPointSize - 设置为 2 时会产生正确的效果,但坐标会变得混乱。对齐变得更加困难。
最终我想要展示的瓷砖:
这是我对 OpenGL 和像素如何工作的更多理解,我正在使用 gameboy 模拟器来解决这个问题。如果有人认为正确的方法是手动创建图形并将它们加载为纹理,那不是可行的答案。
有很多方法可以做到这一点,我会建议您已经找到的第一个。将场景绘制到较小的缓冲区,然后重新绘制 canvas.
您在此处查找的是 FBO(帧缓冲区对象)。找到一些关于如何创建 FBO 并为其附加纹理的示例。这将为您创建一个缓冲区,其中包含您输入的任何尺寸。这里的一些常见问题是您很可能需要 POT 纹理(2 维的幂:2、4、8、16 ... 例如 64x128 缓冲区),因此要控制不同的大小,您应该使用视口,然后只使用你需要的缓冲区的一部分。
所以最后这将创建一个低分辨率纹理,可用于绘制到 canvas(视图)。你如何画一些你应该尝试的东西。这些点可能不是最好的解决方案,即使在您使用缓冲区的情况下,我也会使用您在示例中定义的点之间的线。此时您必须选择使用或不使用抗锯齿进行绘制。要启用它,请在 iOS.
上寻找多重采样
在您拥有绘制形状的纹理后,您需要将其重新绘制到视图中。这几乎是在绘制全屏纹理。同样,您有多种绘制方法。这里最强大的工具是纹理参数:使用最近的将丢弃所有颜色插值并且正方形应该是可见的;使用线性(或三线性)将进行一些插值,结果可能更接近您想要实现的目标。然后您可以再次使用多重采样来创建抗锯齿并获得更好的结果。
所以这里的权力是:
- 不同的 FBO 缓冲区大小
- FBO 抗锯齿
- 纹理参数
- 重绘时抗锯齿 canvas
至于 FBO,这是最简单的事情之一:
- 生成帧缓冲区(
glGenFramebuffers
)
- 绑定帧缓冲区(
glBindFramebuffer
)
- 创建纹理 (
glGenTextures
) 并绑定它 (glBindTexture
)
- 设置纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, twidth, theight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- 将纹理附加到帧缓冲区
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.textureID, 0);
- 现在,当您绘制到纹理时,您需要绑定 FBO 帧缓冲区,而当您绘制到主缓冲区时,只需绑定该帧缓冲区。
因为这是一个相当宽泛的答案(问题也是如此)...如果您要实施这个并且会有其他问题或问题,那么最好创建一个单独的问题并且可能 link他们在评论中。
祝你好运。
我不确定我的问题是否不清楚,但是要在屏幕上绘制像素,您必须创建一个纹理并将像素数据传递给它,然后将该纹理渲染到屏幕上。它相当于 glDrawPixels。
代码为:
#define W 255,255,255
#define G 192,192,192
//8 x 8 tile with 3 bytes for each pixel RGB format
GLubyte pixels[8 * 8 * 3] = {
W,W,W,W,W,W,W,W,
W,W,G,G,G,W,W,W,
W,G,W,W,W,G,W,W,
W,G,W,W,W,G,W,W,
W,W,G,G,G,W,W,W,
W,G,W,W,W,G,W,W,
W,G,W,W,W,G,W,W,
W,W,G,G,G,W,W,W
};
设置中的某处:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
然后像往常一样绘制纹理:
glActiveTexture(GL_TEXTURE0);
glUniform1i([program uniformLocation:@"s_texture"], 0);
glBindTexture(GL_TEXTURE_2D, tex);
glEnableVertexAttribArray(positionAttrib);
glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0, v);
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 0, t);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, i);
我在 iPhones 上的 OpenGL Es 1.1 上显示基于像素的艺术(想想复古瓷砖和艺术)时遇到问题。
图块使用 8 个字节(每行 1 个字节)表示,每个位表示一个像素是否被设置。
例如数字为 8 的方块:
0 0 0 0 0 0 0 0 ->
0 0 1 1 1 0 0 0 -> xxx
0 1 0 0 0 1 0 0 -> x x
0 1 0 0 0 1 0 0 -> x x
0 0 1 1 1 0 0 0 -> xxx
0 1 0 0 0 1 0 0 -> x x
0 1 0 0 0 1 0 0 -> x x
0 0 1 1 1 0 0 0 -> xxx
使用
在 iPhone 上将其转换为 OpenGLglDrawArrays(GL_POINTS, 0, 64);
逻辑是对的,问题是没有复古效果。我正在寻找更多 blocky/retro 风格。我读到我可以关闭像素平滑,这会导致像素显示为正方形。(我认为它是 GL_Disable(POINT_SMOOTH),但不确定这是否会影响 ES,因为没有任何变化。)
我发现的相关问题的可能解决方案:
- 使用帧缓冲区渲染到较小的分辨率,然后在渲染缓冲区中将其放大。我不知道这是怎么做到的,也不知道它是否有效。
- 从像素创建图像,从该图像创建纹理,最后渲染该纹理。
我想到的可能的解决方案:
- 对于每个像素,水平和垂直绘制两个像素。
- 使用三角形将每个像素绘制为正方形。
- 使用 GLPointSize - 设置为 2 时会产生正确的效果,但坐标会变得混乱。对齐变得更加困难。
最终我想要展示的瓷砖:
这是我对 OpenGL 和像素如何工作的更多理解,我正在使用 gameboy 模拟器来解决这个问题。如果有人认为正确的方法是手动创建图形并将它们加载为纹理,那不是可行的答案。
有很多方法可以做到这一点,我会建议您已经找到的第一个。将场景绘制到较小的缓冲区,然后重新绘制 canvas.
您在此处查找的是 FBO(帧缓冲区对象)。找到一些关于如何创建 FBO 并为其附加纹理的示例。这将为您创建一个缓冲区,其中包含您输入的任何尺寸。这里的一些常见问题是您很可能需要 POT 纹理(2 维的幂:2、4、8、16 ... 例如 64x128 缓冲区),因此要控制不同的大小,您应该使用视口,然后只使用你需要的缓冲区的一部分。
所以最后这将创建一个低分辨率纹理,可用于绘制到 canvas(视图)。你如何画一些你应该尝试的东西。这些点可能不是最好的解决方案,即使在您使用缓冲区的情况下,我也会使用您在示例中定义的点之间的线。此时您必须选择使用或不使用抗锯齿进行绘制。要启用它,请在 iOS.
上寻找多重采样在您拥有绘制形状的纹理后,您需要将其重新绘制到视图中。这几乎是在绘制全屏纹理。同样,您有多种绘制方法。这里最强大的工具是纹理参数:使用最近的将丢弃所有颜色插值并且正方形应该是可见的;使用线性(或三线性)将进行一些插值,结果可能更接近您想要实现的目标。然后您可以再次使用多重采样来创建抗锯齿并获得更好的结果。
所以这里的权力是:
- 不同的 FBO 缓冲区大小
- FBO 抗锯齿
- 纹理参数
- 重绘时抗锯齿 canvas
至于 FBO,这是最简单的事情之一:
- 生成帧缓冲区(
glGenFramebuffers
) - 绑定帧缓冲区(
glBindFramebuffer
) - 创建纹理 (
glGenTextures
) 并绑定它 (glBindTexture
) - 设置纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, twidth, theight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- 将纹理附加到帧缓冲区
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.textureID, 0);
- 现在,当您绘制到纹理时,您需要绑定 FBO 帧缓冲区,而当您绘制到主缓冲区时,只需绑定该帧缓冲区。
因为这是一个相当宽泛的答案(问题也是如此)...如果您要实施这个并且会有其他问题或问题,那么最好创建一个单独的问题并且可能 link他们在评论中。
祝你好运。
我不确定我的问题是否不清楚,但是要在屏幕上绘制像素,您必须创建一个纹理并将像素数据传递给它,然后将该纹理渲染到屏幕上。它相当于 glDrawPixels。
代码为:
#define W 255,255,255
#define G 192,192,192
//8 x 8 tile with 3 bytes for each pixel RGB format
GLubyte pixels[8 * 8 * 3] = {
W,W,W,W,W,W,W,W,
W,W,G,G,G,W,W,W,
W,G,W,W,W,G,W,W,
W,G,W,W,W,G,W,W,
W,W,G,G,G,W,W,W,
W,G,W,W,W,G,W,W,
W,G,W,W,W,G,W,W,
W,W,G,G,G,W,W,W
};
设置中的某处:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
然后像往常一样绘制纹理:
glActiveTexture(GL_TEXTURE0);
glUniform1i([program uniformLocation:@"s_texture"], 0);
glBindTexture(GL_TEXTURE_2D, tex);
glEnableVertexAttribArray(positionAttrib);
glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0, v);
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 0, t);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, i);