如何将 LWJGL 游戏场景渲染到 ByteBuffer?
How to render a LWJGL game scene to a ByteBuffer?
我目前正在进行一个项目,该项目涉及将 LWJGL 游戏场景渲染为视频流而不是 window。我相信如果我将游戏场景渲染为中间格式(例如 ByteBuffer),我可以实现这一点。我正在尝试扩展 LWJGL VoxelGame
demo 作为概念证明。
我找到了一个 and a forum post 但我没能成功。我是 OpenGL 和 LWJGL 的初学者,我正在努力寻找相关的可理解文档。
在渲染循环 (runUpdateAndRenderLoop
) 开始时,函数 glBindFramebuffer
被调用。据我了解,它将 FBO 绑定到当前上下文,以便任何渲染都将定向到它。
我曾尝试使用 glGetTexImage
和 glReadPixels
来填充 ByteBuffer,但没有成功。我也在 glBlitFramebuffer
之后尝试过,因为我想将完整的渲染图像获取到 ByteBuffer。
如何将当前游戏场景渲染到 ByteBuffer?有没有更好的方法将游戏场景渲染到视频流而不是中间 ByteBuffer?
private void runAndUpdateRenderLoop() {
// ...
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
byte[] pixels = new byte[width * height * 4];
ByteBuffer buffer = ByteBuffer.allocateDirect(pixels.length).order(ByteOrder.nativeOrder());
glGetTexImage(GL_TEXTURE_BUFFER, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glfwSwapBuffers(window);
}
你的代码中有一堆问题,甚至还没有看到问题的核心,因为它在 // ...
部分:
当你尝试在无头设置中渲染时,你不会使用 glfwSwapBuffers
,只有当你渲染到上下文后台缓冲区并想将它交换到屏幕时才需要.
你不需要glGetTexImage
和glReadPixels
,它们都是从显存读取数据到系统内存。在您的情况下,您正在读取 buffer
中的纹理数据,然后用后缓冲区数据覆盖它,后缓冲区数据应该是空的,因为您从未写入它。
你肯定不要glBlitFramebuffer
,那是为了从显存复制到显存。你真的需要停下来阅读文档,在你的视频驱动程序中使用随机函数肯定会导致它崩溃。
您需要启用 OpenGL 的调试层验证,尤其是在尝试所有这些随机函数时。与此相关的是,您需要检查您的帧缓冲区设置 (glCheckFramebufferStatusEXT
),我愿意打赌您没有这样做,但您没有显示您的代码。
你没有显示你的帧缓冲区绑定,所以我不能确定你是否在这样做,但你需要将帧缓冲区绑定为“读取”( GL_READ_FRAMEBUFFER
) 在阅读之前。我只看到一个“绘制”帧缓冲区绑定,而你只是随机清除它。
最后,你不应该在从视频内存读取操作时停止 CPU,尤其是当你有你应该做的事情时,比如视频编码。以循环方式使用像素缓冲对象对传输进行双倍或三倍缓冲,因此不会出现停顿。
@Blindy 的回答 100% 正确,您应该接受它作为答案。
但是,如果您想要 直接 工作解决方案的代码,请在
之后插入以下内容
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
但是之前
glfwSwapBuffers(window);
代码:
ByteBuffer bb = org.lwjgl.system.MemoryUtil.memAlloc(width * height * 4);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bb);
// Test with stb_image to write as jpeg:
// org.lwjgl.stb.STBImageWrite.stbi_flip_vertically_on_write(true);
// org.lwjgl.stb.STBImageWrite.stbi_write_jpg("frame.jpg", width, height, 4, bb, 50);
org.lwjgl.system.MemoryUtil.memFree(bb);
这将导致 ByteBuffer bb
保存当前帧的像素数据。
然而,正如@Blindy 也指出的那样,这是一个严重的 GPU 停顿,因为您被迫等待当前帧的数据完全渲染并强制 GPU->CPU 传输到您的字节缓冲区。
当您启用调试消息输出时,Nvidia 的驱动程序也会向您大喊:
Pixel-path performance warning: Pixel transfer is synchronized with 3D rendering.
根据您的实际用例,其他方法可能更有用,例如直接从 GPU 内存编码视频(例如使用 NVENC)。
我目前正在进行一个项目,该项目涉及将 LWJGL 游戏场景渲染为视频流而不是 window。我相信如果我将游戏场景渲染为中间格式(例如 ByteBuffer),我可以实现这一点。我正在尝试扩展 LWJGL VoxelGame
demo 作为概念证明。
我找到了一个
在渲染循环 (runUpdateAndRenderLoop
) 开始时,函数 glBindFramebuffer
被调用。据我了解,它将 FBO 绑定到当前上下文,以便任何渲染都将定向到它。
我曾尝试使用 glGetTexImage
和 glReadPixels
来填充 ByteBuffer,但没有成功。我也在 glBlitFramebuffer
之后尝试过,因为我想将完整的渲染图像获取到 ByteBuffer。
如何将当前游戏场景渲染到 ByteBuffer?有没有更好的方法将游戏场景渲染到视频流而不是中间 ByteBuffer?
private void runAndUpdateRenderLoop() {
// ...
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
byte[] pixels = new byte[width * height * 4];
ByteBuffer buffer = ByteBuffer.allocateDirect(pixels.length).order(ByteOrder.nativeOrder());
glGetTexImage(GL_TEXTURE_BUFFER, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glfwSwapBuffers(window);
}
你的代码中有一堆问题,甚至还没有看到问题的核心,因为它在 // ...
部分:
当你尝试在无头设置中渲染时,你不会使用
glfwSwapBuffers
,只有当你渲染到上下文后台缓冲区并想将它交换到屏幕时才需要.你不需要
glGetTexImage
和glReadPixels
,它们都是从显存读取数据到系统内存。在您的情况下,您正在读取buffer
中的纹理数据,然后用后缓冲区数据覆盖它,后缓冲区数据应该是空的,因为您从未写入它。你肯定不要
glBlitFramebuffer
,那是为了从显存复制到显存。你真的需要停下来阅读文档,在你的视频驱动程序中使用随机函数肯定会导致它崩溃。您需要启用 OpenGL 的调试层验证,尤其是在尝试所有这些随机函数时。与此相关的是,您需要检查您的帧缓冲区设置 (
glCheckFramebufferStatusEXT
),我愿意打赌您没有这样做,但您没有显示您的代码。你没有显示你的帧缓冲区绑定,所以我不能确定你是否在这样做,但你需要将帧缓冲区绑定为“读取”(
GL_READ_FRAMEBUFFER
) 在阅读之前。我只看到一个“绘制”帧缓冲区绑定,而你只是随机清除它。最后,你不应该在从视频内存读取操作时停止 CPU,尤其是当你有你应该做的事情时,比如视频编码。以循环方式使用像素缓冲对象对传输进行双倍或三倍缓冲,因此不会出现停顿。
@Blindy 的回答 100% 正确,您应该接受它作为答案。
但是,如果您想要 直接 工作解决方案的代码,请在
之后插入以下内容glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
但是之前
glfwSwapBuffers(window);
代码:
ByteBuffer bb = org.lwjgl.system.MemoryUtil.memAlloc(width * height * 4);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bb);
// Test with stb_image to write as jpeg:
// org.lwjgl.stb.STBImageWrite.stbi_flip_vertically_on_write(true);
// org.lwjgl.stb.STBImageWrite.stbi_write_jpg("frame.jpg", width, height, 4, bb, 50);
org.lwjgl.system.MemoryUtil.memFree(bb);
这将导致 ByteBuffer bb
保存当前帧的像素数据。
然而,正如@Blindy 也指出的那样,这是一个严重的 GPU 停顿,因为您被迫等待当前帧的数据完全渲染并强制 GPU->CPU 传输到您的字节缓冲区。 当您启用调试消息输出时,Nvidia 的驱动程序也会向您大喊:
Pixel-path performance warning: Pixel transfer is synchronized with 3D rendering.
根据您的实际用例,其他方法可能更有用,例如直接从 GPU 内存编码视频(例如使用 NVENC)。