2
$\begingroup$

Is there a way to draw to multiple render targets at once using the Blender's GPU module? I am looking for something similar to glDrawBuffers in OpenGL.

Say, for example, my fragment shader looks like this:

in vec3 pos;
out vec4[2] fragColor;

void main()
{
    fragColor[0] = vec4(1.0, 0, 0, 1.0);
    fragColor[1] = vec4(0, 1.0, 0, 1.0);
}

How do I render this shader with multiple render targets?

$\endgroup$

1 Answer 1

3
$\begingroup$

After a bit of research, here's the solution I found:

Below I show an example of offscreen rendering to a Frame Buffer with two texture color slots. Then, I render the texture from each color slot to the viewport.

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

# Create a custom Frame Buffer with two color slots
my_tex1 = gpu.types.GPUTexture((512, 512))
my_tex2 = gpu.types.GPUTexture((512, 512))
my_buff = gpu.types.GPUFrameBuffer(color_slots=(my_tex1, my_tex2))

# Define Vertex Shader
vertex_shader = '''
    in vec3 position;
    out vec3 pos;

    void main()
    {
        gl_Position = vec4(position, 1.0f);
    }
'''

# Define Fragment Shader
fragment_shader = '''
    in vec3 pos;
    out vec4[2] FragColor;
    void main()
    {
        FragColor[0] = vec4(1.0, 0.0, 0.0, 1.0); //Red
        FragColor[1] = vec4(0.0, 0.0, 1.0, 1.0); //Blue
    }
'''

# Vertex Attributes: Positions
vertices = ((-1, 1, 0), (1, 1, 0), (-1, -1, 0), (1, -1, 0))

# Vertex Attributes: Indices
indices = ((0, 1, 2), (2, 1, 3))

# Create Shader
shader = gpu.types.GPUShader(vertex_shader, fragment_shader)

# Create batch for rendering
batch = batch_for_shader(shader, 'TRIS', {"position": vertices}, indices=indices)

# Custom draw call to draw to the custom Frame Buffer
def my_draw():
    # Bind custom Frame Buffer to be drawn
    with my_buff.bind():
        
        # Bind shader to be drawn
        shader.bind()
        
        # Draw to my_tex1 and my_tex2 offscreen
        batch.draw(shader)

# Main draw call
def draw():
    # Custom draw call
    my_draw()
    
    # Draw my_tex1 and my_tex2 seperately to the Viewport
    draw_texture_2d(my_tex1, (0,0), 10, 10)
    draw_texture_2d(my_tex2, (0,0), -10, 10)

# Add draw call to draw handler
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')

The result in Blender's viewport: result

$\endgroup$

You must log in to answer this question.

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