Skip to content

Commit 809dbe3

Browse files
authored
Clarify compute shader N-body tutorial (#1798)
* Use a more manageable default size for the screen * Set resizable to false to play safe with tiling WMs * Make the tutorial friendlier to low-end systems by using fewer particles * Use a top-level constant to control the number of stars * Make naming consistent, ie stars instead of balls * s/effected/affected/ + phrasing tweak in same sentence * Use self.position instead of .center_x, .center_y * Change the window title to be clearer * s/ball/star/g in the computer shader example's glsl * Improve clarity by renaming buffer variables and updating comments * Clean up buffer initialization by using a nested function * Use standard conditional execution for the example * Add units to comments * Make phrasing consistent in program parts list * Explain templating behavior at the top of the compute shader * Use pathlib.Path.read_text instead of longer with statememnts * Add comments explaining input variables to compute shaders * Randomize star color to make movement visualization easier * Incorporate feedback from discord discussion * Use clearer names for the buffers * Clarify a comment * Add missing "the" to increase smoothness * Add notes on vec3 being fake based on discussion with @einarf * Extend comment to support the .rst file * Rephrase label in SVG diagram * Move initial data generation into separate function to make indent cleaner * Clean up the top of the tutorial's __init__ * Update file docstring, window title, and class name * Remove unused instance variable & reorganize code for easier inclusion in .rst * Initialize both ssbo buffers with the same data to start with * Add section on buffer allocation to computer shader tutorial * Tweak tutorial intro phrasing * Tweak buffer opening phrasing * Add index to SVG diagram label to indicate parallel iteration * Subdivide visualization section with headings + touch up Vertex & Geometry shader sections * Make ordering of variables identical across doc, vertex, and geometry shader * Improve clarity of Geometry Shader section & GLSL * Clean up rst description of geometry & fragment shaders * Finish first full rework draft * Make star color togglable & off by default to match the embedded video * Update example portion line numbers * Fix an ambiguous comment in compute shader * Fix plural in heading
1 parent 7068d35 commit 809dbe3

File tree

5 files changed

+261
-146
lines changed

5 files changed

+261
-146
lines changed

doc/tutorials/compute_shader/index.rst

Lines changed: 92 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,78 +7,143 @@ Compute Shader Tutorial
77

88
<div style="width: 100%; height: 0px; position: relative; padding-bottom: 56.250%;"><iframe src="https://streamable.com/e/ab8d87" frameborder="0" width="100%" height="100%" allowfullscreen style="width: 100%; height: 100%; position: absolute;"></iframe></div>
99

10-
Using the compute shader, you can use the GPU to perform calculations thousands
11-
of times faster than just by using the CPU.
10+
For certain types of calculations, compute shaders on the GPU can be
11+
thousands of times faster than on the CPU alone.
1212

13-
In this example, we will simulate a star field using an 'N-Body simulation'. Each
14-
star is effected by each other star's gravity. For 1,000 stars, this means we have
13+
In this tutorial, we will simulate a star field using an 'N-Body simulation'. Each
14+
star is affected by the gravity of every other star. For 1,000 stars, this means we have
1515
1,000 x 1,000 = 1,000,000 million calculations to perform for each frame.
1616
The video has 65,000 stars, requiring 4.2 billion gravity force calculations per frame.
1717
On high-end hardware it can still run at 60 fps!
1818

1919
How does this work?
2020
There are three major parts to this program:
2121

22-
* The Python code, this glues everything together.
23-
* The visualization shaders, which let us see the data.
24-
* The compute shader, which moves everything.
22+
* The Python code, which allocates buffers & glues everything together
23+
* The visualization shaders, which let us see the data in the buffers
24+
* The compute shader, which moves everything
25+
26+
Buffers
27+
-------
28+
29+
We need a place to store the data we'll visualize. To do so, we'll create
30+
two **Shader Storage Buffer Objects** (SSBOs) of floating point numbers from
31+
within our Python code. One will hold the previous frame's star positions,
32+
and the other will be used to store calculate the next frame's positions.
33+
34+
Each buffer must be able to store the following for each star:
35+
36+
1. The x, y, and radius of each star stored
37+
2. The velocity of the star, which will be unused by the visualization
38+
3. The floating point RGBA color of the star
39+
40+
41+
Generating Aligned Data
42+
^^^^^^^^^^^^^^^^^^^^^^^
43+
44+
To avoid issues with GPU memory alignment quirks, we'll use the function
45+
below to generate well-aligned data ready to load into the SSBO. The
46+
docstrings & comments explain why in greater detail:
47+
48+
.. literalinclude:: main.py
49+
:language: python
50+
:caption: Generating Well-Aligned Data to Load onto the GPU
51+
:lines: 25-70
52+
53+
Allocating the Buffers
54+
^^^^^^^^^^^^^^^^^^^^^^
55+
56+
.. literalinclude:: main.py
57+
:language: python
58+
:caption: Allocating the Buffers & Loading the Data onto the GPU
59+
:lines: 88-116
60+
2561

2662
Visualization Shaders
2763
---------------------
2864

29-
There are multiple visualization shaders, which operate in this order:
65+
Now that we have the data, we need to be able to visualize it. We'll do
66+
it by applying vertex, geometry, and fragment shaders to convert the
67+
data in the SSBO into pixels. For each star's 12 floats in the array, the
68+
following flow of data will take place:
3069

3170
.. image:: shaders.svg
3271

33-
The Python program creates a **shader storage buffer object** (SSBO) of
34-
floating point numbers. This buffer
35-
has the x, y, z and radius of each star stored in ``in_vertex``. It also
36-
stores the color in ``in_color``.
72+
Vertex Shader
73+
^^^^^^^^^^^^^
74+
75+
In this tutorial, the vertex shader will be run for each star's 12 float
76+
long stretch of raw padded data in ``self.ssbo_current``. Each execution
77+
will output clean typed data to an instance of the geometry shader.
78+
79+
Data is read in as follows:
3780

38-
The **vertex shader** doesn't do much more than separate out the radius
39-
variable from the group of floats used to store position.
81+
* The x, y, and radius of each star are accessed via ``in_vertex``
82+
* The floating point RGBA color of the star, via ``in_color``
4083

4184
.. literalinclude:: shaders/vertex_shader.glsl
4285
:language: glsl
4386
:caption: shaders/vertex_shader.glsl
4487
:linenos:
4588

46-
The **geometry shader** converts the single point (which we can't render) to
47-
a square, which we can render. It changes the one point, to four points of a quad.
89+
The variables below are then passed as inputs to the geometry shader:
90+
91+
* ``vertex_pos``
92+
* ``vertex_radius``
93+
* ``vertex_color``
94+
95+
Geometry Shader
96+
^^^^^^^^^^^^^^^
97+
98+
The **geometry shader** converts a single point into a quad, in this
99+
case a square, which the GPU can render. It does this by emitting four
100+
points centered on the input point.
48101

49102
.. literalinclude:: shaders/geometry_shader.glsl
50103
:language: glsl
51104
:caption: shaders/geometry_shader.glsl
52105
:linenos:
53106

54-
The **fragment shader** runs for each pixel. It produces the soft glow effect of the
55-
star, and rounds off the quad into a circle.
107+
Fragment Shader
108+
^^^^^^^^^^^^^^^
109+
110+
A **fragment shader** runs for each pixel in a quad. It converts a UV
111+
coordinate within the quad to a float RGBA value. In this tutorial's
112+
case, the shader produces the soft glowing circle on the surface of each
113+
star's quad.
56114

57115
.. literalinclude:: shaders/fragment_shader.glsl
58116
:language: glsl
59117
:caption: shaders/fragment_shader.glsl
60118
:linenos:
61119

62120

63-
Compute Shaders
64-
---------------
121+
Compute Shader
122+
--------------
123+
124+
Now that we have a way to display data, we should update it.
65125

66-
This program runs two buffers. We have an **input buffer**, with all our current data. We perform
67-
calculations on that data and write to the **output buffer**. We then swap those buffers for the
68-
next frame, where we use the output of the previous frame as the input to the next frame.
126+
We created pairs of buffers earlier. We will use one SSBO as an
127+
**input buffer** holding the previous frame's data, and another
128+
as our **output** buffer to write results to.
129+
130+
We then swap our buffers each frame after drawing, using the output
131+
as the input of the next frame, and repeat the process until the program
132+
stops running.
69133

70134
.. literalinclude:: shaders/compute_shader.glsl
71135
:language: glsl
72136
:caption: shaders/compute_shader.glsl
73137
:linenos:
74138

75-
Python Program
76-
--------------
139+
The Finished Python Program
140+
---------------------------
77141

78-
Read through the code here, I've tried hard to explain all the parts in the comments.
142+
The code includes thorough docstrings and annotations explaining how it works.
79143

80144
.. literalinclude:: main.py
81145
:caption: main.py
82146
:linenos:
83147

84-
An expanded version of this, with support for 3D, is available at: https://github.com/pvcraven/n-body
148+
An expanded version of this tutorial whith support for 3D is available at:
149+
https://github.com/pvcraven/n-body

0 commit comments

Comments
 (0)