Skip to content

Commit

Permalink
Merge pull request #1662 from heinezen/docs/frustum_culling
Browse files Browse the repository at this point in the history
Frustum culling documentation
  • Loading branch information
TheJJ authored Jul 29, 2024
2 parents 4a8b88d + ae9dd8f commit d66e9f9
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 28 deletions.
6 changes: 4 additions & 2 deletions assets/test/shaders/demo_6_2d_frame.frag.glsl
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#version 330

out vec4 col;
out vec4 outcol;

uniform vec4 incol;

void main() {
col = vec4(1.0, 0.0, 0.0, 0.8);
outcol = incol;
}
8 changes: 8 additions & 0 deletions assets/test/shaders/demo_6_2d_frustum_frame.vert.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#version 330

layout(location=0) in vec2 v_position;

void main() {
// flip the y coordinate in OpenGL
gl_Position = vec4(v_position.x, v_position.y, 0.0, 1.0);
}
172 changes: 172 additions & 0 deletions doc/code/renderer/demos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Interactive Demos & Stresstests

openage builds contain several *interactive* renderer tech demo entrypoints that show of specific features.
These demos are also useful for learning the renderer API and for testing new functionality. In addition to
the demos, there are also stresstests that are used to test the performance of the renderer.
The source code for renderer demos and stresstests is located in [`libopenage/renderer/demo`](/libopenage/renderer/demo/).

This documents describes the purpose of each demo and contains instructions on how to interact with them.

