summaryrefslogtreecommitdiff
path: root/gfx/README.md
blob: f0b103d0b90a92092867ed14ca70177c37abcb25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# GFX - 3D Rendering Library

A portable 3D rendering library with minimal dependencies written for
educational purposes.

## Guiding Principles

- Provide enough functionality for graphics applications and indie games.
- Provide a minimal interface, physically hide the implementation. Make bindings
  easy.
- Establish a clean separation from the render backend and the rest of the
  library to allow for additional rendering backends (e.g. Vulkan).
- Strive for a minimal set of dependencies, all of which should ship and compile
  with the graphics library for ease of use.
- Rely on dynamic allocation as little as possible. Isolate dynamic allocation
  to where it is strictly needed.

## Design

### Gfx

The `Gfx` object represents the graphics subsystem and is at the center of the
library's high-level API. The `Gfx` object exposes a `Render` backend and a
`Renderer` and allows the caller to create `Scene`s.

### Render Backend

The `Render` backend is a thin abstraction layer over low-level graphics APIs
like OpenGL or Vulkan. It holds GPU resources such as geometry, textures,
shaders, etc, and exposes functions to manipulate them.

Currently there is only one implementation of the `Render` backend based on
OpenGL.

#### Ownership

The `Render` backend owns all rendering resources: buffers, geometries,
textures, shaders, etc. Even resources that point to other resources do not own
those other resources (geometries pointing to buffers).

There is no ownership tracking in the render backend. It is up to the client to
manage resource lifetime.

### Scene

A `Scene` encapsulates a scene graph. A scene graph contains the elements that
make up a scene: nodes, cameras, lights, objects, etc. The current scene graph
implementation includes:

- Camera
- Light
- Material
- Mesh
- Node
- Object
- Scene

#### Hierarchy and Parenting

Scene graphs typically expose functions on nodes to add/remove objects, cameras,
lights, etc. This implementation forces the hierarchy to be a strict tree and
not a more general DAG. Given this, and to avoid confusion, we instead expose
functions to set the parent node of an object/camera/light. If we exposed the
former, the API could create the illusion that the hierarchy can be a DAG.

The strict tree hierarchy should not be that restrictive in practice. Even the
glTF 2.0 spec [enforces this](https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#nodes-and-hierarchy):

> *For Version 2.0 conformance, the glTF node hierarchy is not a directed
> acyclic graph (DAG) or scene graph, but a disjoint union of strict trees. That
> is, no node may be a direct descendant of more than one node. This restriction
> is meant to simplify implementation and facilitate conformance.*

#### Instancing

Two use cases for instancing seem to be:

1. Creating N identical clones, but each with a unique transform. (Ex: N
animated characters animated in unison but located in different locations.)
2. Creating N copies of a sub-tree, each now being their own unique tree. (Ex:
The same N animated characters, but each of them now being animated separately.)

Some scene graphs
([Panda3D](https://docs.panda3d.org/1.10/python/programming/scene-graph/instancing))
allow two or more nodes to point to the same child, or, in other words, a node
to have multiple parents. This turns the scene graph into a DAG and adds a
number of complications for us:

1. Shared ownership of children. We would now need some sort of ref counting or
deferred GC to delete nodes and their subtrees.
2. Nodes no longer have a unique parent.
3. Given a node, we can no longer determine its location (which parent link do
you follow?), or any attribute that is derived from its parent(s).

In our case, we stick to strict tree hierarchies.

Use case (1), N identical clones with unique transforms, is not a problem for
us. This is because the bulk of the data -- geometry buffers, etc. -- is stored
in the render backend anyway. So creating a full copy of the node does not
present a significant overhead since we need a unique transform for each of the
clones anyway.

Use case (2) does present a bit more overhead and we currently do not handle it.
This could be handled in the future by special-casing a node such as
`InstanceNode` that has one child subtree and N transforms (or other
attributes), one for each unique instance of that child subtree.

Therefore, to visit the use cases again:

1. N character clones animated in unison in different locations -> future
   `InstanceNode`.
2. N unique character copies animated on their own -> copy the character subtree
   (N unique skeletons; shared mesh data and textures stored in the render
   backend.)

#### Reading

[Panda3D Scene Graph](https://docs.panda3d.org/1.10/python/programming/scene-graph/index)

[Pixar's USD](https://graphics.pixar.com/usd/release/intro.html)

### Renderer

The `Renderer` takes a `Render` backend and a `Scene` and renders the scene.
Currently, only a forward renderer is provided, but additional renderers can be
implemented in the future.

### Util

Code under `util/` provides functionality that need not be in the core part
of the library (Gfx, render backend, scene or renderer). This includes functions
to compute irradiance maps, create procedural geometry, etc.

## Ideas for Future Work

- Render graphs to allow for custom multi-pass rendering algorithms.

## Build (Ubuntu)

```
sudo apt install libbsd-dev libgl-dev libglfw3-dev zlib1g-dev
```

TODO: Add these libraries to `contrib/`.