0
$\begingroup$

My addon that works on blender 3.3 up to blender 3.6 on windows does not compile anymore on Mac/Metal computers, with the error:

Exception: Shader Compile Error

The reason of this issue is that the render code was not updated properly to use the new gpu api, among others gpu.types.ShaderCreateInfo that should be now used to define shaders that support cross platform source generation.

I will post the solution here, and additional guide to update your rendering code, hopefully it will help other people as the official documentation is a bit thin.

The shader in itself if not important but it is just a good example. It takes a input some uv coords, 2d vertex positions, a projection matrix, a texture, some color and background color, and a fade value. The texture color is multiplied by either the color or the background color based on its opacity.

The definition of the shader was as follow:

shader_2d_image_vertex = '''
    uniform mat4 ModelViewProjectionMatrix;
    in vec2 texCoord;
    in vec2 pos;
    out vec2 texCoord_interp;
    void main()
    {
      gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f);
      gl_Position.z = 1.0;
      texCoord_interp = texCoord;
    }
'''
shader_2d_image_fragment = '''
    uniform vec4 color;
    uniform vec4 color_bg;
    uniform float fade;
    in vec2 texCoord_interp;
    out vec4 fragColor;
    uniform sampler2D image;
    void main()
    {
      vec4 texture_sampled=texture(image, texCoord_interp);
      if(texture_sampled.a>0.6)
        fragColor = color * texture_sampled;
      else
        fragColor = color_bg * texture_sampled;
      fragColor=vec4(fragColor.r, fragColor.g, fragColor.b, fragColor.a * smoothstep(0,1,fade));
    }
'''
shader_2d_image_color = gpu.types.GPUShader(shader_2d_image_vertex, shader_2d_image_fragment) # Crash was here

I also have error spam when I use: bgl.glEnable() to have smooth lines, to enable/disable depth test or to have smooth blend.

Answer below.

$\endgroup$

1 Answer 1

4
$\begingroup$

Since 3.3 (or earlier) it is possible to define shaders using gpu.types.ShaderCreateInfo() that will allow cross cross platform shader generation. What seemed to work cross platform before does not seem to work anymore in 3.5 and later. Here is the same shader code as above but using the new gpu api:

# The shader info will store all the data.
shader_2d_image_info = gpu.types.GPUShaderCreateInfo()
    
# Defines vertex shader inputs and uniforms that are now called constants.
shader_2d_image_info.vertex_in(0, 'VEC2', "texCoord")
shader_2d_image_info.vertex_in(1, 'VEC2', "pos")
shader_2d_image_info.sampler(0, 'FLOAT_2D', "Image")
shader_2d_image_info.push_constant('MAT4', "ModelViewProjectionMatrix")
shader_2d_image_info.push_constant('VEC4', "Color")
shader_2d_image_info.push_constant('VEC4', "Color_bg")
shader_2d_image_info.push_constant('FLOAT', "Fade")

# Define as Interface the attributes that will be transferred from the vertex shader to the fragment shader. 
# Before they would be both a vertex shader output and fragment shader input.
# An interface can be flat(), no_perspective() or smooth()
# Warning: You need to give a string to the GPUStageInterfaceInfo() or the shader will not work. Any string will work.
shader_2d_image_interface = gpu.types.GPUStageInterfaceInfo("shader_2d_image_interface")    
shader_2d_image_interface.smooth('VEC2', "texCoord_interp")
shader_2d_image_info.vertex_out(shader_2d_image_interface)

# fragment shader output
shader_2d_image_info.fragment_out(0, 'VEC4', 'fragColor')

shader_2d_image_info.vertex_source(
    '''
    void main()
    {
      gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f);
      gl_Position.z = 1.0;
      texCoord_interp = texCoord;
    }
    ''')    
shader_2d_image_info.fragment_source(
    '''
    void main()
    {
      vec4 texture_sampled=texture(Image, texCoord_interp);
      if(texture_sampled.a>0.6)
        fragColor = Color * texture_sampled;
      else
        fragColor = Color_bg * texture_sampled;
      fragColor=vec4(fragColor.r, fragColor.g, fragColor.b, fragColor.a * smoothstep(0,1,Fade));
    }

    ''')
shader_2d_image_color = gpu.shader.create_from_info(shader_2d_image_info)
del shader_2d_image_info
del shader_2d_image_interface

For information, the code to use that shader was (if you are discovering the blender rendering apis. Also fyi, the path below are from pathlib import Path):

def draw_image(self, position_x=100, position_y=100, size=20, image='MISSING', color=(1, 1, 1, 1),
               color_bg=(0, 0, 0, 1), fade=1):
    """
        Draw an icon in the viewport.
    """
    halfsize = size * .5
    vertices = (
        (position_x - halfsize, position_y - halfsize),
        (position_x + halfsize, position_y - halfsize),
        (position_x + halfsize, position_y + halfsize),
        (position_x - halfsize, position_y + halfsize))
    previous_blend_state = gpu.state.blend_get()
    gpu.state.blend_set("ALPHA")
    if image not in icons:
        texture_name = f'ICON_{image}.tif'
        texture_path = get_icons_dir() / texture_name
        if not texture_path.exists():
            texture_path = get_icons_dir() / f'ICON_MISSING.tif'
            if not texture_path.exists():
                return
        if texture_name in bpy.data.images:
            bpy.data.images.remove(bpy.data.images[f'ICON_{image}.tif'])
        img = bpy.data.images.load(str(texture_path), check_existing=True)
        icons[image] = gpu.texture.from_image(img)
    batch = batch_for_shader(
        shader_2d_image_color, 'TRI_FAN',
        {
            "pos": vertices,
            "texCoord": ((0, 0), (1, 0), (1, 1), (0, 1)),
        },
    )
    shader_2d_image_color.bind()
    shader_2d_image_color.uniform_float("Color", color)
    shader_2d_image_color.uniform_float("Color_bg", color_bg)
    shader_2d_image_color.uniform_float("Fade", fade)
    shader_2d_image_color.uniform_sampler("Image", icons[image])
    batch.draw(shader_2d_image_color)
    gpu.state.blend_set(previous_blend_state)

You can read here about the different types of interfaces, attributes, samplers.

Considering the other errors: You should no use bgl.glEnable() anymore, here are how you can replace some of those calls:

  • bgl.glEnable(bgl.GL_BLEND) => gpu.state.blend_set("ALPHA") Other blen modes here
  • bgl.glDisable(bgl.GL_BLEND) => gpu.state.blend_set("NONE")
  • bgl.glEnable(bgl.GL_DEPTH_TEST) => gpu.state.depth_test_set("LESS")
  • bgl.glDisable(bgl.GL_DEPTH_TEST) => gpu.state.depth_test_set("NONE")
  • bgl.glLineWidth(line_width) => gpu.state.line_width_set(line_width)

If you want a smooth line, you cannot use bgl.glEnable(bgl.GL_LINE_SMOOTH) anymore, and there is no equivalent in the gpu api, but there is new shader that will do what you want:

shader_3d_polyline_smooth_color = gpu.shader.from_builtin('POLYLINE_SMOOTH_COLOR')
$\endgroup$
2

You must log in to answer this question.

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