forked from bartvdbraak/blender
Cleanup: gpu docs
This commit is contained in:
parent
5f21030a81
commit
3c8c976086
@ -4,7 +4,8 @@ Geometry Batches
|
||||
|
||||
Geometry is drawn in batches.
|
||||
A batch contains the necessary data to perform the drawing.
|
||||
That includes an obligatory *Vertex Buffer* and an optional *Index Buffer*, each of which is described in more detail in the following sections.
|
||||
That includes an obligatory *Vertex Buffer* and an optional *Index Buffer*,
|
||||
each of which is described in more detail in the following sections.
|
||||
A batch also defines a draw type.
|
||||
Typical draw types are `POINTS`, `LINES` and `TRIS`.
|
||||
The draw type determines how the data will be interpreted and drawn.
|
||||
@ -12,25 +13,29 @@ The draw type determines how the data will be interpreted and drawn.
|
||||
Vertex Buffers
|
||||
++++++++++++++
|
||||
|
||||
A *Vertex Buffer Object* (VBO) (:class:`gpu.types.GPUVertBuf`) is an array that contains the vertex attributes needed for drawing using a specific shader.
|
||||
A *Vertex Buffer Object* (VBO) (:class:`gpu.types.GPUVertBuf`)
|
||||
is an array that contains the vertex attributes needed for drawing using a specific shader.
|
||||
Typical vertex attributes are *location*, *normal*, *color*, and *uv*.
|
||||
Every vertex buffer has a *Vertex Format* (:class:`gpu.types.GPUVertFormat`) and a length corresponding to the number of vertices in the buffer.
|
||||
Every vertex buffer has a *Vertex Format* (:class:`gpu.types.GPUVertFormat`)
|
||||
and a length corresponding to the number of vertices in the buffer.
|
||||
A vertex format describes the attributes stored per vertex and their types.
|
||||
|
||||
The following code demonstrates the creation of a vertex buffer that contains 6 vertices.
|
||||
For each vertex 2 attributes will be stored: The position and the normal::
|
||||
For each vertex 2 attributes will be stored: The position and the normal.
|
||||
|
||||
import gpu
|
||||
vertex_positions = [(0, 0, 0), ...]
|
||||
vertex_normals = [(0, 0, 1), ...]
|
||||
.. code-block:: python
|
||||
|
||||
fmt = gpu.types.GPUVertFormat()
|
||||
fmt.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
|
||||
fmt.attr_add(id="normal", comp_type='F32', len=3, fetch_mode='FLOAT')
|
||||
import gpu
|
||||
vertex_positions = [(0, 0, 0), ...]
|
||||
vertex_normals = [(0, 0, 1), ...]
|
||||
|
||||
vbo = gpu.types.GPUVertBuf(len=6, format=fmt)
|
||||
vbo.attr_fill(id="pos", data=vertex_positions)
|
||||
vbo.attr_fill(id="normal", data=vertex_normals)
|
||||
fmt = gpu.types.GPUVertFormat()
|
||||
fmt.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
|
||||
fmt.attr_add(id="normal", comp_type='F32', len=3, fetch_mode='FLOAT')
|
||||
|
||||
vbo = gpu.types.GPUVertBuf(len=6, format=fmt)
|
||||
vbo.attr_fill(id="pos", data=vertex_positions)
|
||||
vbo.attr_fill(id="normal", data=vertex_normals)
|
||||
|
||||
This vertex buffer could be used to draw 6 points, 3 separate lines, 5 consecutive lines, 2 separate triangles, ...
|
||||
E.g. in the case of lines, each two consecutive vertices define a line.
|
||||
@ -42,19 +47,24 @@ Index Buffers
|
||||
Often triangles and lines share one or more vertices.
|
||||
With only a vertex buffer one would have to store all attributes for the these vertices multiple times.
|
||||
This is very inefficient because in a connected triangle mesh every vertex is used 6 times on average.
|
||||
A more efficient approach would be to use an *Index Buffer* (IBO) (:class:`gpu.types.GPUIndexBuf`), sometimes referred to as *Element Buffer*.
|
||||
A more efficient approach would be to use an *Index Buffer* (IBO) (:class:`gpu.types.GPUIndexBuf`),
|
||||
sometimes referred to as *Element Buffer*.
|
||||
An *Index Buffer* is an array that references vertices based on their index in the vertex buffer.
|
||||
For instance, to draw a rectangle composed of two triangles, one could use an index buffer::
|
||||
|
||||
positions = (
|
||||
(-1, 1), (1, 1),
|
||||
(-1, -1), (1, -1))
|
||||
For instance, to draw a rectangle composed of two triangles, one could use an index buffer.
|
||||
|
||||
indices = ((0, 1, 2), (2, 1, 3))
|
||||
.. code-block:: python
|
||||
|
||||
ibo = gpu.types.GPUIndexBuf(type='TRIS', seq=indices)
|
||||
positions = (
|
||||
(-1, 1), (1, 1),
|
||||
(-1, -1), (1, -1))
|
||||
|
||||
Here the first tuple in `indices` describes which vertices should be used for the first triangle (same for the second tuple).
|
||||
indices = ((0, 1, 2), (2, 1, 3))
|
||||
|
||||
ibo = gpu.types.GPUIndexBuf(type='TRIS', seq=indices)
|
||||
|
||||
Here the first tuple in `indices` describes which vertices should be used for the first triangle
|
||||
(same for the second tuple).
|
||||
Note how the diagonal vertices 1 and 2 are shared between both triangles.
|
||||
|
||||
Shaders
|
||||
@ -66,7 +76,8 @@ The most important ones are *Vertex Shaders* and *Fragment Shaders*.
|
||||
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 `2D_UNIFORM_COLOR` or `3D_FLAT_COLOR`.
|
||||
For common drawing tasks there are some built-in shaders accessible from :class:`gpu.shader.from_builtin`
|
||||
with an identifier such as `2D_UNIFORM_COLOR` or `3D_FLAT_COLOR`.
|
||||
|
||||
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.
|
||||
@ -89,14 +100,18 @@ Offscreen Rendering
|
||||
+++++++++++++++++++
|
||||
|
||||
What one can see on the screen after rendering is called the *Front Buffer*.
|
||||
When draw calls are issued, batches are drawn on a *Back Buffer* that will only be displayed when all drawing is done and the current back buffer will become the new front buffer.
|
||||
Sometimes, one might want to draw the batches into a distinct buffer that could be used as texture to display on another object or to be saved as image on disk.
|
||||
When draw calls are issued, batches are drawn on a *Back Buffer* that will only be displayed
|
||||
when all drawing is done and the current back buffer will become the new front buffer.
|
||||
Sometimes, one might want to draw the batches into a distinct buffer that could be used as
|
||||
texture to display on another object or to be saved as image on disk.
|
||||
This is called Offscreen Rendering.
|
||||
In Blender Offscreen Rendering is done using the :class:`gpu.types.GPUOffScreen` type.
|
||||
|
||||
.. warning::
|
||||
`GPUOffScreen` objects are bound to the OpenGL context they have been created in.
|
||||
This means that once Blender discards this context (i.e. the window is closed), the offscreen instance will be freed.
|
||||
|
||||
`GPUOffScreen` objects are bound to the OpenGL context they have been created in.
|
||||
This means that once Blender discards this context (i.e. the window is closed),
|
||||
the offscreen instance will be freed.
|
||||
|
||||
Examples
|
||||
++++++++
|
||||
@ -104,4 +119,4 @@ Examples
|
||||
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.
|
||||
"""
|
||||
"""
|
||||
|
@ -3,7 +3,8 @@ Rendering the 3D View into a Texture
|
||||
------------------------------------
|
||||
|
||||
The scene has to have a camera for this example to work.
|
||||
You could also make this independent of a specific camera, but Blender does not expose good functions to create view and projection matrices yet.
|
||||
You could also make this independent of a specific camera,
|
||||
but Blender does not expose good functions to create view and projection matrices yet.
|
||||
"""
|
||||
import bpy
|
||||
import bgl
|
||||
@ -15,6 +16,7 @@ HEIGHT = 256
|
||||
|
||||
offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT)
|
||||
|
||||
|
||||
def draw():
|
||||
context = bpy.context
|
||||
scene = context.scene
|
||||
@ -35,4 +37,5 @@ def draw():
|
||||
bgl.glDisable(bgl.GL_DEPTH_TEST)
|
||||
draw_texture_2d(offscreen.color_texture, (10, 10), WIDTH, HEIGHT)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
@ -3,8 +3,9 @@ Custom Shader for dotted 3D Line
|
||||
--------------------------------
|
||||
|
||||
In this example the arc length (distance to the first point on the line) is calculated in every vertex.
|
||||
Between the vertex and fragment shader that value is automatically interpolated for all points that will be visible on the screen.
|
||||
In the fragment shader the `sin` of the arc length is calculated.
|
||||
Between the vertex and fragment shader that value is automatically interpolated
|
||||
for all points that will be visible on the screen.
|
||||
In the fragment shader the ``sin`` of the arc length is calculated.
|
||||
Based on the result a decision is made on whether the fragment should be drawn or not.
|
||||
"""
|
||||
import bpy
|
||||
@ -47,9 +48,11 @@ for a, b in zip(coords[:-1], coords[1:]):
|
||||
arc_lengths.append(arc_lengths[-1] + (a - b).length)
|
||||
|
||||
shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||
batch = batch_for_shader(shader, 'LINE_STRIP',
|
||||
{"position" : coords,
|
||||
"arcLength" : arc_lengths})
|
||||
batch = batch_for_shader(
|
||||
shader, 'LINE_STRIP',
|
||||
{"position": coords, "arcLength": arc_lengths},
|
||||
)
|
||||
|
||||
|
||||
def draw():
|
||||
shader.bind()
|
||||
@ -58,4 +61,5 @@ def draw():
|
||||
shader.uniform_float("u_Scale", 10)
|
||||
batch.draw(shader)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
@ -8,12 +8,13 @@ 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('3D_UNIFORM_COLOR')
|
||||
batch = batch_for_shader(shader, 'LINES', {"pos" : coords})
|
||||
batch = batch_for_shader(shader, 'LINES', {"pos": coords})
|
||||
|
||||
|
||||
def draw():
|
||||
shader.bind()
|
||||
shader.uniform_float("color", (1, 1, 0, 1))
|
||||
batch.draw(shader)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
@ -32,7 +32,8 @@ fragment_shader = '''
|
||||
|
||||
coords = [(1, 1, 1), (2, 0, 0), (-2, -1, 3)]
|
||||
shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||
batch = batch_for_shader(shader, 'TRIS', {"position" : coords})
|
||||
batch = batch_for_shader(shader, 'TRIS', {"position": coords})
|
||||
|
||||
|
||||
def draw():
|
||||
shader.bind()
|
||||
@ -41,4 +42,5 @@ def draw():
|
||||
shader.uniform_float("brightness", 0.5)
|
||||
batch.draw(shader)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
@ -7,10 +7,10 @@ import gpu
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
coords = (
|
||||
(-1, -1, -1), ( 1, -1, -1),
|
||||
(-1, 1, -1), ( 1, 1, -1),
|
||||
(-1, -1, 1), ( 1, -1, 1),
|
||||
(-1, 1, 1), ( 1, 1, 1))
|
||||
(-1, -1, -1), (+1, -1, -1),
|
||||
(-1, +1, -1), (+1, +1, -1),
|
||||
(-1, -1, +1), (+1, -1, +1),
|
||||
(-1, +1, +1), (+1, +1, +1))
|
||||
|
||||
indices = (
|
||||
(0, 1), (0, 2), (1, 3), (2, 3),
|
||||
@ -18,11 +18,13 @@ indices = (
|
||||
(0, 4), (1, 5), (2, 6), (3, 7))
|
||||
|
||||
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
|
||||
batch = batch_for_shader(shader, 'LINES', {"pos" : coords}, indices=indices)
|
||||
batch = batch_for_shader(shader, 'LINES', {"pos": coords}, indices=indices)
|
||||
|
||||
|
||||
def draw():
|
||||
shader.bind()
|
||||
shader.uniform_float("color", (1, 0, 0, 1))
|
||||
batch.draw(shader)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
@ -14,20 +14,23 @@ mesh.calc_loop_triangles()
|
||||
vertices = np.empty((len(mesh.vertices), 3), 'f')
|
||||
indices = np.empty((len(mesh.loop_triangles), 3), 'i')
|
||||
|
||||
mesh.vertices.foreach_get("co",
|
||||
np.reshape(vertices, len(mesh.vertices) * 3))
|
||||
mesh.loop_triangles.foreach_get("vertices",
|
||||
np.reshape(indices, len(mesh.loop_triangles) * 3))
|
||||
mesh.vertices.foreach_get(
|
||||
"co", np.reshape(vertices, len(mesh.vertices) * 3))
|
||||
mesh.loop_triangles.foreach_get(
|
||||
"vertices", np.reshape(indices, len(mesh.loop_triangles) * 3))
|
||||
|
||||
vertex_colors = [(random(), random(), random(), 1) for _ in range(len(mesh.vertices))]
|
||||
|
||||
shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
|
||||
batch = batch_for_shader(shader, 'TRIS',
|
||||
{"pos" : vertices,
|
||||
"color" : vertex_colors},
|
||||
indices=indices)
|
||||
batch = batch_for_shader(
|
||||
shader, 'TRIS',
|
||||
{"pos": vertices, "color": vertex_colors},
|
||||
indices=indices,
|
||||
)
|
||||
|
||||
|
||||
def draw():
|
||||
batch.draw(shader)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
@ -14,11 +14,13 @@ indices = (
|
||||
(0, 1, 2), (2, 1, 3))
|
||||
|
||||
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
||||
batch = batch_for_shader(shader, 'TRIS', {"pos" : vertices}, indices=indices)
|
||||
batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
|
||||
|
||||
|
||||
def draw():
|
||||
shader.bind()
|
||||
shader.uniform_float("color", (0, 0.5, 0.5, 1.0))
|
||||
batch.draw(shader)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
@ -13,13 +13,18 @@ IMAGE_NAME = "Untitled"
|
||||
image = bpy.data.images[IMAGE_NAME]
|
||||
|
||||
shader = gpu.shader.from_builtin('2D_IMAGE')
|
||||
batch = batch_for_shader(shader, 'TRI_FAN',
|
||||
{"pos" : ((100, 100), (200, 100), (200, 200), (100, 200)),
|
||||
"texCoord" : ((0, 0), (1, 0), (1, 1), (0, 1))})
|
||||
batch = batch_for_shader(
|
||||
shader, 'TRI_FAN',
|
||||
{
|
||||
"pos": ((100, 100), (200, 100), (200, 200), (100, 200)),
|
||||
"texCoord": ((0, 0), (1, 0), (1, 1), (0, 1)),
|
||||
},
|
||||
)
|
||||
|
||||
if image.gl_load():
|
||||
raise Exception()
|
||||
|
||||
|
||||
def draw():
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode)
|
||||
@ -28,4 +33,5 @@ def draw():
|
||||
shader.uniform_int("image", 0)
|
||||
batch.draw(shader)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
@ -2,10 +2,10 @@
|
||||
Generate a texture using Offscreen Rendering
|
||||
--------------------------------------------
|
||||
|
||||
1. Create an :class:`gpu.types.GPUOffScreen` object.
|
||||
2. Draw some circles into it.
|
||||
3. Make a new shader for drawing a planar texture in 3D.
|
||||
4. Draw the generated texture using the new shader.
|
||||
#. Create an :class:`gpu.types.GPUOffScreen` object.
|
||||
#. Draw some circles into it.
|
||||
#. Make a new shader for drawing a planar texture in 3D.
|
||||
#. Draw the generated texture using the new shader.
|
||||
"""
|
||||
import bpy
|
||||
import gpu
|
||||
@ -63,9 +63,13 @@ fragment_shader = '''
|
||||
'''
|
||||
|
||||
shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||
batch = batch_for_shader(shader, 'TRI_FAN',
|
||||
{"position" : ((-1, -1), (1, -1), (1, 1), (-1, 1)),
|
||||
"uv" : ((0, 0), (1, 0), (1, 1), (0, 1))})
|
||||
batch = batch_for_shader(
|
||||
shader, 'TRI_FAN',
|
||||
{
|
||||
"position": ((-1, -1), (1, -1), (1, 1), (-1, 1)),
|
||||
"uv": ((0, 0), (1, 0), (1, 1), (0, 1)),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def draw():
|
||||
@ -78,4 +82,5 @@ def draw():
|
||||
shader.uniform_float("image", 0)
|
||||
batch.draw(shader)
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
||||
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
@ -6,7 +6,8 @@ This will create a new image with the given name.
|
||||
If it already exists, it will override the existing one.
|
||||
|
||||
Currently almost all of the execution time is spent in the last line.
|
||||
In the future this will hopefully be solved by implementing the Python buffer protocol for `bgl.Buffer` and `Image.pixels` (aka `bpy_prop_array`).
|
||||
In the future this will hopefully be solved by implementing the Python buffer protocol
|
||||
for :class:`bgl.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``).
|
||||
"""
|
||||
import bpy
|
||||
import gpu
|
||||
@ -35,7 +36,6 @@ with offscreen.bind():
|
||||
(random.uniform(-1, 1), random.uniform(-1, 1)),
|
||||
(1, 1, 1, 1), random.uniform(0.1, 1), 20)
|
||||
|
||||
|
||||
buffer = bgl.Buffer(bgl.GL_BYTE, WIDTH * HEIGHT * 4)
|
||||
bgl.glReadBuffer(bgl.GL_BACK)
|
||||
bgl.glReadPixels(0, 0, WIDTH, HEIGHT, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
|
||||
@ -47,4 +47,4 @@ if not IMAGE_NAME in bpy.data.images:
|
||||
bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT)
|
||||
image = bpy.data.images[IMAGE_NAME]
|
||||
image.scale(WIDTH, HEIGHT)
|
||||
image.pixels = [v / 255 for v in buffer]
|
||||
image.pixels = [v / 255 for v in buffer]
|
||||
|
@ -4,32 +4,33 @@ Built-in shaders
|
||||
|
||||
All built-in shaders have the ``mat4 ModelViewProjectionMatrix`` uniform.
|
||||
The value of it can only be modified using the :class:`gpu.matrix` module.
|
||||
"""
|
||||
|
||||
2D_UNIFORM_COLOR:
|
||||
attributes: vec3 pos
|
||||
uniforms: vec4 color
|
||||
attributes: vec3 pos
|
||||
uniforms: vec4 color
|
||||
|
||||
2D_FLAT_COLOR:
|
||||
attributes: vec3 pos, vec4 color
|
||||
uniforms: -
|
||||
attributes: vec3 pos, vec4 color
|
||||
uniforms: -
|
||||
|
||||
2D_SMOOTH_COLOR:
|
||||
attributes: vec3 pos, vec4 color
|
||||
uniforms: -
|
||||
attributes: vec3 pos, vec4 color
|
||||
uniforms: -
|
||||
|
||||
2D_IMAGE:
|
||||
attributes: vec3 pos, vec2 texCoord
|
||||
uniforms: sampler2D image
|
||||
attributes: vec3 pos, vec2 texCoord
|
||||
uniforms: sampler2D image
|
||||
|
||||
3D_UNIFORM_COLOR:
|
||||
attributes: vec3 pos
|
||||
uniforms: vec4 color
|
||||
attributes: vec3 pos
|
||||
uniforms: vec4 color
|
||||
|
||||
3D_FLAT_COLOR:
|
||||
attributes: vec3 pos, vec4 color
|
||||
uniforms: -
|
||||
attributes: vec3 pos, vec4 color
|
||||
uniforms: -
|
||||
|
||||
3D_SMOOTH_COLOR:
|
||||
attributes: vec3 pos, vec4 color
|
||||
uniforms: -
|
||||
attributes: vec3 pos, vec4 color
|
||||
uniforms: -
|
||||
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user