mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-04 10:40:09 -06:00
bloom is kinda working, need to implement a better technique!
This commit is contained in:
8
.claude/settings.local.json
Normal file
8
.claude/settings.local.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"permissions": {
|
||||
"defaultMode": "acceptEdits",
|
||||
"allow": [
|
||||
"Bash(cmake:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
187
CLAUDE.md
Normal file
187
CLAUDE.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
OpenSpace is an open-source interactive data visualization software designed to visualize the known universe. It's a C++ application using OpenGL 3.3+ with a modular architecture, Lua scripting interface, and support for multiple display environments (flat screens, multi-projector, planetariums).
|
||||
|
||||
## Build System
|
||||
|
||||
### Core Build Tools
|
||||
- **CMake 3.25+** is the primary build system
|
||||
- **C++20/C++23** compiler required (Visual Studio 2022 17.11+, GCC13+, Clang17+, AppleClang 15.0.0+)
|
||||
- **Qt** framework required for GUI components
|
||||
|
||||
### Common Build Commands
|
||||
```bash
|
||||
# Configure step (only do this when changes are made to the CMake build system)
|
||||
cmake . --preset windows-min --fresh
|
||||
# Build for RelWithDebInfo configuration always
|
||||
cmake --build build --config RelWithDebInfo -j
|
||||
```
|
||||
|
||||
### Code Generation
|
||||
OpenSpace uses a custom code generation system:
|
||||
- The `codegen-tool` processes modules and src directories
|
||||
- Code generation runs automatically during build via the `run_codegen` target
|
||||
- Generated files have `_codegen.cpp` suffix
|
||||
|
||||
## Code Architecture
|
||||
|
||||
### Core Engine Structure
|
||||
The main engine components are located in `src/`:
|
||||
|
||||
- **`engine/`** - Core engine systems (OpenSpaceEngine, Configuration, ModuleEngine, SyncEngine)
|
||||
- **`rendering/`** - Render engine, renderables, dashboard, screen-space renderables
|
||||
- **`scene/`** - Scene graph, assets, profiles, transformations (rotation, scale, translation)
|
||||
- **`navigation/`** - Camera navigation (OrbitalNavigator, PathNavigator, KeyframeNavigator)
|
||||
- **`interaction/`** - Input handling, action management, key bindings, session recording
|
||||
- **`scripting/`** - Lua script engine and bindings
|
||||
- **`network/`** - Parallel connection and peer systems
|
||||
- **`properties/`** - Property system (scalar, vector, matrix, list properties)
|
||||
|
||||
### Module System
|
||||
OpenSpace has a modular architecture with modules in `modules/`:
|
||||
|
||||
#### Complete Module Reference
|
||||
|
||||
**atmosphere** - Provides realistic atmospheric rendering with Rayleigh and Mie scattering, supporting various atmospheric parameters, shadows, and deferred rendering for planetary bodies.
|
||||
|
||||
**audio** - Manages spatial and non-spatial audio playback using the SoLoud library, supporting 3D positioning, volume control, looping, and listener parameters for immersive sound experiences.
|
||||
|
||||
**base** - Core foundational module that provides essential rendering primitives like spheres, planes, trails, labels, coordinate axes, and screen space elements, along with basic transformations and lighting systems.
|
||||
|
||||
**cefwebgui** - Integrates Chromium Embedded Framework to provide web-based user interfaces, allowing HTML/CSS/JavaScript GUIs to be rendered within the OpenSpace environment.
|
||||
|
||||
**debugging** - Provides debugging tools and visualizations including performance statistics, frame information display, and debug rendering capabilities for development and troubleshooting.
|
||||
|
||||
**digitaluniverse** - Renders large-scale astronomical datasets from the Digital Universe catalog, displaying cosmic structures and phenomena as mesh-based visualizations.
|
||||
|
||||
**exoplanets** - Visualizes exoplanet data including orbital discs, habitable zones, host star properties, and comparison tools for exploring discovered planets beyond our solar system.
|
||||
|
||||
**fieldlines** - Renders magnetic or electric field lines from vector field data, supporting various seeding methods and field line tracing algorithms for scientific visualization.
|
||||
|
||||
**fieldlinessequence** - Extends fieldlines module to handle time-varying field line sequences, allowing animation of changing magnetic field structures over time.
|
||||
|
||||
**fitsfilereader** - Reads and processes FITS (Flexible Image Transport System) files commonly used in astronomy, supporting both image data and tabular data extraction.
|
||||
|
||||
**gaia** - Handles massive star catalogs from the Gaia space mission, using octree spatial structures for efficient rendering of millions of stars with proper filtering and culling.
|
||||
|
||||
**galaxy** - Renders galaxy structures using both volume rendering and point-based methods, supporting Milky Way visualization with stellar distributions and dust lanes.
|
||||
|
||||
**globebrowsing** - Provides interactive 3D globe rendering with multi-resolution tiling, layer management, and WMS server integration for displaying geographic and planetary data.
|
||||
|
||||
**imgui** - Implements immediate-mode GUI using Dear ImGui library, providing various interface components for property editing, scene management, and system controls.
|
||||
|
||||
**iswa** - Integrates with Integrated Space Weather Analysis system to visualize space weather data including solar wind, magnetic fields, and other heliospheric phenomena.
|
||||
|
||||
**kameleon** - Provides wrapper functionality for Kameleon library, enabling access to space physics simulation data and magnetospheric modeling results.
|
||||
|
||||
**kameleonvolume** - Extends kameleon module with volume rendering capabilities, allowing 3D visualization of magnetospheric and ionospheric simulation data.
|
||||
|
||||
**multiresvolume** - Implements multi-resolution volume rendering with adaptive level-of-detail, brick management, and error-based selection for large-scale volumetric datasets.
|
||||
|
||||
**opensoundcontrol** - Enables Open Sound Control (OSC) protocol communication for external device integration and remote control of OpenSpace parameters.
|
||||
|
||||
**postprocessing** - Provides post-processing rendering effects pipeline, allowing application of visual filters and enhancements to the final rendered output.
|
||||
|
||||
**server** - Implements server functionality for network communication, enabling distributed rendering and remote control capabilities.
|
||||
|
||||
**skybrowser** - Provides sky browsing functionality for navigating and exploring celestial coordinates and astronomical object catalogs.
|
||||
|
||||
**space** - Core space-related functionality including orbital mechanics, Kepler elements, ephemeris data handling, and SPICE toolkit integration for spacecraft trajectories.
|
||||
|
||||
**spacecraftinstruments** - Manages spacecraft instrument data visualization, handling coordinate frame transformations and instrument-specific rendering capabilities.
|
||||
|
||||
**spout** - Integrates Spout framework for real-time texture sharing between applications on Windows, enabling external video input/output capabilities.
|
||||
|
||||
**statemachine** - Implements state machine functionality for managing complex application states and transitions during presentations or automated sequences.
|
||||
|
||||
**sync** - Handles data synchronization and repository management for downloading and updating astronomical datasets and resources.
|
||||
|
||||
**telemetry** - Collects and manages telemetry data for monitoring system performance, usage statistics, and operational metrics.
|
||||
|
||||
**touch** - Provides multi-touch input handling and gesture recognition for interactive displays and touch-enabled devices.
|
||||
|
||||
**toyvolume** - Simple volume rendering module for testing and demonstration purposes, providing basic volumetric visualization capabilities.
|
||||
|
||||
**video** - Handles video file playback and streaming for incorporating multimedia content into space visualizations.
|
||||
|
||||
**volume** - Core volume rendering infrastructure providing transfer functions, raw volume data handling, and fundamental volumetric rendering capabilities.
|
||||
|
||||
**webbrowser** - Integrates web browser functionality for displaying web content and online resources within the OpenSpace environment.
|
||||
|
||||
**webgui** - Provides web-based graphical user interface through HTTP server, enabling browser-based control and interaction with OpenSpace.
|
||||
|
||||
#### Module Structure Pattern
|
||||
Each module follows the standard pattern:
|
||||
- `{module}module.h/cpp` - Main module class
|
||||
- `include.cmake` - CMake configuration
|
||||
- `rendering/` - Module-specific renderables
|
||||
- `shaders/` - GLSL shaders
|
||||
|
||||
### Asset System
|
||||
- **Assets** (`.asset` files) define scene content and data sources
|
||||
- **Profiles** (`.profile` files) define complete scenes with assets and settings
|
||||
- **Tasks** (`.task` files) define data processing and conversion operations
|
||||
|
||||
### Shader Architecture
|
||||
- Shaders located in `shaders/` directory and module-specific `shaders/` subdirectories
|
||||
- Uses HGLSL (header GLSL) for shared shader code
|
||||
- PowerScaling system for astronomical distances in `shaders/PowerScaling/`
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Build and run tests (if OPENSPACE_HAVE_TESTS=ON)
|
||||
cmake --build build --target OpenSpaceTest --config RelWithDebInfo
|
||||
./bin/RelWithDebInfo/OpenSpaceTest
|
||||
```
|
||||
|
||||
### Running the Application
|
||||
```bash
|
||||
# From build directory or using built executable
|
||||
./bin/RelWithDebInfo/OpenSpace
|
||||
# Or with specific profile
|
||||
./bin/RelWithDebInfo/OpenSpace --profile data/profiles/default.profile
|
||||
```
|
||||
|
||||
### Module Development
|
||||
When creating or modifying modules:
|
||||
1. Each module must have a `{ModuleName}Module` class inheriting from `OpenSpaceModule`
|
||||
2. Use the code generation system for Lua bindings (`_codegen.cpp` files)
|
||||
3. Follow the existing module structure with CMakeLists.txt and include.cmake
|
||||
4. Register renderables in the module's factory system
|
||||
|
||||
### Scripting
|
||||
- Lua scripts for configuration and interaction
|
||||
- Core scripts in `scripts/` directory
|
||||
- Module-specific scripts in `modules/{module}/scripts/`
|
||||
- Lua bindings auto-generated via codegen system
|
||||
|
||||
## Important Conventions
|
||||
|
||||
### Code Organization
|
||||
- Header files typically use `.h` extension
|
||||
- Implementation files use `.cpp` extension
|
||||
- Generated code files use `_codegen.cpp` suffix
|
||||
- Lua binding files use `_lua.inl` and `_lua_codegen.cpp` suffixes
|
||||
|
||||
### Build Targets
|
||||
- Main application: `OpenSpace`
|
||||
- Code generation: `run_codegen` (runs automatically)
|
||||
- Tests: `OpenSpaceTest` (if enabled)
|
||||
- Module libraries: `openspace-module-{modulename}`
|
||||
|
||||
### Configuration
|
||||
- Main config: `openspace.cfg`
|
||||
- User settings: `settings.json`
|
||||
- Window/display configs in `config/` directory
|
||||
- builds may take 10-15 minutes
|
||||
|
||||
## Rendering
|
||||
- OpenSpace rendering is managed by SGCT: @rendering.md
|
||||
- In the Draw step, `RenderEngine::render()` is called, rendering objects in the scene, such as globes (planets) and stars
|
||||
- In the Draw2D step, `RenderEngine::renderOverlays()` is called. These are UI elements
|
||||
@@ -57,6 +57,17 @@ if (NOT EXISTS "${PROJECT_SOURCE_DIR}/ext/ghoul/CMakeLists.txt")
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Optionally disable the use of vcpkg for Windows builds
|
||||
set(OPENSPACE_DISABLE_VCPKG OFF CACHE BOOL "Disable the use of vcpkg libs for Windows builds")
|
||||
if (WIN32 AND OPENSPACE_DISABLE_VCPKG)
|
||||
# This works for all subprojects due to MSBuild's hierarchical search
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/Directory.Build.Template.props"
|
||||
"${CMAKE_BINARY_DIR}/Directory.Build.props"
|
||||
COPYONLY
|
||||
)
|
||||
endif ()
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER CMake)
|
||||
|
||||
|
||||
51
CMakePresets.json
Normal file
51
CMakePresets.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"version": 4,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 19,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "windows-min",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"warnings": {
|
||||
"dev": false,
|
||||
"deprecated": false,
|
||||
"systemVars": false
|
||||
},
|
||||
"environment": {
|
||||
"VCPKG_ROOT": "",
|
||||
"VCPKG_DEFAULT_TRIPLET": "",
|
||||
"CMAKE_TOOLCHAIN_FILE": ""
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
|
||||
"GHOUL_HAVE_TESTS": "OFF",
|
||||
"OPENSPACE_HAVE_TESTS": "OFF",
|
||||
"OPENSPACE_MODULE_VIDEO": "OFF",
|
||||
"OPENSPACE_MODULE_TOYVOLUME": "OFF",
|
||||
"OPENSPACE_MODULE_AUDIO": "OFF",
|
||||
"OPENSPACE_MODULE_POSTPROCESSING": "OFF",
|
||||
"OPENSPACE_DISABLE_VCPKG": "ON",
|
||||
"SGCT_BUILD_TESTS": "OFF",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
},
|
||||
"hidden": false
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "windows-min",
|
||||
"configurePreset": "windows-min",
|
||||
"configuration": "RelWithDebInfo",
|
||||
"jobs": 20,
|
||||
"nativeToolOptions": [
|
||||
"-consoleLoggerParameters:ForceConsoleColor"
|
||||
],
|
||||
"inheritConfigureEnvironment": true,
|
||||
"hidden": false
|
||||
}
|
||||
]
|
||||
}
|
||||
11
Directory.Build.Template.props
Normal file
11
Directory.Build.Template.props
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This is a template to disable vcpkg libs in output build dirs on windows -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<VcpkgEnabled>false</VcpkgEnabled>
|
||||
<VcpkgAutoLink>false</VcpkgAutoLink>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Ensure this applies to all projects in subdirectories -->
|
||||
<Import Project="$(MSBuildThisFileDirectory)**/*.props" Condition="Exists('$(MSBuildThisFileDirectory)**/*.props')" />
|
||||
</Project>
|
||||
@@ -49,6 +49,7 @@ namespace openspace {
|
||||
class Camera;
|
||||
struct DeferredcastData;
|
||||
struct DeferredcasterTask;
|
||||
class PostprocessingBloom;
|
||||
struct RaycastData;
|
||||
struct RaycasterTask;
|
||||
class Scene;
|
||||
@@ -161,6 +162,14 @@ public:
|
||||
void enableFXAA(bool enable);
|
||||
void setDisableHDR(bool disable);
|
||||
|
||||
PostprocessingBloom* bloomEffect() const { return _bloomEffect.get(); }
|
||||
|
||||
/**
|
||||
* Apply Tone Mapping Operator on the current framebuffer content for composite frame processing.
|
||||
* This should be called after bloom but before GUI overlays.
|
||||
*/
|
||||
void applyTMOComposite(float blackoutFactor);
|
||||
|
||||
void update();
|
||||
void performRaycasterTasks(const std::vector<RaycasterTask>& tasks,
|
||||
const glm::ivec4& viewport);
|
||||
@@ -208,6 +217,8 @@ private:
|
||||
std::unique_ptr<ghoul::opengl::ProgramObject> _fxaaProgram;
|
||||
std::unique_ptr<ghoul::opengl::ProgramObject> _downscaledVolumeProgram;
|
||||
|
||||
std::unique_ptr<PostprocessingBloom> _bloomEffect;
|
||||
|
||||
UniformCache(hdrFeedingTexture, blackoutFactor, hdrExposure, gamma,
|
||||
Hue, Saturation, Value, Viewport, Resolution) _hdrUniformCache;
|
||||
UniformCache(renderedTexture, inverseScreenSize, Viewport,
|
||||
|
||||
@@ -91,6 +91,8 @@ public:
|
||||
|
||||
void renderOverlays(const ShutdownInformation& shutdownInfo);
|
||||
void renderEndscreen();
|
||||
void applyBloomEffect();
|
||||
void applyTMOEffect(float blackoutFactor);
|
||||
void postDraw();
|
||||
|
||||
float hdrExposure() const;
|
||||
@@ -161,13 +163,14 @@ public:
|
||||
|
||||
uint64_t frameNumber() const;
|
||||
|
||||
float combinedBlackoutFactor() const;
|
||||
|
||||
private:
|
||||
void renderScreenLog();
|
||||
void renderVersionInformation();
|
||||
void renderCameraInformation();
|
||||
void renderShutdownInformation(float timer, float fullTime);
|
||||
void renderDashboard() const;
|
||||
float combinedBlackoutFactor() const;
|
||||
|
||||
Camera* _camera = nullptr;
|
||||
Scene* _scene = nullptr;
|
||||
@@ -196,6 +199,12 @@ private:
|
||||
|
||||
properties::BoolProperty _enableFXAA;
|
||||
|
||||
properties::BoolProperty _bloomEnabled;
|
||||
properties::FloatProperty _bloomThreshold;
|
||||
properties::IntProperty _bloomBlurPasses;
|
||||
properties::FloatProperty _bloomBlurMagnitude;
|
||||
properties::FloatProperty _bloomIntensity;
|
||||
|
||||
properties::BoolProperty _disableHDRPipeline;
|
||||
properties::FloatProperty _hdrExposure;
|
||||
properties::FloatProperty _gamma;
|
||||
|
||||
28
renderdoc_config.cap
Normal file
28
renderdoc_config.cap
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"rdocCaptureSettings": 1,
|
||||
"settings": {
|
||||
"autoStart": false,
|
||||
"commandLine": "-c config/single_fisheye.json -b",
|
||||
"environment": [
|
||||
],
|
||||
"executable": "C:\\OpenSpace\\OpenSpace.worktrees\\feature\\postprocessing-effects\\bin\\RelWithDebInfo\\OpenSpace.exe",
|
||||
"inject": false,
|
||||
"numQueuedFrames": 0,
|
||||
"options": {
|
||||
"allowFullscreen": true,
|
||||
"allowVSync": true,
|
||||
"apiValidation": false,
|
||||
"captureAllCmdLists": false,
|
||||
"captureCallstacks": false,
|
||||
"captureCallstacksOnlyDraws": false,
|
||||
"debugOutputMute": true,
|
||||
"delayForDebugger": 0,
|
||||
"hookIntoChildren": false,
|
||||
"refAllResources": false,
|
||||
"softMemoryLimit": 0,
|
||||
"verifyBufferAccess": false
|
||||
},
|
||||
"queuedFrameCap": 0,
|
||||
"workingDir": "C:\\OpenSpace\\OpenSpace.worktrees\\feature\\postprocessing-effects"
|
||||
}
|
||||
}
|
||||
80
rendering.md
Normal file
80
rendering.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# SGCT Rendering Pipeline
|
||||
## Rendering
|
||||
The central component in SGCT is a singleton class called the `Engine`. Its instance handles all initialization, rendering, network communication, and configuration handling. The application needs to bind callback functions to the engine to customize specific tasks, for example initialization, rendering, and input handling. To start SGCT's render loop, the function `Engine::render()` has to be called and it will only return once the application is ready to be terminated.
|
||||
|
||||
|
||||
### Pre-Window
|
||||
This callback is called by SGCT once before the windows for the node are created and thus before any OpenGL context is created, which means that no OpenGL calls should be made in this callback. However, the configuration has already been read at this point and the network connecting the server and clients has been initialized.
|
||||
|
||||
|
||||
### Init OpenGL
|
||||
This callback is executed once after all windows for the node have been created and everything related to OpenGL in SGCT has been initialized. SGCT creates an OpenGL context for each window and an additional shared OpenGL context. The callback function received a pointer to the shared OpenGL context which can be used by the application to either initialize OpenGL objects or create additional contexts.
|
||||
|
||||
This callback is usually used by applications to load textures, compile shaders, and do other initial setup before the render loop starts.
|
||||
|
||||
|
||||
### Poll Events
|
||||
In the beginning of the render loop, the input devices are polled from the operating system and the registered callbacks are executed. All of these callbacks are based on the GLFW input system, documentation of which can be found [here](https://www.glfw.org/docs/3.0/group__input.html).
|
||||
|
||||
#### Mouse Button
|
||||
This callback is called whenever the stage of a mouse button changes. The arguments to this function are the button that changed, any keyboard modifier that was pressed at the time (`Shift`, `Control`, `Alt`, or `Super`), and the action that caused the callback.
|
||||
|
||||
#### Mouse Position
|
||||
This callback is called whenever the position of the mouse changes. The arguments to the callback are the x and y position in screen coordinates.
|
||||
|
||||
#### Mouse Scroll
|
||||
This callback is executed when the mosition of the mouse's scroll wheel changes. The parameters to this function are the scroll offset along the x and y axes.
|
||||
|
||||
#### Keyboard
|
||||
This callback is called whenever the state of a key on the keyboard changes. The parameters are the key that was changed, any modifiers on the keyboard (`Shift`, `Control`, `Alt`, or `Super`) that were pressed at the same time, the action that triggered the callback (Press, Release, or Repeat), and the system-specific scancode of the key.
|
||||
|
||||
#### Character
|
||||
This callback is called whenever a character was entered using the keyboard. The argument to this callback is the unicode code point of the character. The difference between this and the `Keyboard` callback is that the `Keyboard` responds to physical keys on the keyboard, whereas this callback is based on Unicode codepoints generated by the keys. In general, a physical key press can create any number of Unicode characters.
|
||||
|
||||
#### Drop
|
||||
This callback is executed when the user drops one or more files onto one of the active windows of the application. The parameters are the number of files that were dropped and a list of paths to the individual files.
|
||||
|
||||
|
||||
### Pre Synchronization (PreSync)
|
||||
This callback is called before the synchronization stage during which data is synchronized from the server to the client nodes in the cluster. Shared variables that are set in this callback by the server will be synchronized to the clients and can be read there.
|
||||
|
||||
### Sync
|
||||
This stage distributes the shared data from the server to the clients. The clients wait for the data to be received before the rendering takes place. Two callbacks have to be set for this stage to work; at runtime, SGCT determines which of the callbacks will be called. The data set by the application in the `encode()` callback will be serialized by the server, transferred to the clients via network, deserialized, and its contents are then available in the `decode()` callback. The `shareddata.h` file contains two helper functions `serializeObject` and `deserializeObject` that can be used by the application to serialize its data.
|
||||
|
||||
As the data is transferred as is, it is important to make sure that the data serialized is [POD](https://en.cppreference.com/w/cpp/named_req/PODType), or the behavior might be surprising when decoding the data. Additionally, be aware of serializing any data structure that contains pointers to other parts in memory. Just serializing the pointer value itself is most definitely not what you would want to do as that pointer will be invalid on the client machines. In those cases, it is better to use your own way of referring to objects, for example through the use of identifiers.
|
||||
|
||||
#### Encode
|
||||
This callback is only executed on the server and the application is responsible to return a buffer containing the binary data that should be sent to the clients. This function is called on a separate thread from all other functions.
|
||||
|
||||
#### Decode
|
||||
This callback will be called only on the clients at which time they can use the provided buffer to read the transferred data. This function is called on a separate thread from all other functions.
|
||||
|
||||
|
||||
### Post Synchronization Pre Draw (PostSyncPreDraw)
|
||||
At this stage, the synchronized variables are available and can be used to perform application-specific tasks. These might include, for example, synchronizing random number generator seeds, applying the physics computation performed on the server, react to user input, etc.
|
||||
|
||||
|
||||
### Draw
|
||||
This callback is executed by SGCT multiple times per frame and the application has to render its content in this step. The number of times this callback is executed depends on the configuration that was used to start the application. For example: for each monoscopic, flat viewport, the `draw()` callback is called once, for each stereoscopic, flat viewport the `draw()` callback is called twice, once for the left eye, once for the right eye. For a 180 degree fisheye, the draw callback is called four times, once for each environment map cube face that is used to construct the fisheye.
|
||||
|
||||
In order to render correctly, the application **has** to use the `RenderData` struct that is passed as an argument to the callback. The struct contains information about the active window, the viewport, and frustum mode that the callback is called for. Most importantly, however, the struct also contains the `modelViewProjectionMatrix` that should be used by the application to render its scene. For the normal OpenGL pipeline, the application should treat this matrix as the projection matrix component and it should continue handling its own model and view matrices, depending on the use case.
|
||||
|
||||
|
||||
### Draw 2D
|
||||
This callback is executed after all `Draw` callbacks have been executed. This callback can be used by the application to rendering 2D elements, for example user interfaces. While there is no strict reason to split the `Draw` and `Draw2D` callbacks, the SGCT configuration files allow users to toggle the `Draw3D` and `Draw2D` for individual windows. This makes it easier to, for example, setup a scenario in which a user creates two windows, one window for controlling an application that omits the expensive 3D rendering, and a second window that only contains the 3D rendering but omits the user interface.
|
||||
|
||||
|
||||
### Post Draw
|
||||
This stage is called after the rendering is finished and before SGCT waits for frame lock or V-Sync, if these are enabled. The application can use this callback to trigger computations that can make use of excess CPU cycles that otherwise would be wasted while waiting for V-Sync, if the rendering is finished faster than the target frame rate.
|
||||
|
||||
|
||||
### Frame lock
|
||||
In the case of a clustered environment with more than one node that are **not** connected via Nvidia's swap barriers, this stage of the render loop will synchronize the server and clients via light-weight network packages such that the rendering on all nodes has finished before continuing. If there is only a single node or all nodes are in a swap group, this stage is effectively a null operation.
|
||||
|
||||
|
||||
### Swap Buffers
|
||||
At this point the front and back buffers (or both front and back buffers in the case of active stereo) are swapped. Either because of swap groups or because of the frame locking stage before, these swaps will happen on all machines simulteneously(*-ish* in the second case) to prevent tearing.
|
||||
|
||||
|
||||
### Cleanup
|
||||
This callback is called exactly once after the render loop of SGCT has been terminated by either closing all windows or calling the `Engine::terminate` function. The shared OpenGL context is still avaiable in this callback and should be used by the application to cleanup any remaining objects.
|
||||
@@ -1283,6 +1283,7 @@ void OpenSpaceEngine::render(const glm::mat4& sceneMatrix, const glm::mat4& view
|
||||
void OpenSpaceEngine::drawOverlays() {
|
||||
ZoneScoped;
|
||||
TracyGpuZone("Draw2D");
|
||||
const ghoul::GLDebugGroup group("Overlays");
|
||||
LTRACE("OpenSpaceEngine::drawOverlays(begin)");
|
||||
#ifdef TRACY_ENABLE
|
||||
TracyPlot("RAM", static_cast<int64_t>(ramInUse()));
|
||||
@@ -1291,6 +1292,13 @@ void OpenSpaceEngine::drawOverlays() {
|
||||
|
||||
viewportChanged();
|
||||
|
||||
// Apply bloom effect on the stitched / composited image
|
||||
// global::renderEngine->applyBloomEffect();
|
||||
|
||||
// Apply TMO on the composite frame after bloom
|
||||
// global::renderEngine->applyTMOEffect(global::renderEngine->combinedBlackoutFactor());
|
||||
|
||||
// Render the overlays on top of the rendered scene
|
||||
const bool isGuiWindow =
|
||||
global::windowDelegate->hasGuiWindow() ?
|
||||
global::windowDelegate->isGuiWindow() :
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <openspace/rendering/framebufferrenderer.h>
|
||||
|
||||
#include <openspace/camera/camera.h>
|
||||
#include <openspace/rendering/postprocess.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/rendering/deferredcaster.h>
|
||||
@@ -338,6 +339,11 @@ void FramebufferRenderer::initialize() {
|
||||
updateDeferredcastData();
|
||||
updateDownscaledVolume();
|
||||
|
||||
// Initialize bloom effect
|
||||
_bloomEffect = std::make_unique<PostprocessingBloom>();
|
||||
_bloomEffect->initialize();
|
||||
_bloomEffect->setResolution(_resolution.x, _resolution.y);
|
||||
|
||||
// Sets back to default FBO
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO);
|
||||
|
||||
@@ -401,6 +407,12 @@ void FramebufferRenderer::deinitialize() {
|
||||
glDeleteBuffers(1, &_vertexPositionBuffer);
|
||||
glDeleteVertexArrays(1, &_screenQuad);
|
||||
|
||||
// Cleanup bloom effect
|
||||
if (_bloomEffect) {
|
||||
_bloomEffect->deinitialize();
|
||||
_bloomEffect.reset();
|
||||
}
|
||||
|
||||
global::raycasterManager->removeListener(*this);
|
||||
global::deferredcasterManager->removeListener(*this);
|
||||
}
|
||||
@@ -454,6 +466,69 @@ void FramebufferRenderer::applyTMO(float blackoutFactor, const glm::ivec4& viewp
|
||||
_hdrFilteringProgram->deactivate();
|
||||
}
|
||||
|
||||
void FramebufferRenderer::applyTMOComposite(float blackoutFactor) {
|
||||
ZoneScoped;
|
||||
TracyGpuZone("applyTMOComposite");
|
||||
|
||||
GLint currentFbo;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFbo);
|
||||
|
||||
// Create temporary texture to capture current framebuffer content
|
||||
static GLuint captureTexture = 0;
|
||||
static glm::ivec2 lastRes = glm::ivec2(0);
|
||||
|
||||
if (captureTexture == 0 || lastRes != _resolution) {
|
||||
if (captureTexture != 0) {
|
||||
glDeleteTextures(1, &captureTexture);
|
||||
}
|
||||
glGenTextures(1, &captureTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, captureTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, _resolution.x, _resolution.y, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
lastRes = _resolution;
|
||||
}
|
||||
|
||||
// Copy current framebuffer to texture
|
||||
glBindTexture(GL_TEXTURE_2D, captureTexture);
|
||||
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, _resolution.x, _resolution.y, 0);
|
||||
|
||||
_hdrFilteringProgram->activate();
|
||||
|
||||
ghoul::opengl::TextureUnit hdrFeedingTextureUnit;
|
||||
hdrFeedingTextureUnit.activate();
|
||||
glBindTexture(GL_TEXTURE_2D, captureTexture);
|
||||
|
||||
_hdrFilteringProgram->setUniform(
|
||||
_hdrUniformCache.hdrFeedingTexture,
|
||||
hdrFeedingTextureUnit
|
||||
);
|
||||
|
||||
_hdrFilteringProgram->setUniform(_hdrUniformCache.blackoutFactor, blackoutFactor);
|
||||
_hdrFilteringProgram->setUniform(_hdrUniformCache.hdrExposure, _hdrExposure);
|
||||
_hdrFilteringProgram->setUniform(_hdrUniformCache.gamma, _gamma);
|
||||
_hdrFilteringProgram->setUniform(_hdrUniformCache.Hue, _hue);
|
||||
_hdrFilteringProgram->setUniform(_hdrUniformCache.Saturation, _saturation);
|
||||
_hdrFilteringProgram->setUniform(_hdrUniformCache.Value, _value);
|
||||
// Use full viewport for composite frame
|
||||
_hdrFilteringProgram->setUniform(_hdrUniformCache.Viewport, glm::vec4(0, 0, _resolution.x, _resolution.y));
|
||||
_hdrFilteringProgram->setUniform(_hdrUniformCache.Resolution, glm::vec2(_resolution));
|
||||
|
||||
glDepthMask(false);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glBindVertexArray(_screenQuad);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glBindVertexArray(0);
|
||||
|
||||
glDepthMask(true);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
_hdrFilteringProgram->deactivate();
|
||||
}
|
||||
|
||||
void FramebufferRenderer::applyFXAA(const glm::ivec4& viewport) {
|
||||
_fxaaProgram->activate();
|
||||
|
||||
@@ -669,6 +744,10 @@ void FramebufferRenderer::update() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_bloomEffect && _bloomEffect->isEnabled()) {
|
||||
_bloomEffect->update();
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferRenderer::updateResolution() {
|
||||
@@ -898,6 +977,11 @@ void FramebufferRenderer::updateResolution() {
|
||||
glObjectLabel(GL_TEXTURE, _exitDepthTexture, -1, "Exit depth");
|
||||
}
|
||||
|
||||
// Update bloom effect resolution
|
||||
if (_bloomEffect) {
|
||||
_bloomEffect->setResolution(_resolution.x, _resolution.y);
|
||||
}
|
||||
|
||||
_dirtyResolution = false;
|
||||
}
|
||||
|
||||
@@ -1138,6 +1222,14 @@ void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFac
|
||||
glDrawBuffers(1, &ColorAttachmentArray[_pingPongIndex]);
|
||||
glEnablei(GL_BLEND, 0);
|
||||
|
||||
// Apply bloom effect before TMO (in HDR color space)
|
||||
if (_bloomEffect && _bloomEffect->isEnabled()) {
|
||||
TracyGpuZone("Apply Bloom");
|
||||
const ghoul::GLDebugGroup group("Apply Bloom");
|
||||
|
||||
_bloomEffect->render(_pingPongBuffers.colorTexture[_pingPongIndex]);
|
||||
}
|
||||
|
||||
{
|
||||
TracyGpuZone("Overlay")
|
||||
const ghoul::GLDebugGroup group("Overlay");
|
||||
|
||||
@@ -229,6 +229,8 @@ void PostprocessingBloom::render(GLuint inputTexture) {
|
||||
|
||||
renderExtract(inputTexture);
|
||||
renderBlur();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo);
|
||||
renderBlend(inputTexture);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo);
|
||||
@@ -305,7 +307,6 @@ void PostprocessingBloom::renderBlur() {
|
||||
}
|
||||
|
||||
void PostprocessingBloom::renderBlend(GLuint inputTexture) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glViewport(0, 0, _width, _height);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
@@ -273,6 +273,45 @@ namespace {
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo BloomEnabledInfo = {
|
||||
"BloomEnabled",
|
||||
"Enable Bloom",
|
||||
"Enable bloom post-processing effect.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo BloomThresholdInfo = {
|
||||
"BloomThreshold",
|
||||
"Bloom Threshold",
|
||||
"Brightness threshold for bloom effect. Only pixels brighter than this value "
|
||||
"will contribute to the bloom.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo BloomBlurPassesInfo = {
|
||||
"BloomBlurPasses",
|
||||
"Bloom Blur Passes",
|
||||
"Number of blur passes for the bloom effect. More passes create a smoother "
|
||||
"bloom but reduce performance.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo BloomBlurMagnitudeInfo = {
|
||||
"BloomBlurMagnitude",
|
||||
"Bloom Blur Magnitude",
|
||||
"Magnitude of the blur effect for bloom. Higher values create a larger bloom "
|
||||
"radius.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo BloomIntensityInfo = {
|
||||
"BloomIntensity",
|
||||
"Bloom Intensity",
|
||||
"Intensity of the bloom effect. Controls how strong the bloom appears in the "
|
||||
"final image.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo EnabledFontColorInfo = {
|
||||
"EnabledFontColor",
|
||||
"Enabled Font Color",
|
||||
@@ -324,6 +363,11 @@ RenderEngine::RenderEngine()
|
||||
, _globalBlackOutFactor(GlobalBlackoutFactorInfo, 1.f, 0.f, 1.f)
|
||||
, _applyBlackoutToMaster(ApplyBlackoutToMasterInfo, true)
|
||||
, _enableFXAA(FXAAInfo, true)
|
||||
, _bloomEnabled(BloomEnabledInfo, true)
|
||||
, _bloomThreshold(BloomThresholdInfo, 0.2f, 0.0f, 10.0f)
|
||||
, _bloomBlurPasses(BloomBlurPassesInfo, 5, 1, 10)
|
||||
, _bloomBlurMagnitude(BloomBlurMagnitudeInfo, 1.0e-4f, 1.0e-8f, 1.0e-2f)
|
||||
, _bloomIntensity(BloomIntensityInfo, 1.0f, 0.0f, 5.0f)
|
||||
, _disableHDRPipeline(DisableHDRPipelineInfo, false)
|
||||
, _hdrExposure(HDRExposureInfo, 3.7f, 0.01f, 10.f)
|
||||
, _gamma(GammaInfo, 0.95f, 0.01f, 5.f)
|
||||
@@ -372,6 +416,41 @@ RenderEngine::RenderEngine()
|
||||
_enableFXAA.onChange([this]() { _renderer.enableFXAA(_enableFXAA); });
|
||||
addProperty(_enableFXAA);
|
||||
|
||||
_bloomEnabled.onChange([this]() {
|
||||
if (_renderer.bloomEffect()) {
|
||||
_renderer.bloomEffect()->setEnabled(_bloomEnabled);
|
||||
}
|
||||
});
|
||||
addProperty(_bloomEnabled);
|
||||
|
||||
_bloomThreshold.onChange([this]() {
|
||||
if (_renderer.bloomEffect()) {
|
||||
_renderer.bloomEffect()->setThreshold(_bloomThreshold);
|
||||
}
|
||||
});
|
||||
addProperty(_bloomThreshold);
|
||||
|
||||
_bloomBlurPasses.onChange([this]() {
|
||||
if (_renderer.bloomEffect()) {
|
||||
_renderer.bloomEffect()->setBlurPasses(_bloomBlurPasses);
|
||||
}
|
||||
});
|
||||
addProperty(_bloomBlurPasses);
|
||||
|
||||
_bloomBlurMagnitude.onChange([this]() {
|
||||
if (_renderer.bloomEffect()) {
|
||||
_renderer.bloomEffect()->setBlurMagnitude(_bloomBlurMagnitude);
|
||||
}
|
||||
});
|
||||
addProperty(_bloomBlurMagnitude);
|
||||
|
||||
_bloomIntensity.onChange([this]() {
|
||||
if (_renderer.bloomEffect()) {
|
||||
_renderer.bloomEffect()->setIntensity(_bloomIntensity);
|
||||
}
|
||||
});
|
||||
addProperty(_bloomIntensity);
|
||||
|
||||
_disableHDRPipeline.onChange([this]() {
|
||||
_renderer.setDisableHDR(_disableHDRPipeline);
|
||||
});
|
||||
@@ -514,6 +593,15 @@ void RenderEngine::initializeGL() {
|
||||
_renderer.setHDRExposure(_hdrExposure);
|
||||
_renderer.initialize();
|
||||
|
||||
// Initialize bloom effect parameters
|
||||
if (_renderer.bloomEffect()) {
|
||||
_renderer.bloomEffect()->setEnabled(_bloomEnabled);
|
||||
_renderer.bloomEffect()->setThreshold(_bloomThreshold);
|
||||
_renderer.bloomEffect()->setBlurPasses(_bloomBlurPasses);
|
||||
_renderer.bloomEffect()->setBlurMagnitude(_bloomBlurMagnitude);
|
||||
_renderer.bloomEffect()->setIntensity(_bloomIntensity);
|
||||
}
|
||||
|
||||
_bloom.initialize();
|
||||
const glm::ivec2 res = renderingResolution();
|
||||
_bloom.setResolution(res.x, res.y);
|
||||
@@ -920,7 +1008,7 @@ float RenderEngine::combinedBlackoutFactor() const {
|
||||
}
|
||||
}
|
||||
|
||||
void RenderEngine::postDraw() {
|
||||
void RenderEngine::applyBloomEffect() {
|
||||
ZoneScoped;
|
||||
|
||||
// Apply bloom postprocessing effect
|
||||
@@ -955,6 +1043,16 @@ void RenderEngine::postDraw() {
|
||||
// Apply bloom effect
|
||||
_bloom.render(captureTexture);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderEngine::applyTMOEffect(float blackoutFactor) {
|
||||
ZoneScoped;
|
||||
|
||||
_renderer.applyTMOComposite(blackoutFactor);
|
||||
}
|
||||
|
||||
void RenderEngine::postDraw() {
|
||||
ZoneScoped;
|
||||
|
||||
++_frameNumber;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user