1. [Demos](#demos)
1. [Demo 0](#demo-0)

## Demos

### Demo 0

This demo shows the creation of a minmal renderer setup and the rendering of a simple mesh.

The demo initializes a GUI application, a window, a renderer object, and a render pass.
It then loads a shader program and creates a single mesh object which is then rendered to the screen
using the shader program.

The demo mostly follows the steps described in the [Level 1 Renderer - Basic Usage](level1.md#basic-usage)
documentation.

```bash
./bin/run test --demo renderer.tests.renderer_demo 0
```

**Result:**

![Demo 0](/doc/code/renderer/images/demo_0.png)


### Demo 1

This demo shows how simple *textured* meshes can be created and rendered. It also demonstrates
how to interact with the window and the renderer using window callbacks.

```bash
./bin/run test --demo renderer.tests.renderer_demo 1
```

**Controls:**

- <kbd>LMB</kbd>: Click on the textured meshes to get a debug message with the object ID.

**Result:**

![Demo 1](/doc/code/renderer/images/demo_1.png)


### Demo 2

In this demo, we show how animation and texture metadata files are parsed and used to
load and render the correct textures and animations for a mesh.

```bash
./bin/run test --demo renderer.tests.renderer_demo 2
```

**Controls:**

- <kbd>LMB</kbd>: Click on the sprite to get a debug message with the object ID.
- <kbd>←</kbd>: Go back one frame in the animation.
- <kbd>→</kbd>: Advance the animation by one frame.

**Result:**

![Demo 2](/doc/code/renderer/images/demo_2.png)


### Demo 3

This demo shows a minimal setup for the [Level 2 Renderer](level2.md) and how to render objects
with it. The demo also introduces the camera system and how to interact with it.

```bash
./bin/run test --demo renderer.tests.renderer_demo 3
```

**Controls:**

- <kbd>W</kbd>, <kbd>A</kbd>, <kbd>S</kbd>, <kbd>D</kbd>: Move the camera in the scene.
- `Mouse Wheel`: Zoom in and out.

**Result:**

![Demo 3](/doc/code/renderer/images/demo_3.png)


### Demo 4

This demos shows how animation frame timing works and how to control the animation speed
with the engine's internal clock.

```bash
./bin/run test --demo renderer.tests.renderer_demo 4
```

**Controls:**

- <kbd>Space</kbd>: Pause/resume the clock.
- <kbd>+</kbd>, <kbd>-</kbd>: Increase/decrease the simulation speed.
- <kbd>Return</kbd>: Toggle between real time and simulation time.

**Result:**

![Demo 4](/doc/code/renderer/images/demo_4.png)


### Demo 5

This demo shows how to create [uniform buffers](level1.md#uniform-buffers) and how to use them to pass data to shaders.
Additionally, uniform buffer usage for the camera system is demonstrated.

```bash
./bin/run test --demo renderer.tests.renderer_demo 5
```

**Controls:**

- <kbd>W</kbd>, <kbd>A</kbd>, <kbd>S</kbd>, <kbd>D</kbd>: Move the camera in the scene.

**Result:**

![Demo 5](/doc/code/renderer/images/demo_5.png)


### Demo 6

This demo shows how to use [frustum culling](level2.md#frustum-culling) in the renderer.

```bash
./bin/run test --demo renderer.tests.renderer_demo 6
```

**Controls:**

- <kbd>W</kbd>, <kbd>A</kbd>, <kbd>S</kbd>, <kbd>D</kbd>: Move the camera in the scene.

**Result:**

![Demo 6](/doc/code/renderer/images/demo_6.png)


## Stresstests

### Stresstest 0

This stresstest tests the performance when rendering an increasingly larger number of objects.

```bash
./bin/run test --demo renderer.tests.stresstest 0
```

**Result:**

![Stresstest 0](/doc/code/renderer/images/stresstest_0.png)

### Stresstest 1

This stresstest tests the performance when [frustum culling](level2.md#frustum-culling) is enabled and an increasingly larger
number of objects is rendered on the screen.

```bash
./bin/run test --demo renderer.tests.stresstest 1
```

**Result:**

![Stresstest 1](/doc/code/renderer/images/stresstest_1.png)
Binary file added doc/code/renderer/images/demo_0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/code/renderer/images/demo_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/code/renderer/images/demo_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/code/renderer/images/demo_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/code/renderer/images/demo_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/code/renderer/images/demo_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/code/renderer/images/demo_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/code/renderer/images/stresstest_0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/code/renderer/images/stresstest_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions doc/code/renderer/level2.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ High-level renderer for transforming data from the gamestate to render objects f
2. [Stages](#stages)
1. [Updating Render Stages from the Gamestate](#updating-render-stages-from-the-gamestate)
3. [Camera](#camera)
1. [Frustum Culling](#frustum-culling)

## Stages

Expand Down Expand Up @@ -61,3 +62,40 @@ Zoom levels can also be adjusted with these methods:
For displaying 3D objects, the `Camera` can also calculate a view matrix (`get_view_matrix()`) and projection matrix (`get_projection_matrix()`) that take current position and zoom level into account.

Camera parameters may be used for raycasting operations, e.g. mouse picking/selection. Since the camera utilizes orthographic projection and a fied angle, the ray direction is exactly the same as the camera direction vector (accessible as `cam_direction`). To find the origin point of a ray for a pixel coordinate in the viewport, the `get_input_pos(..)` method can be used. This method calculates the position of the pixel coordinate on the othographic camera plane that represents the viewport. The result is the absolute position of the pixel coordinate inside the 3D scene. Ray origin point and direction can then be used to perform calculations for line-plane or line sphere intersections.

### Frustum Culling

Frustum culling is a technique used to discard objects that are outside the view frustum of the camera.
This can save a lot of computation time that would be spent on updating shaders and rendering objects
that are not visible in the camera's view.

The openage renderer provides two frustum types: 2D and 3D frustums. 2D frustums are used
for sprite animations and other 2D objects, while 3D frustums are used for 3D objects. Both frustums
can be created from a camera object.

```c++
std::shared_ptr<Camera> camera = std::make_shared<Camera>(renderer, {800, 600});

Frustum2d frustum_2d = camera->get_frustum_2d();
Frustum3d frustum_3d = camera->get_frustum_3d();
```
`Frustum2d` and `Frustum3d` provide a method `is_visible(..)` that can be used to check if an object is
located inside the frustum. The required inputs differ depending on the frustum type. For 3D frustums,
only the 3D scene position is required:
```c++
bool is_visible = frustum_3d.is_visible({0.f, 0.f, 0.f});
```

For 2D frustums, in addition to the 3D scene position, a model matrix, the animation's scalefactor
as well as the bounding box of the animation must be provided.

```c++
bool is_visible = frustum_2d.is_visible(
{0.f, 0.f, 0.f},
model_matrix, // the model matrix of the animation
scalefactor, // how much the animation is scaled
{10, 20, 50, 10} // max distance from the center to the edges of the bounding box
);
```
12 changes: 6 additions & 6 deletions libopenage/renderer/camera/frustum_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void Frustum2d::update(const util::Vector2s &viewport_size,
const Eigen::Matrix4f &view_matrix,
const Eigen::Matrix4f &projection_matrix,
const float zoom) {
this->inv_viewport_size = {1.0f / viewport_size[0], 1.0f / viewport_size[1]};
this->pixel_size_ndc = {2.0f / viewport_size[0], 2.0f / viewport_size[1]};
this->inv_zoom_factor = 1.0f / zoom;

// calculate the transformation matrix
Expand All @@ -34,11 +34,11 @@ bool Frustum2d::in_frustum(const Eigen::Vector3f &scene_pos,

float zoom_scale = scalefactor * this->inv_zoom_factor;

// Scale the boundaries by the zoom factor and the viewport size
float left_bound = boundaries[0] * zoom_scale * this->inv_viewport_size[0];
float right_bound = boundaries[1] * zoom_scale * this->inv_viewport_size[0];
float top_bound = boundaries[2] * zoom_scale * this->inv_viewport_size[1];
float bottom_bound = boundaries[3] * zoom_scale * this->inv_viewport_size[1];
// Scale the boundaries by the zoom factor and the pixel size
float left_bound = boundaries[0] * zoom_scale * this->pixel_size_ndc[0];
float right_bound = boundaries[1] * zoom_scale * this->pixel_size_ndc[0];
float top_bound = boundaries[2] * zoom_scale * this->pixel_size_ndc[1];
float bottom_bound = boundaries[3] * zoom_scale * this->pixel_size_ndc[1];

// check if the object boundaries are inside the frustum
if (x_ndc - left_bound >= 1.0f) {
Expand Down
6 changes: 4 additions & 2 deletions libopenage/renderer/camera/frustum_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ class Frustum2d {
Eigen::Matrix4f transform_matrix;

/**
* Viewport size of the camera (width x height).
* Size of a pixel (width x height) in clip space.
*
* Uses normalized device coordinates (NDC) for the pixel size.
*/
Eigen::Vector2f inv_viewport_size;
Eigen::Vector2f pixel_size_ndc;

/**
* Zoom factor of the camera.
Expand Down
Loading

0 comments on commit d66e9f9

Please sign in to comment.