用PyOpenGL读取深度缓冲区 [英] Reading depth buffer with PyOpenGL

查看:135
本文介绍了用PyOpenGL读取深度缓冲区的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本上,我正在尝试在渲染模型(脚本为从指定路径作为第一个命令行参数的文件中加载模型).

Basically, I'm trying to extract a depth map (by this I mean a matrix with z corresponding to z-coordinates for vertices in GL.glVertex3dv(vertex) call - obviously, interpolated for plane pixels) after rendering a model (script is loading the model from file with path specified as the first command line argument).

我想到了几个问题:

  • 为什么glReadPixels调用返回形状为(width, shape)而不是(height, width)的numpy数组?

  • why glReadPixels call returns a numpy array with shape (width, shape), instead of (height, width)?

为什么会返回一些未连接到渲染模型的垃圾?

why it returns some trash, not connected to the rendered model?

是否有一种简单的方法可以使用PyOpenGL框架在OpenGL旧代码上获取z坐标?

is there an easy way to get z-coordinates on OpenGL legacy code with PyOpenGL framework?

我可以在此处获得的最大值是范围为[0; 1]的某个数组是否正确,基本上是zNear和zFar之间的某个分数(并且由于某种原因由glReadPixels进行了归一化)?

is it correct that maximum I can get here is some array with range [0; 1], basically is some fraction between zNear and zFar (and normalized by glReadPixels, for whatever reason)?

代码本身:

import sys
import argparse
import pyassimp
from pyassimp.postprocess import aiProcess_JoinIdenticalVertices, aiProcess_Triangulate
import numpy as np
import matplotlib.pyplot as plt
from collections import namedtuple
from OpenGL import GL, GLUT

Mesh = namedtuple('Mesh', ('vertices', 'faces'))

def load_mesh(filename):
    scene = pyassimp.load(filename, processing=aiProcess_JoinIdenticalVertices | aiProcess_Triangulate)
    mesh = scene.mMeshes[0].contents

    def get_vector_array(vector):
        return [vector.x, vector.y, vector.z]

    def get_face_array(face):
        return [face.mIndices[i] for i in xrange(face.mNumIndices)]

    vertices = np.array([get_vector_array(mesh.mVertices[i]) for i in xrange(mesh.mNumVertices)])
    faces = np.array([get_face_array(mesh.mFaces[i]) for i in xrange(mesh.mNumFaces)])

    pyassimp.release(scene)

    return Mesh(vertices, faces)


def load_ortho():
    GL.glMatrixMode(GL.GL_PROJECTION)
    GL.glLoadIdentity()
    GL.glOrtho(-1, 1, -1, 1, -1, 1)
    GL.glMatrixMode(GL.GL_MODELVIEW)
    GL.glLoadIdentity()


mesh = None
width, height = 1920, 1080

def draw_mesh():
    global mesh, width, height
    GL.glClearColor(0, 0, 0, 0)
    GL.glClearDepth(0.5)
    GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
    GL.glDepthMask(GL.GL_TRUE)
    load_ortho()
    for face in mesh.faces:
        GL.glBegin(GL.GL_POLYGON)
        for vertex in mesh.vertices[face]:
            GL.glVertex3dv(vertex)
        GL.glEnd()
    GLUT.glutSwapBuffers()
    d = GL.glReadPixels(0, 0, width, height, GL.GL_DEPTH_COMPONENT, GL.GL_FLOAT)
    plt.imshow(d)
    plt.show()


def reshape(w, h):
    GL.glViewport(0, 0, w, h)
    GLUT.glutDisplayFunc(draw_mesh)
    GLUT.glutPostRedisplay()


def init(width, height):
    GLUT.glutInit(sys.argv)
    GLUT.glutInitDisplayMode(GLUT.GLUT_RGBA | GLUT.GLUT_DOUBLE)
    GLUT.glutInitWindowSize(width, height)
    GLUT.glutInitWindowPosition(0, 0)
    GLUT.glutCreateWindow("test")
    # GLUT.glutDisplayFunc(draw_mesh)
    # GLUT.glutIdleFunc(draw_mesh)
    GLUT.glutReshapeFunc(reshape)
    GLUT.glutIdleFunc(GLUT.glutPostRedisplay)

    def keyPressed(self, *args):
        if args[0] == '\033':
            sys.exit()
    GLUT.glutKeyboardFunc(keyPressed)


