4
$\begingroup$

I've been attempting to write a custom RenderEngine for Blender, but have stumbled on a fundamental problem which is preventing me from going any further.

To illustrate the issue I will include an example from the official docs as a small repro:

"""
3D Image
--------

Similar to the 2D Image shader, but works with 3D positions for the image vertices.
To use this example you have to provide an image that should be displayed.
"""
import bpy
import gpu
from gpu_extras.batch import batch_for_shader

IMAGE_NAME = "Untitled"
image = bpy.data.images[IMAGE_NAME]
texture = gpu.texture.from_image(image)

shader = gpu.shader.from_builtin('IMAGE')
batch = batch_for_shader(
    shader, 'TRIS',
    {
        "pos": ((0, 0, 0), (0, 1, 1), (1, 1, 1), (1, 1, 1), (1, 0, 0), (0, 0, 0)),
        "texCoord": ((0, 0), (0, 1), (1, 1), (1, 1), (1, 0), (0, 0)),
    },
)


def draw():
    shader.uniform_sampler("image", texture)
    batch.draw(shader)


bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')

When the example is run, a simple textured plane will show up in the 3D viewport.

However, when I adapt the example and add an extra batch of polygons to the script, this happens:

3D viewport

When the viewport is rotated, the second batch (furthest away) appears in front of the first batch (nearest). It seems they are drawn on top of each other in the order I specified in the script.

How can I resolve this issue?

Complete code:

import bpy
import gpu
from gpu_extras.batch import batch_for_shader

IMAGE_NAME = "Untitled"
image = bpy.data.images[IMAGE_NAME]
texture = gpu.texture.from_image(image)

shader = gpu.shader.from_builtin('IMAGE')
batch1 = batch_for_shader(
    shader, 'TRIS',
    {
        "pos": ((0, 0, 0), (0, 1, 1), (1, 1, 1), (1, 1, 1), (1, 0, 0), (0, 0, 0)),
        "texCoord": ((0, 0), (0, 1), (1, 1), (1, 1), (1, 0), (0, 0)),
    },
)
batch2 = batch_for_shader(
    shader, 'TRIS',
    {
        "pos": ((2, 2, 0), (2, 3, 1), (3, 3, 1), (3, 3, 1), (3, 2, 0), (2, 2, 0)),
        "texCoord": ((0, 0), (0, 1), (1, 1), (1, 1), (1, 0), (0, 0)),
    },
)

def draw():
    fb = gpu.state.active_framebuffer_get()
    fb.clear(color=(0, 0, 0, 1))

    shader.uniform_sampler("image", texture)
    batch1.draw(shader)
    batch2.draw(shader)


bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
$\endgroup$
3
  • 1
    $\begingroup$ use gpu.state.depth_test_set(mode='ALWAYS') docs.blender.org/api/current/… $\endgroup$
    – Karan
    Commented Jan 19 at 9:56
  • $\begingroup$ @Karan Thanks for your response. I also thought this but couldn't get it to work myself. I tried all the modes, also in combination with gpu.state.face_culling_set(), but I'm still getting artefacts. I wanted to try this with the example RenderEngine from here, but I can't even get the example to run. I even installed Lubuntu on VirtualBox with a fresh install of Blender, thinking it was a problem with my installation, but still nothing happens when I run the script from the text editor. So I wonder if it works for you? $\endgroup$
    – Midas
    Commented Jan 20 at 3:25
  • $\begingroup$ I had to enable viewport shading in the 3D view, my stupid mistake. The example now runs fine, but I'm still not sure how to get rid of the overlapping geometry... $\endgroup$
    – Midas
    Commented Jan 20 at 21:15

1 Answer 1

5
$\begingroup$

This is what worked for me in the end. In draw():

def draw():
    gpu.state.depth_test_set("LESS_EQUAL")
    gpu.state.face_culling_set('BACK')
    gpu.state.front_facing_set(True)

    fb = gpu.state.active_framebuffer_get()
    fb.clear(color=(0, 0, 0, 1))

    shader.uniform_sampler("image", texture)
    batch1.draw(shader)
    batch2.draw(shader)
    
    gpu.state.depth_test_set("NONE")
    gpu.state.face_culling_set('NONE')
    gpu.state.front_facing_set(False)
$\endgroup$

You must log in to answer this question.

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