| name | blender-core-gpu |
| description | Use when drawing custom overlays, viewport visualizations, or offscreen renders in Blender Python. Prevents the critical mistake of using the deprecated bgl module (removed in Blender 5.0) instead of the gpu module. Covers gpu.shader, gpu.batch, gpu.state, SpaceView3D draw handlers, built-in shaders, and BGL-to-gpu migration. Keywords: gpu module, bgl, draw handler, viewport overlay, offscreen rendering, SpaceView3D, shader, UNIFORM_COLOR, Blender Python drawing, draw on viewport, custom overlay, draw lines in 3D view.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires Blender 3.x/4.x/5.x with Python. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
blender-core-gpu
Quick Reference
Critical Warnings
NEVER use the bgl module — it is deprecated since Blender 3.5 and completely removed in Blender 5.0. ALL drawing code MUST use the gpu module.
NEVER use shader name 3D_UNIFORM_COLOR or 3D_FLAT_COLOR in Blender 4.0+ — the 3D_ prefix was removed. Use UNIFORM_COLOR, POLYLINE_UNIFORM_COLOR, etc.
NEVER forget to restore gpu.state to defaults at the end of draw callbacks — failing to restore state corrupts rendering for ALL subsequent draw handlers and Blender's own UI.
NEVER create gpu.types.GPUOffScreen outside a valid OpenGL/GPU context — offscreen buffers require a graphics context (viewport draw callback or bpy.types.SpaceView3D).
NEVER call gpu.shader.from_builtin() or create batches from a background thread — ALL GPU operations MUST execute on the main thread.
ALWAYS remove draw handlers in addon unregister() — leaked handlers cause crashes or persistent ghost overlays.
ALWAYS set viewportSize and lineWidth uniforms when using POLYLINE_* shaders — these are REQUIRED uniforms (Blender 4.0+).
ALWAYS call offscreen.free() when done with offscreen buffers to prevent GPU memory leaks.
Module Overview
| Module | Purpose |
|---|
gpu | Core GPU module — shader creation, state management |
gpu.shader | Shader compilation, built-in shader access |
gpu.types | GPU types: GPUShader, GPUBatch, GPUOffScreen, GPUTexture |
gpu.state | OpenGL-like state: blend, depth, line width, viewport |
gpu.texture | Texture creation from images |
gpu.matrix | Model-view-projection matrix stack |
gpu.select | GPU-based selection utilities |
gpu.platform | GPU backend info (vendor, renderer, version) |
gpu_extras.batch | batch_for_shader() helper |
gpu_extras.presets | draw_circle_2d(), draw_texture_2d() |
Built-in Shaders (Blender 4.0+)
| Shader Name | Geometry | Use Case |
|---|
'UNIFORM_COLOR' | Triangles, points | Flat colored filled geometry |
'POLYLINE_UNIFORM_COLOR' | Lines | Lines with configurable width |
'FLAT_COLOR' | Triangles | Per-vertex colored (flat shaded) |
'POLYLINE_FLAT_COLOR' | Lines | Per-vertex colored lines |
'SMOOTH_COLOR' | Triangles | Per-vertex colored (smooth shaded) |
'POLYLINE_SMOOTH_COLOR' | Lines | Smooth per-vertex colored lines |
'IMAGE' | Triangles | Textured quads/geometry |
'IMAGE_COLOR' | Triangles | Textured with color tint |
Shader Name Version Map
| Blender 3.x Name | Blender 4.0+ Name |
|---|
'3D_UNIFORM_COLOR' | 'UNIFORM_COLOR' |
'3D_FLAT_COLOR' | 'FLAT_COLOR' |
'3D_SMOOTH_COLOR' | 'SMOOTH_COLOR' |
'3D_IMAGE' | 'IMAGE' |
'2D_UNIFORM_COLOR' | 'UNIFORM_COLOR' |
'2D_FLAT_COLOR' | 'FLAT_COLOR' |
'2D_SMOOTH_COLOR' | 'SMOOTH_COLOR' |
'2D_IMAGE' | 'IMAGE' |
In Blender 4.0+, the 2D_/3D_ distinction is removed. A single 'UNIFORM_COLOR' works in both 2D and 3D contexts.
Essential Patterns
Pattern 1: Basic Line Drawing with POLYLINE Shader
import bpy
import gpu
from gpu_extras.batch import batch_for_shader
def draw_lines():
coords = [(0, 0, 0), (5, 0, 0), (5, 5, 0), (0, 5, 0), (0, 0, 0)]
shader = gpu.shader.from_builtin('POLYLINE_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": coords})
gpu.state.blend_set('ALPHA')
gpu.state.line_width_set(2.0)
shader.bind()
region = bpy.context.region
shader.uniform_float("viewportSize", (region.width, region.height))
shader.uniform_float("lineWidth", 2.0)
shader.uniform_float("color", (1.0, 0.0, 0.0, 0.8))
batch.draw(shader)
gpu.state.blend_set('NONE')
gpu.state.line_width_set(1.0)
_handle = bpy.types.SpaceView3D.draw_handler_add(draw_lines, (), 'WINDOW', 'POST_VIEW')
Pattern 2: Filled Triangles with UNIFORM_COLOR
import gpu
from gpu_extras.batch import batch_for_shader
def draw_filled():
verts = [(0, 0, 0), (5, 0, 0), (5, 5, 0), (0, 5, 0)]
indices = ((0, 1, 2), (2, 3, 0))
shader = gpu.shader.from_builtin('UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRIS', {"pos": verts}, indices=indices)
gpu.state.blend_set('ALPHA')
shader.bind()
shader.uniform_float("color", (0.0, 0.5, 1.0, 0.3))
batch.draw(shader)
gpu.state.blend_set('NONE')
Pattern 3: Draw Handler Registration/Removal
import bpy
_draw_handle = None
def register():
global _draw_handle
_draw_handle = bpy.types.SpaceView3D.draw_handler_add(
draw_callback, (), 'WINDOW', 'POST_VIEW'
)
def unregister():
global _draw_handle
if _draw_handle is not None:
bpy.types.SpaceView3D.draw_handler_remove(_draw_handle, 'WINDOW')
_draw_handle = None
Draw Handler Layer Options
| Layer | When | Coordinate Space |
|---|
'PRE_VIEW' | Before 3D scene | 3D world space |
'POST_VIEW' | After 3D scene, before gizmos | 3D world space |
'POST_PIXEL' | After everything, on top | 2D pixel space |
Pattern 4: Version-Compatible Shader Selection
import bpy
import gpu
def get_line_shader():
if bpy.app.version >= (4, 0, 0):
return gpu.shader.from_builtin('POLYLINE_UNIFORM_COLOR')
else:
return gpu.shader.from_builtin('3D_UNIFORM_COLOR')
def get_flat_shader():
if bpy.app.version >= (4, 0, 0):
return gpu.shader.from_builtin('UNIFORM_COLOR')
else:
return gpu.shader.from_builtin('3D_UNIFORM_COLOR')
Pattern 5: Offscreen Rendering
import gpu
offscreen = gpu.types.GPUOffScreen(512, 512)
with offscreen.bind():
fb = gpu.state.active_framebuffer_get()
fb.clear(color=(0.0, 0.0, 0.0, 0.0))
texture = offscreen.texture_color
offscreen.free()
Pattern 6: GPU State Management
import gpu
def draw_with_state():
gpu.state.blend_set('ALPHA')
gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(True)
gpu.state.line_width_set(2.0)
gpu.state.point_size_set(5.0)
gpu.state.face_culling_set('BACK')
gpu.state.blend_set('NONE')
gpu.state.depth_test_set('NONE')
gpu.state.depth_mask_set(False)
gpu.state.line_width_set(1.0)
gpu.state.point_size_set(1.0)
gpu.state.face_culling_set('NONE')
Common Operations
Drawing 2D Overlays (POST_PIXEL)
import bpy
import gpu
from gpu_extras.batch import batch_for_shader
def draw_2d_overlay():
verts = [(10, 10), (200, 10), (200, 50), (10, 50)]
indices = ((0, 1, 2), (2, 3, 0))
shader = gpu.shader.from_builtin('UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRIS', {"pos": verts}, indices=indices)
gpu.state.blend_set('ALPHA')
shader.bind()
shader.uniform_float("color", (0.0, 0.0, 0.0, 0.5))
batch.draw(shader)
gpu.state.blend_set('NONE')
_handle = bpy.types.SpaceView3D.draw_handler_add(
draw_2d_overlay, (), 'WINDOW', 'POST_PIXEL'
)
Drawing Textured Quads
import gpu
from gpu_extras.batch import batch_for_shader
def draw_image(texture):
shader = gpu.shader.from_builtin('IMAGE')
batch = batch_for_shader(
shader, 'TRI_FAN',
{
"pos": ((0, 0), (200, 0), (200, 200), (0, 200)),
"texCoord": ((0, 0), (1, 0), (1, 1), (0, 1)),
},
)
shader.bind()
shader.uniform_sampler("image", texture)
batch.draw(shader)
Per-Vertex Colored Geometry
import gpu
from gpu_extras.batch import batch_for_shader
shader = gpu.shader.from_builtin('SMOOTH_COLOR')
batch = batch_for_shader(shader, 'TRIS', {
"pos": [(0, 0, 0), (5, 0, 0), (2.5, 5, 0)],
"color": [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1)],
})
shader.bind()
batch.draw(shader)
Loading Image as GPU Texture
import bpy
import gpu
image = bpy.data.images.load("/path/to/image.png")
texture = gpu.texture.from_image(image)
BGL-to-gpu Migration Checklist
When migrating code from bgl to gpu:
- Remove ALL
import bgl statements
- Replace
bgl.glEnable(bgl.GL_BLEND) → gpu.state.blend_set('ALPHA')
- Replace
bgl.glDisable(bgl.GL_BLEND) → gpu.state.blend_set('NONE')
- Replace
bgl.glLineWidth(w) → gpu.state.line_width_set(w)
- Replace
bgl.glEnable(bgl.GL_DEPTH_TEST) → gpu.state.depth_test_set('LESS_EQUAL')
- Replace
bgl.glDisable(bgl.GL_DEPTH_TEST) → gpu.state.depth_test_set('NONE')
- Replace
bgl.glDepthMask(GL_TRUE) → gpu.state.depth_mask_set(True)
- Replace
bgl.glPointSize(s) → gpu.state.point_size_set(s)
- Replace
bgl.glEnable(bgl.GL_LINE_SMOOTH) → use POLYLINE_* shaders (no direct replacement)
- Replace
image.gl_load() / bgl.glBindTexture() → gpu.texture.from_image(image)
- Replace
3D_UNIFORM_COLOR → POLYLINE_UNIFORM_COLOR (for lines) or UNIFORM_COLOR (for triangles)
- Replace
3D_FLAT_COLOR → POLYLINE_FLAT_COLOR (for lines) or FLAT_COLOR (for triangles)
- Add
viewportSize and lineWidth uniforms for ALL POLYLINE_* shaders
- Add state restoration at end of every draw callback
- Test on Apple Silicon (Metal backend) — bgl was already non-functional there
BGL-to-gpu API Mapping
| bgl (REMOVED in 5.0) | gpu replacement (3.5+) | Notes |
|---|
bgl.glEnable(bgl.GL_BLEND) | gpu.state.blend_set('ALPHA') | Options: 'ALPHA', 'ADDITIVE', 'ADDITIVE_PREMUL', 'MULTIPLY' |
bgl.glDisable(bgl.GL_BLEND) | gpu.state.blend_set('NONE') | |
bgl.glLineWidth(w) | gpu.state.line_width_set(w) | Also set lineWidth uniform for POLYLINE shaders |
bgl.glEnable(bgl.GL_DEPTH_TEST) | gpu.state.depth_test_set('LESS_EQUAL') | Options: 'NONE', 'LESS', 'LESS_EQUAL', 'EQUAL', 'GREATER' |
bgl.glDisable(bgl.GL_DEPTH_TEST) | gpu.state.depth_test_set('NONE') | |
bgl.glDepthMask(GL_TRUE) | gpu.state.depth_mask_set(True) | |
bgl.glPointSize(s) | gpu.state.point_size_set(s) | |
bgl.glEnable(bgl.GL_LINE_SMOOTH) | (no direct replacement) | Use POLYLINE_* shaders for antialiased lines |
image.gl_load() / bgl.glBindTexture() | gpu.texture.from_image(image) | Completely different API |
Version Timeline
| Version | Change |
|---|
| Blender 3.0 | gpu module stable, gpu_extras.batch available |
| Blender 3.5 | bgl module officially deprecated |
| Blender 4.0 | 3D_/2D_ shader name prefixes removed; POLYLINE_* shaders added |
| Blender 5.0 | bgl module completely removed; gpu is the ONLY drawing API |
Reference Links
Cross-References
Official Sources