2
$\begingroup$

How to get proper GPUOffScreen projection matrix from viewport?

API docs have example only for camera.
I tried using view_matrix and perspective_matrix, but that does not seem to work.

gpu.matrix.load_matrix(region_3d.view_matrix.inverted())
gpu.matrix.load_projection_matrix(region_3d.perspective_matrix)

Here is full script, it supposed to display silhouettes for all objects in the scene:

import bpy
import gpu
from gpu_extras.batch import batch_for_shader
from gpu_extras.presets import draw_texture_2d


def get_regions():
    for window in bpy.context.window_manager.windows:
        for area in window.screen.areas:
            if area.type == "VIEW_3D":
                r3d = area.spaces[0].region_3d
                for r in area.regions:
                    if r.type == "WINDOW":
                        return r, r3d


region, region_3d = get_regions()
offscreen = gpu.types.GPUOffScreen(region.width, region.height)

with offscreen.bind():
    fb = gpu.state.active_framebuffer_get()
    fb.clear(color=(0.0, 0.0, 0.0, 0.0))

    with gpu.matrix.push_pop():
        gpu.matrix.load_matrix(region_3d.view_matrix.inverted())
        gpu.matrix.load_projection_matrix(region_3d.perspective_matrix)

        for ob in bpy.context.scene.objects:
            me = ob.to_mesh()
            me.transform(ob.matrix_world)
            me.calc_loop_triangles()

            points = [v.co for v in me.vertices]
            indices = [tri.vertices for tri in me.loop_triangles]

            shader = gpu.shader.from_builtin("UNIFORM_COLOR")
            shader.bind()
            shader.uniform_float("color", (1.0, 0.2, 0.2, 1.0))
            batch = batch_for_shader(shader, "TRIS", {"pos": points}, indices=indices)
            batch.draw(shader)

            ob.to_mesh_clear()


def draw(self, context):
    gpu.state.blend_set("ALPHA")
    draw_texture_2d(offscreen.texture_color, (0, 0), region.width, region.height)
    gpu.state.blend_set("NONE")


bpy.types.SpaceView3D.draw_handler_add(draw, (None, None), "WINDOW", "POST_PIXEL")
$\endgroup$

1 Answer 1

3
$\begingroup$

You need to replace:

with gpu.matrix.push_pop():
    gpu.matrix.load_matrix(region_3d.view_matrix.inverted())
    gpu.matrix.load_projection_matrix(region_3d.perspective_matrix)

with:

with gpu.matrix.push_pop():
    gpu.matrix.load_matrix(region_3d.view_matrix)
    gpu.matrix.load_projection_matrix(region_3d.window_matrix)

Relevant section in the docs:

Current perspective matrix (window_matrix * view_matrix)

So you accidently did $W \cdot V \cdot V^{-1} = P \cdot V^{-1}$

but you just want $W \cdot V = P$

This perspective is already "through the window". You can convince yourself with trying:

with gpu.matrix.push_pop():
    #gpu.matrix.load_matrix(region_3d.view_matrix.inverted())
    gpu.matrix.load_projection_matrix(region_3d.perspective_matrix)

which works as well. But the view matrix should potentionally not be merged on the projection stack to avoid other problems.

$\endgroup$

You must log in to answer this question.

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