1
$\begingroup$

I want to use custom vertex and fragment shaders to draw 2d rectangles.

import bpy
from bpy.types import Operator

import gpu
from gpu_extras.batch import batch_for_shader


class ModalOperator(Operator):
    bl_idname = "object.modal_operator"
    bl_label = "Simple Modal Operator"

    def overlay(self):
        shader_info = gpu.types.GPUShaderCreateInfo()
        shader_info.push_constant('MAT4', "ModelViewProjectionMatrix")
        shader_info.vertex_in(0, 'VEC2', "pos")
        shader_info.fragment_out(0, 'VEC4', "FragColor")

        shader_info.vertex_source(
            "void main()"
            "{"
            "  gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0f);"
            "}"
        )

        shader_info.fragment_source(
            "void main()"
            "{"
            "  FragColor = vec4(pos, 1.0);"
            "}"
        )

        shader = gpu.shader.create_from_info(shader_info)
        del vert_out
        del shader_info

        vertices = [
            (100, 100), (300, 100),
            (100, 200), (300, 200)]

        indices = (
            (0, 1, 2), (2, 1, 3))

        batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
        batch.draw(shader)

    def invoke(self, context, event):
        self._2d_handle = bpy.types.SpaceView3D.draw_handler_add(self.overlay, (), "WINDOW", "POST_PIXEL")
        context.window_manager.modal_handler_add(self)
        context.area.tag_redraw()
        return {"RUNNING_MODAL"}

    def modal(self, context, event):
        if event.type in {"WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
            return {"PASS_THROUGH"}

        elif event.type in {"RIGHTMOUSE", "ESC"}:
            bpy.types.SpaceView3D.draw_handler_remove(self._2d_handle, "WINDOW")
            context.area.tag_redraw()
            return {"CANCELLED"}

        return {"RUNNING_MODAL"}


classes = (ModalOperator,)


register, unregister = bpy.utils.register_classes_factory(classes)


if __name__ == "__main__":
    register()

I am getting this error

ERROR (gpu.shader): pyGPU_Shader VertShader: | Error: C1067: too little data in type constructor

ERROR (gpu.shader): pyGPU_Shader FragShader: | Error: C1503: undefined variable "pos"

shader = gpu.shader.create_from_info(shader_info)

Exception: Shader Compile Error, see console for more details

$\endgroup$
1
  • $\begingroup$ There's an example for exactly this in the docs. Position needs to be vec3, not vec2, and you need to pass the position from the vertex shader to the fragment shader with a GPUStageInterfaceInfo. $\endgroup$
    – scurest
    Commented Feb 29 at 2:21

1 Answer 1

3
+50
$\begingroup$

You are missing GPUStageInterfaceInfo, and you should use VEC3 instead of VEC2.

It is surprising that python does not generate an error when trying to delete an attribute that does not exist (del vert_out), I learned something new :)

import bpy
import gpu
from gpu_extras.batch import batch_for_shader


class ModalOperator(bpy.types.Operator):
    bl_idname = "object.modal_operator"
    bl_label = "Simple Modal Operator"

    def overlay(self):
        vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
        vert_out.smooth('VEC3', "pos")
        
        shader_info = gpu.types.GPUShaderCreateInfo()
        shader_info.push_constant('MAT4', "ModelViewProjectionMatrix")
        shader_info.vertex_in(0, 'VEC3', "position")
        shader_info.vertex_out(vert_out)
        shader_info.fragment_out(0, 'VEC4', "FragColor")

        shader_info.vertex_source(
            """void main()
            {
              pos = position;
              gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0f);
            }"""
        )

        shader_info.fragment_source(
            """void main()
            {
              FragColor = vec4(pos, 1.0);
            }"""
        )

        shader = gpu.shader.create_from_info(shader_info)
        del vert_out
        del shader_info

        vertices = [
            (100, 100, 0), (100, 200, 0), (300, 200, 0), (300, 100, 0)
            ]

        batch = batch_for_shader(shader, 'TRI_FAN', {"position": vertices})
        batch.draw(shader)

    def invoke(self, context, event):
        self._2d_handle = bpy.types.SpaceView3D.draw_handler_add(self.overlay, (), "WINDOW", "POST_PIXEL")
        context.window_manager.modal_handler_add(self)
        context.area.tag_redraw()
        return {"RUNNING_MODAL"}

    def modal(self, context, event):
        if event.type in {"WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
            return {"PASS_THROUGH"}

        elif event.type in {"RIGHTMOUSE", "ESC"}:
            bpy.types.SpaceView3D.draw_handler_remove(self._2d_handle, "WINDOW")
            context.area.tag_redraw()
            return {"CANCELLED"}

        return {"RUNNING_MODAL"}


classes = (ModalOperator,)


register, unregister = bpy.utils.register_classes_factory(classes)


if __name__ == "__main__":
    register()
```
$\endgroup$
1
  • $\begingroup$ Hello @Oxicid, thanks for the answer. I need your further help, can you make the corners for the rectangle rounded and if possible also add an outline with the fragment shader? I don't know GLSL shader language 😔 $\endgroup$
    – Karan
    Commented Mar 6 at 19:29

You must log in to answer this question.

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