Simple Mesh Shader using Callback Node

Mesh Shaders (along with task shaders: no example of those here though) are a super powerful relatively new type of shader. They can be thought of as a compute shader that replaces the vertex, geometry, and tessellation shaders. You can do all sorts of stuff with them from fine grained culling (like nanite) to procedural generation. The link below goes over all the stuff you can do with them.

image

One thing to note is that they only change the pipeline for one shader program not all of them. So you can still use vertex shaders when you want.

As for the code this shows how you can draw a triangle that’s been generated on the GPU using PyOpenGL and Callback Nodes.

from OpenGL.GL.NV.mesh_shader import GL_MESH_SHADER_NV, glDrawMeshTasksNV
from OpenGL.GL import *
from direct.showbase.ShowBase import ShowBase
from panda3d.core import CallbackNode, PythonCallbackObject, NodePath

fragment_shader = """
#version 450
layout(location = 0) out vec4 color;

in PerVertexData
{
  vec4 color;
} in_vertex_data;  

void main()
{
  color = in_vertex_data.color;
}

"""

mesh_shader = """#version 450

// Needed to use mesh shaders
#extension GL_NV_mesh_shader : require

// Set the number of threads per workgroup (always one-dimensional).
layout(local_size_x=3) in; // if gl_LocalInvocationID.x is used

// maximum allocation size for each meshlet
layout(max_vertices=3, max_primitives=1) out;

// the primitive type (points,lines or triangles)
layout(triangles) out;

// define your own vertex output blocks as usual
out PerVertexData{
  vec4 color;
} v_out[];

// We can create data inside the mesh shader
const vec3 vertices[3] = {vec3(-1,-0.5,0), vec3(0,1,0), vec3(1,-1,0)};
const vec3 colors[3] = {vec3(0.8,0.2,0.3), vec3(0.2,0.5,0.3), vec3(0.1,0.2,0.7)};


void main(){

    uint id = gl_LocalInvocationID.x;
    gl_MeshVerticesNV[id].gl_Position = vec4(vertices[id], 1.0);
    gl_PrimitiveIndicesNV[id] = id;
    v_out[id].color = vec4(colors[id], 1.0);
    
    barrier();
    
    if (gl_LocalInvocationID.x == 0)
    gl_PrimitiveCountNV = 1;
}
"""

cbnode = CallbackNode("")

program = None


# Compile the mesh and fragment shader
def init(cbdata):
    global program
    program = glCreateProgram()

    mesh = glCreateShader(GL_MESH_SHADER_NV)
    glShaderSource(mesh, mesh_shader)
    glCompileShader(mesh)

    fragment = glCreateShader(GL_FRAGMENT_SHADER)
    glShaderSource(fragment, fragment_shader)
    glCompileShader(fragment)

    glAttachShader(program, mesh)
    glAttachShader(program, fragment)
    glLinkProgram(program)

    # Check for errors
    result = glGetProgramInfoLog(program)

    # Don't run the shader if there's a linking error
    if result:
        print(result)
        cbnode.setDrawCallback(PythonCallbackObject(lambda cbdata: None))
    else:
        # Now that we've created the shaders we need to switch to drawing them
        cbnode.setDrawCallback(PythonCallbackObject(draw))


# Set up the shaders
cbnode.setDrawCallback(PythonCallbackObject(init))


def draw(cbdata):
    glUseProgram(program)
    glDrawMeshTasksNV(0, 1)

    # Needed to draw in the 2d scene graph
    cbdata.upcall()


class Window(ShowBase):
    def __init__(self):
        super().__init__()
        # Attach the mesh shader to the screen
        self.cbnp: NodePath = self.render2d.attach_new_node(cbnode)

        # You need to be using shaders if you want to render other nodes
        self.render.setShaderAuto()
        self.p = self.loader.load_model("panda")
        self.p.reparentTo(self.render)


window = Window()
window.run()

Note: due to the extensions used you need a Nvidia GPU that supports mesh shaders. Neither AMD or Intel GPU’s currently support the OpenGL mesh shader extension.

Running the program you should see something like this:

And if you zoom out there should be a panda in the background covered by the triangle. The panda is not draw using mesh shaders.

Pretty cool stuff.

Mesh Shader heavily based on (Demo) RGB Triangle with Mesh Shaders in OpenGL | HackLAB

4 Likes

Cool!
Do you see any possibility of using this on a Mac with an M1 chipset?

There is no mesh shader support in OpenGL on non-NVIDIA hardware at this time, unfortunately.

It sounds like AMD’s OpenGL team is working on mesh shader support. How long it will take though no clue.

1 Like

Looks like AMD is planing to add support for mesh shaders within the next quarterly releases. So hopefully soon.

1 Like

That’s great. We’ll implement it as soon as there’s a standard cross-vendor extension.

1 Like