if __name__ == '__main__':
    parser = argparse.ArgumentParser("Test on extracting depth while rendering a model with PyOpenGL")
    parser.add_argument("model", type=str)
    args = parser.parse_args()
    global mesh
    mesh = load_mesh(args.model)

    init(width, height)
    draw_mesh()

我个人用于测试的模型文件: bunny.obj 该代码段的结果是此处

The model file I personally used for testing: bunny.obj The snippet's result is here

推荐答案

运行代码为glReadPixels调用提供了一些"Invalid Operation Error: 1282"消息.相反,这是我刚刚编写的一个简单演示,演示了如何从OpenGL获取渲染三角形的颜色和深度缓冲区.我在这里执行的操作是使用所需的纹理附件(用于接收颜色和深度数据)将FBO(帧缓冲区对象)绑定到屏幕.然后,我使用glGetTexImage从GPU读取数据.使用纹理可能不是最快的方法,但这很简单,应该可以很好地工作.让我知道是否有任何不清楚的地方,我将对其进行详细说明.

Running your code gave me some "Invalid Operation Error: 1282" messages for the glReadPixels call. Instead, here is a simple demo I just wrote that shows how to obtain the color and the depth buffer from OpenGL for a rendered triangle. What I do here is bind an FBO (framebuffer object) to the screen with the desired texture attachments (for receiving the color and depth data). I then read out the data from the GPU using glGetTexImage. Using textures might not be the fastest approach, but this is pretty simple and should work nicely. Let me know if anything in this is unclear and I will elaborate on it.

from OpenGL.GL import *
from OpenGL.GLUT import *
import numpy as np
import sys

def draw_scene():
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glBegin(GL_TRIANGLES)
    glColor3f(1, 0, 0)
    glVertex3f(-1, -1, 0)
    glColor3f(0, 1, 0)
    glVertex3f(0, 1, 0)
    glColor3f(0, 0, 1)
    glVertex3f(1, -1, 0)
    glEnd()

def draw_texture():
    global color_texture
    glColor3f(1, 1, 1)
    glBindTexture(GL_TEXTURE_2D, color_texture)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glBegin(GL_QUADS)
    glTexCoord2f(0, 0)
    glVertex3f(-1, -1, 0)
    glTexCoord2f(0, 1)
    glVertex3f(-1, 1, 0)
    glTexCoord2f(1, 1)
    glVertex3f(1, 1, 0)
    glTexCoord2f(1, 0)
    glVertex3f(1, -1, 0)
    glEnd()
    glBindTexture(GL_TEXTURE_2D, 0)

def update_display():
    global fbo, color_texture, depth_texture

    #Render the scene to an offscreen FBO
    glBindFramebuffer(GL_FRAMEBUFFER, fbo)
    draw_scene()
    glBindFramebuffer(GL_FRAMEBUFFER, 0)

    #Then render the results of the color texture attached to the FBO to the screen
    draw_texture()

    #Obtain the color data in a numpy array
    glBindTexture(GL_TEXTURE_2D, color_texture)
    color_str = glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE)
    glBindTexture(GL_TEXTURE_2D, 0)
    color_data = np.fromstring(color_str, dtype=np.uint8)

    #Obtain the depth data in a numpy array
    glBindTexture(GL_TEXTURE_2D, depth_texture)
    depth_str = glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_FLOAT)
    glBindTexture(GL_TEXTURE_2D, 0)
    depth_data = np.fromstring(depth_str, dtype=np.float32)
    print(np.min(depth_data), np.max(depth_data))#This is just to show the normalized range of depth values obtained

    glutSwapBuffers()

width, height = 800, 600
fbo = None
color_texture = None
depth_texture = None

if __name__ == '__main__':
    glutInit([])
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE)
    glutInitWindowSize(width, height)
    glutInitWindowPosition(0, 0)
    glutCreateWindow("Triangle Test")

    glEnable(GL_TEXTURE_2D)#not needed if using shaders...
    glEnable(GL_DEPTH_TEST)

    color_texture = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, color_texture)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
    glBindTexture(GL_TEXTURE_2D, 0)

    depth_texture = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, depth_texture)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, None)
    glBindTexture(GL_TEXTURE_2D, 0)

    fbo = glGenFramebuffers(1)
    glBindFramebuffer(GL_FRAMEBUFFER, fbo)
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_texture, 0)
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0)
    glBindFramebuffer(GL_FRAMEBUFFER, 0)

    glutDisplayFunc(update_display)
    glutIdleFunc(glutPostRedisplay)
    glutMainLoop()

这篇关于用PyOpenGL读取深度缓冲区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