1
$\begingroup$

I'm making a script allowing me to apply fragment shaders to the 3D Viewport real-time. However, I don't know how to get view and perspective matrices of the current point of view. I know how to get view and perspective matrices of the currently active camera using

view_matrix = scene.camera.matrix_world.inverted()

projection_matrix = scene.camera.calc_matrix_camera(context.evaluated_depsgraph_get(), x=WIDTH, y=HEIGHT)

but that's not what I want. I want to get these matrices, but for the current point of view (I mean the view you see when you hold and drag Middle Mouse Button in 3D Viewport).

I tried to use:

bpy.context.region_data.perspective_matrix
bpy.context.region_data.view_matrix

but it doesn't work as I would expect.

EDIT: My whole code:

import bpy
import gpu
import bgl
from mathutils import Matrix
from gpu_extras.batch import batch_for_shader


def get_viewport_width_height():
    'gets the current height and width of the viewport'
    for a in bpy.context.screen.areas:
        if a.type == 'VIEW_3D':
            for r in a.regions:
                if r.type == 'WINDOW':
                    return r.width, r.height

vertex_shader = '''
//   uniform mat4 modelMatrix;
//    uniform mat4 viewProjectionMatrix;

    in vec2 position;
    in vec2 uv;

    out vec2 uvInterp;

    void main()
    {
        uvInterp = uv;
        gl_Position = vec4(position, 0.0, 1.0); //viewProjectionMatrix * modelMatrix * 
    }
'''

fragment_shader = '''
    uniform sampler2D image;

    in vec2 uvInterp;
    
    out vec4 out_color;

    void main()
    {
        vec2 uv = uvInterp;
        //float pixelation = 500.;
        //uv = ceil(uv*pixelation)/pixelation;
        vec3 tex = texture(image, uv).rgb;
        out_color = vec4(tex,1.);
    }
'''

WIDTH, HEIGHT = get_viewport_width_height()  
offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT)   
shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
batch = batch_for_shader(
    shader, 'TRI_FAN',
    {
        "position": ((-1, -1), (1, -1), (1, 1), (-1, 1)),
        "uv": ((0, 0), (1, 0), (1, 1), (0, 1)),
    },
)

def draw():
    'draw loop'
    WIDTH, HEIGHT = get_viewport_width_height()
    
    context = bpy.context
    scene = context.scene

#################
# That doesn't work (give some weird point of view)
##################
#    for area in bpy.context.screen.areas:
#        if area.type == 'VIEW_3D':
#            area.spaces.active.region_3d.view_matrix
#            view_matrix = area.spaces.active.region_3d.view_matrix
#            projection_matrix = area.spaces.active.region_3d.perspective_matrix
     
     
#################
# That give the camera's view
##################       
    view_matrix = scene.camera.matrix_world.inverted()
    projection_matrix = scene.camera.calc_matrix_camera(
        context.evaluated_depsgraph_get(), x=WIDTH, y=HEIGHT)


    offscreen.draw_view3d(
        scene,
        context.view_layer,
        context.space_data,
        context.region,
        view_matrix,
        projection_matrix)
        
        
    bgl.glClearColor(1., 1., 0., 1.)
    bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
    bgl.glActiveTexture(bgl.GL_TEXTURE0)
    bgl.glBindTexture(bgl.GL_TEXTURE_2D, offscreen.color_texture)
    bgl.glDisable(bgl.GL_DEPTH_TEST)
    

    shader.bind()
    shader.uniform_float("image", 0)
    batch.draw(shader)
    
    draw_texture_2d(offscreen.color_texture, (0, 0), WIDTH, HEIGHT)
    
    
    
#registering and unregistering a handler
h = bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
def rm_handler():
    global h
    print("Handler removed")
    bpy.types.SpaceView3D.draw_handler_remove(h, 'WINDOW')
bpy.app.timers.register(rm_handler, first_interval=5)
$\endgroup$

1 Answer 1

3
$\begingroup$

Try this script:

import bpy

area  = [area for area in bpy.context.window.screen.areas if area.type == 'VIEW_3D'][0]

with bpy.context.temp_override(area=area):
    view3d = bpy.context.space_data
    view_matrix = view3d.region_3d.view_matrix
    perspective_matrix = view3d.region_3d.perspective_matrix
    print(view_matrix)
    print(perspective_matrix)
$\endgroup$
2
  • $\begingroup$ I'm using Blender 3.3 and when I put your code inside my draw function: def draw(): registered by bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL') I get a Blender crash :D And by "put your code" I actually only mean the with bpy.context.temp_override(area=area): part - it's enough to cause the crash. Seems like Blender doesn't handle Python's context managers inside the draw handler $\endgroup$ Commented Jan 21, 2023 at 15:09
  • $\begingroup$ I also added my whole code. Check it out if it helps. $\endgroup$ Commented Jan 21, 2023 at 15:19

You must log in to answer this question.

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