Fix #139565: PyGPU: Add builtin point shaders

This PR adds builtin shaders for drawing points. Using `FLAT_COLOR`,
`SMOOTH_COLOR`, `UNIFORM_COLOR` can lead to undesired behavior
on Metal and Vulkan backends. To ensure future compatibility this PR
adds `POINT_FLAT_COLOR` and `POINT_UNIFORM_COLOR`.

The point size can be set using `gpu.state.point_size_set`.

Pull Request: https://projects.blender.org/blender/blender/pulls/139583
This commit is contained in:
Jeroen Bakker 2025-05-29 14:36:32 +02:00
parent fd58d730b0
commit c56a855b9f
9 changed files with 119 additions and 2 deletions

View File

@ -77,7 +77,8 @@ Typically multiple shaders are linked together into a *Program*.
However, in the Blender Python API the term *Shader* refers to an OpenGL Program.
Every :class:`gpu.types.GPUShader` consists of a vertex shader, a fragment shader and an optional geometry shader.
For common drawing tasks there are some built-in shaders accessible from :class:`gpu.shader.from_builtin`
with an identifier such as ``UNIFORM_COLOR`` or ``FLAT_COLOR``.
with an identifier such as ``UNIFORM_COLOR`` or ``FLAT_COLOR``. There are specific builtin shaders for
drawing triangles, lines and points.
Every shader defines a set of attributes and uniforms that have to be set in order to use the shader.
Attributes are properties that are set using a vertex buffer and can be different for individual vertices.
@ -140,6 +141,29 @@ To try these examples, just copy them into Blenders text editor and execute them
To keep the examples relatively small, they just register a draw function that can't easily be removed anymore.
Blender has to be restarted in order to delete the draw handlers.
3D Points with Single Color
"""
import bpy
import gpu
from gpu_extras.batch import batch_for_shader
coords = [(1, 1, 1), (-2, 0, 0), (-2, -1, 3), (0, 1, 1)]
shader = gpu.shader.from_builtin('POINT_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'POINTS', {"pos": coords})
def draw():
shader.uniform_float("color", (1, 1, 0, 1))
gpu.state.point_size_set(4.5)
batch.draw(shader)
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
"""
3D Lines with Single Color
--------------------------
"""

View File

@ -505,6 +505,7 @@ set(GLSL_SRC
shaders/gpu_shader_point_varying_color_frag.glsl
shaders/gpu_shader_3D_point_varying_size_varying_color_vert.glsl
shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl
shaders/gpu_shader_3D_point_flat_color_vert.glsl
shaders/gpu_shader_2D_point_varying_size_varying_color_vert.glsl
shaders/gpu_shader_2D_point_uniform_size_aa_vert.glsl
shaders/gpu_shader_2D_point_uniform_size_outline_aa_vert.glsl

View File

@ -104,6 +104,8 @@ enum eGPUBuiltinShader {
*/
GPU_SHADER_3D_FLAT_COLOR,
GPU_SHADER_3D_POLYLINE_FLAT_COLOR,
GPU_SHADER_3D_POINT_FLAT_COLOR,
/**
* Take a 3D position and color for each vertex with perspective correct interpolation.
*
@ -112,6 +114,7 @@ enum eGPUBuiltinShader {
*/
GPU_SHADER_3D_SMOOTH_COLOR,
GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR,
/**
* Take a single color for all the vertices and a 3D position for each vertex.
*
@ -120,6 +123,8 @@ enum eGPUBuiltinShader {
*/
GPU_SHADER_3D_UNIFORM_COLOR,
GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR,
GPU_SHADER_3D_POINT_UNIFORM_COLOR,
/**
* Draw a texture in 3D. Take a 3D position and a 2D texture coordinate for each vertex.
*

View File

@ -74,6 +74,10 @@ static const char *builtin_shader_create_info_name(eGPUBuiltinShader shader)
return "gpu_shader_3D_point_varying_size_varying_color";
case GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA:
return "gpu_shader_3D_point_uniform_size_uniform_color_aa";
case GPU_SHADER_3D_POINT_FLAT_COLOR:
return "gpu_shader_3D_point_flat_color";
case GPU_SHADER_3D_POINT_UNIFORM_COLOR:
return "gpu_shader_3D_point_uniform_color";
case GPU_SHADER_2D_AREA_BORDERS:
return "gpu_shader_2D_area_borders";
case GPU_SHADER_2D_WIDGET_BASE:

View File

@ -31,6 +31,7 @@ set(SRC_GLSL_VERT
gpu_shader_3D_normal_vert.glsl
gpu_shader_3D_point_uniform_size_aa_vert.glsl
gpu_shader_3D_point_varying_size_varying_color_vert.glsl
gpu_shader_3D_point_flat_color_vert.glsl
gpu_shader_3D_smooth_color_vert.glsl
gpu_shader_display_fallback_vert.glsl
gpu_shader_gpencil_stroke_vert.glsl

View File

@ -0,0 +1,21 @@
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_3D_flat_color_info.hh"
#include "gpu_shader_cfg_world_clip_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_flat_color)
void main()
{
float4 pos_4d = float4(pos, 1.0f);
gl_Position = ModelViewProjectionMatrix * pos_4d;
gl_PointSize = size;
finalColor = color;
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * pos_4d).xyz);
#endif
}

View File

@ -48,3 +48,29 @@ ADDITIONAL_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa)
ADDITIONAL_INFO(gpu_clip_planes)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_flat_color)
VERTEX_IN(0, float3, pos)
VERTEX_IN(1, float4, color)
VERTEX_OUT(flat_color_iface)
FRAGMENT_OUT(0, float4, fragColor)
PUSH_CONSTANT(float4x4, ModelViewProjectionMatrix)
PUSH_CONSTANT(float, size)
VERTEX_SOURCE("gpu_shader_3D_point_flat_color_vert.glsl")
FRAGMENT_SOURCE("gpu_shader_flat_color_frag.glsl")
ADDITIONAL_INFO(gpu_srgb_to_framebuffer_space)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_color)
VERTEX_IN(0, float3, pos)
VERTEX_OUT(flat_color_iface)
FRAGMENT_OUT(0, float4, fragColor)
PUSH_CONSTANT(float4x4, ModelViewProjectionMatrix)
PUSH_CONSTANT(float4, color)
PUSH_CONSTANT(float, size)
VERTEX_SOURCE("gpu_shader_3D_point_flat_color_vert.glsl")
FRAGMENT_SOURCE("gpu_shader_flat_color_frag.glsl")
ADDITIONAL_INFO(gpu_srgb_to_framebuffer_space)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()

View File

@ -352,6 +352,33 @@ static PyObject *pygpu_batch_draw(BPyGPUBatch *self, PyObject *args)
}
}
/* Emit a warning when trying to draw points with a regular shader as it is too late to
* automatically switch to a point shader. */
if (py_shader && py_shader->is_builtin && self->batch->prim_type == GPU_PRIM_POINTS) {
GPUShader *shader = py_shader->shader;
if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR)) {
PyErr_WarnEx(PyExc_DeprecationWarning,
"Calling GPUBatch.draw to draw points with "
"GPU_SHADER_3D_FLAT_COLOR is deprecated. "
"Use GPU_SHADER_3D_POINT_FLAT_COLOR instead.",
1);
}
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR)) {
PyErr_WarnEx(PyExc_DeprecationWarning,
"Calling GPUBatch.draw to draw points with "
"GPU_SHADER_3D_SMOOTH_COLOR is deprecated. "
"Use GPU_SHADER_3D_POINT_FLAT_COLOR instead.",
1);
}
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_UNIFORM_COLOR)) {
PyErr_WarnEx(PyExc_DeprecationWarning,
"Calling GPUBatch.draw to draw points with "
"GPU_SHADER_3D_UNIFORM_COLOR is deprecated. "
"Use GPU_SHADER_3D_POINT_SMOOTH_COLOR instead.",
1);
}
}
if (const char *error = pygpu_shader_check_compatibility(self->batch)) {
PyErr_SetString(PyExc_RuntimeError, error);
return nullptr;

View File

@ -59,7 +59,13 @@
" :Uniforms: vec2 viewportSize, float lineWidth\n" \
"``POLYLINE_UNIFORM_COLOR``\n" \
" :Attributes: vec3 pos\n" \
" :Uniforms: vec2 viewportSize, float lineWidth\n"
" :Uniforms: vec2 viewportSize, float lineWidth\n" \
"``POINT_FLAT_COLOR``\n" \
" :Attributes: vec3 pos, vec4 color\n" \
" :Uniforms: float size\n" \
"``POLYLINE_UNIFORM_COLOR``\n" \
" :Attributes: vec3 pos\n" \
" :Uniforms: vec4 color, float size\n"
static const PyC_StringEnumItems pygpu_shader_builtin_items[] = {
{GPU_SHADER_3D_FLAT_COLOR, "FLAT_COLOR"},
@ -70,6 +76,8 @@ static const PyC_StringEnumItems pygpu_shader_builtin_items[] = {
{GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "POLYLINE_FLAT_COLOR"},
{GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "POLYLINE_SMOOTH_COLOR"},
{GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "POLYLINE_UNIFORM_COLOR"},
{GPU_SHADER_3D_POINT_FLAT_COLOR, "POINT_FLAT_COLOR"},
{GPU_SHADER_3D_POINT_UNIFORM_COLOR, "POINT_UNIFORM_COLOR"},
{0, nullptr},
};