1
$\begingroup$

I'm trying to read the depth buffer from a frame buffer during offscreen rendering using the gpu.types.GPUFrameBuffer.read_depth() function from the gpu module. No matter what I do, all values in the buffer are always 0. I found this answer, which works for getting the depth buffer of the viewport. I tried to adapt it to offscreen rendering, but with no luck. Here is the relevant code snipped of my script that does the rendering. I am using a custom shader to render the currently selected object.

offscreen = gpu.types.GPUOffScreen(DIM, DIM)
with offscreen.bind():
    gpu.state.depth_mask_set(True)
    gpu.state.depth_test_set('LESS')
    framebuffer = gpu.state.active_framebuffer_get()

    batch.draw(shader)
    depth_buffer = framebuffer.read_depth(0, 0, DIM, DIM)
    # have to reset these because I run into https://projects.blender.org/blender/blender/issues/98486
    gpu.state.depth_mask_set(False)
    gpu.state.depth_test_set('NONE')

offscreen.free()

Interestingly, writing to the color buffer by not setting depth mask and depth test and using framebuffer.read_color() like this works:

offscreen = gpu.types.GPUOffScreen(DIM, DIM)
with offscreen.bind():
    framebuffer = gpu.state.active_framebuffer_get()

    batch.draw(shader)
    color_buffer = framebuffer.read_color(0, 0, DIM, DIM, 4, 0, 'FLOAT')

offscreen.free()

Is reading the depth buffer not supported when rendering offscreen or am I missing something here? I am very new to blender, so this might very well be a mistake on my side.

For reference, here is the entire script

import bpy
import gpu
import numpy as np
from gpu_extras.batch import batch_for_shader

def get_world_mesh_data(o):
    """
    Returns the meshes of all passed objects combined.

    Parameters
    ----------
    obs : Iterable[bpy_types.Object]
        Objects to combine. Fails if non-mesh object is passed.

    Returns
    -------
    verts : numpy.ndarray
        Nx3 float array of XYZ vertex coordinates in world space
    indcs : numpy.ndarray
        Nx3 int array of triangle indices
    info : list[tuple]
        Holds a tuple for each passed object. The first entry stores
        the index of the first element in 'verts' belonging to the
        corresponding object, the second entry the same for 'indcs'.
    """
    # Accumulate vertex and triangle counts of all passed objects to
    # later allocate the right amount of memory
    mesh = o.data
    mesh.calc_loop_triangles()

    # Initialize joined lists
    verts = np.empty(len(mesh.vertices) * 3, dtype=np.float32)
    indcs = np.empty(len(mesh.loop_triangles) * 3, dtype=np.int32)
    vstart = 0
    istart = 0

    # Calculate object's slice in combined list
    mesh = o.data
    vend = vstart + len(mesh.vertices) * 3
    iend = istart + len(mesh.loop_triangles) * 3
    vslice = verts[vstart:vend]
    islice = indcs[istart:iend]

    # Get vertex coordinates of current object
    mesh.vertices.foreach_get('co', vslice)
    # Transform vertices to world space
    verts[vstart:vend] = transf_pts(
        o.matrix_world,
        vslice.reshape(-1, 3),
        ).ravel()

    # Get triangle indices of current object
    mesh.loop_triangles.foreach_get('vertices', islice)

    vstart = vend
    istart = iend

    verts.shape = (-1, 3)
    indcs.shape = (-1, 3)
    return verts, indcs

def transf_pts(mat, pts):
    """
    Apply a transformation matrix to every point in a list.

    Parameters
    ----------
    mat : Iterable
        4x4 transformation matrix to apply to every point
    pts : Iterable
        List of 3D points

    Returns
    -------
    pts : numpy.ndarray
        Copy of 'pts' with transformed coordinates
    """
    # blender.stackexchange.com/a/139513
    # return np.einsum('ij,aj->ai', mat, homog_vecs(pts))[:, :-1]
    return (np.asanyarray(mat) @ append_one(pts).T).T[:, :3]

def append_one(pts):
    """
    Append a coordinate equal to one to every vector.

    Parameters
    ----------
    pts : Iterable
        Points to append to.

    Returns
    -------
    hpts : numpy.ndarray
        Copy of 'pts' with appended coordinates

    Examples
    --------
    >>> append_one([[0, 1, 2], [3, 4, 5]])
    array([[0, 1, 2, 1], [3, 4, 5, 1]])
    """
    vs = np.asanyarray(pts)
    hpts = np.ones(np.add(vs.shape, (0, 1)), dtype=vs.dtype)
    hpts[:, :-1] = vs
    return hpts

DIM = 512
COLOR_IMG_NAME = "Color"
DEPTH_IMAGE_NAME = "Depth"

# switch to edit mode
bpy.ops.object.mode_set(mode='EDIT')

# Construct depthpass shader
shader = gpu.types.GPUShader(
    vertexcode='''
    in vec3 pos;
    void main() {
        gl_Position = vec4(pos, 1);
    }''',
    fragcode='''
    out vec4 col;
    void main() {
        col = vec4(0, 0, 1, 1);
    }'''
)
shader.bind()

# Create batch from all objects in edit mode
verts, indcs = get_world_mesh_data(bpy.context.active_object)

batch = batch_for_shader(
    shader, 'TRIS',
    {"pos": verts},
    indices=indcs,
)

offscreen = gpu.types.GPUOffScreen(DIM, DIM)
with offscreen.bind():
    gpu.state.depth_mask_set(True)
    gpu.state.depth_test_set('LESS')
    framebuffer = gpu.state.active_framebuffer_get()

    batch.draw(shader)
    depth_buffer = framebuffer.read_depth(0, 0, DIM, DIM)
    # have to reset these because I run into https://projects.blender.org/blender/blender/issues/98486
    gpu.state.depth_mask_set(False)
    gpu.state.depth_test_set('NONE')

offscreen.free()
$\endgroup$

1 Answer 1

0
$\begingroup$

Seems I simply forgot to clear the depth buffer before writing to it. This works:

offscreen = gpu.types.GPUOffScreen(DIM, DIM)
with offscreen.bind():
    gpu.state.depth_mask_set(True)
    gpu.state.depth_test_set('LESS')
    framebuffer = gpu.state.active_framebuffer_get()
    # This is the line I was missing
    framebuffer.clear(depth=1.0)

    batch.draw(shader)
    depth_buffer = framebuffer.read_depth(0, 0, DIM, DIM)
    # have to reset these because I run into https://projects.blender.org/blender/blender/issues/98486
    gpu.state.depth_mask_set(False)
    gpu.state.depth_test_set('NONE')

offscreen.free()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .