Files
OpenSpace/modules/blackhole/rendering/renderableblackhole.cpp
Wilhelm Björkström 76cdf4adf3 Accretion disk texture
Co-Authored-By: Emil Wallberg <49481622+EmilWallberg@users.noreply.github.com>
2025-05-09 16:07:22 +02:00

414 lines
15 KiB
C++

#include <modules/blackhole/rendering/renderableblackhole.h>
#include <modules/blackhole/blackholemodule.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/rendering/framebufferrenderer.h>
#include <openspace/util/distanceconstants.h>
#include <openspace/navigation/navigationhandler.h>
#include <openspace/navigation/orbitalnavigator.h>
#include <openspace/rendering/renderengine.h>
#include <ghoul/opengl/framebufferobject.h>
#include <ghoul/opengl/openglstatecache.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/filesystem/filesystem.h>
#include <openspace/util/updatestructures.h>
#include <modules/base/basemodule.h>
#include <filesystem>
#include <vector>
#include <chrono>
#define M_Kerr false
#if M_Kerr
#include <modules/blackhole/cuda/kerr.h>
#else
#include <modules/blackhole/cuda/schwarzschild.h>
#endif
namespace {
constexpr std::string_view _loggerCat = "BlackHoleModule";
constexpr std::string_view ProgramName = "BlackHoleProgram";
constexpr std::array<GLfloat, 24> QuadVtx = {
-1.f, -1.f, 0.f, 0.f,
1.f, 1.f, 1.f, 1.f,
-1.f, 1.f, 0.f, 1.f,
-1.f, -1.f, 0.f, 0.f,
1.f, -1.f, 1.f, 0.f,
1.f, 1.f, 1.f, 1.f
};
constexpr openspace::properties::Property::PropertyInfo SolarMassInfo = {
"SolarMass",
"Solar Mass",
"The mass of the blackhole in solar mass units",
openspace::properties::Property::Visibility::User
};
constexpr openspace::properties::Property::PropertyInfo ColorTextureInfo = {
"ColorMap",
"Color Texture",
"The path to the texture that is used to convert from the magnitude of the star "
"to its color. The texture is used as a one dimensional lookup function.",
openspace::properties::Property::Visibility::AdvancedUser
};
struct [[codegen::Dictionary(RenderableModel)]] Parameters {
std::optional<float> SolarMass;
std::string colorMap;
};
auto lastTime = std::chrono::high_resolution_clock::now();
#include "renderableblackhole_codegen.cpp"
}
namespace openspace {
RenderableBlackHole::RenderableBlackHole(const ghoul::Dictionary& dictionary)
: Renderable(dictionary), _solarMass(SolarMassInfo, 4.297e6f), _colorBVMapTexturePath(ColorTextureInfo) {
//setRenderBin(Renderable::RenderBin::Background);
const Parameters p = codegen::bake<Parameters>(dictionary);
_solarMass = p.SolarMass.value_or(_solarMass);
constexpr float G = 6.67430e-11;
constexpr float c = 2.99792458e8;
constexpr float M = 1.9885e30;
_rs = 2.0f * G * 8.543e36 / (c * c);
setInteractionSphere(_rs);
setBoundingSphere(_rs*20);
_colorBVMapTexturePath = absPath(p.colorMap).string();
}
RenderableBlackHole::~RenderableBlackHole() {}
void RenderableBlackHole::initialize() {
_blackHoleWarpTable.reserve(_rayCountHighRes * _rayCountHighRes * 4);
}
void RenderableBlackHole::initializeGL() {
const glm::vec2 screenSize = glm::vec2(global::renderEngine->renderingResolution());
ZoneScoped;
setupQuad();
setupShaders();
loadEnvironmentTexture();
_viewport.updateViewGrid(screenSize);
}
void RenderableBlackHole::deinitializeGL() {
_warpTableTex = nullptr;
_environmentTexture = nullptr;
_viewport.viewGrid = nullptr;
BaseModule::ProgramObjectManager.release(_program);
_program = nullptr;
glDeleteBuffers(1, &_quadVbo);
glDeleteVertexArrays(1, &_quadVao);
}
bool RenderableBlackHole::isReady() const {
return _program != nullptr;
}
bool highres = false;
void RenderableBlackHole::update(const UpdateData& data) {
if (data.modelTransform.translation != _chachedTranslation) {
_chachedTranslation = data.modelTransform.translation;
_starKDTree.build("${BASE}/sync/http/stars_du/6/stars.speck", _chachedTranslation, { {0, 25 }, {25, 50}, { 50, -1 } });
}
_viewport.updateViewGrid(global::renderEngine->renderingResolution());
#if M_Kerr
// world-space camera
glm::dvec3 camW = global::navigationHandler->camera()->positionVec3();
// 1) Translate into model-centered space
glm::dvec3 v = camW - data.modelTransform.translation;
// 2) Remove the rotation: for an orthonormal matrix, inverse == transpose
glm::dvec3 v_rot = glm::transpose(data.modelTransform.rotation) * v;
// 3) Remove scaling (component-wise)
glm::dvec3 cameraPosition = v_rot / data.modelTransform.scale;
if (glm::distance(cameraPosition, _chacedCameraPos) > 0.01f * _rs) {
//kerr(2e11, 0, 0, _rs, 0.99f, _rayCount, _stepsCount, _blackHoleWarpTable);
float env_scale = glm::distance(cameraPosition, { 0,0,0 }) / (0.99f * _rs);
kerr(cameraPosition.x, cameraPosition.y, cameraPosition.z, _rs, 0.99f, { 3.5f * env_scale, 3.8f * env_scale, 4.0f * env_scale }, _rayCount, _stepsCount, _blackHoleWarpTable);
_chacedCameraPos = cameraPosition;
highres = false;
lastTime = std::chrono::high_resolution_clock::now();
}
else if (!highres){
float env_scale = glm::distance(cameraPosition, { 0,0,0 }) / (0.99f * _rs);
auto currentTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> deltaTime = currentTime - lastTime;
float deltaTimeSeconds = deltaTime.count();
if (deltaTimeSeconds > 1.0f) {
kerr(cameraPosition.x, cameraPosition.y, cameraPosition.z, _rs, 0.99f, { 3.5f * env_scale, 3.8f * env_scale, 4.0f * env_scale }, _rayCountHighRes, _stepsCount, _blackHoleWarpTable);
highres = true;
}
}
#else
glm::dvec3 cameraPosition = global::navigationHandler->camera()->positionVec3();
float distanceToAnchor = static_cast<float>(glm::distance(cameraPosition, _chachedTranslation));
if (abs(_rCamera * _rs - distanceToAnchor) > _rs * 0.1) {
_rCamera = distanceToAnchor / _rs;
schwarzschild({ 12.5f / _rs * static_cast<float>(distanceconstants::Parsec), 37.5f / _rs * static_cast<float>(distanceconstants::Parsec), 40.f / _rs * static_cast<float>(distanceconstants::Parsec)}, _rayCount, _stepsCount, _rCamera, _stepLength, _blackHoleWarpTable);
}
#endif
bindSSBOData(_program, "ssbo_warp_table", _ssboBlackHoleDataBinding, _ssboBlackHoleWarpTable);
bindSSBOData(_program, "ssbo_star_map_start_indices", _ssboStarIndicesDataBinding, _ssboStarKDTreeIndices);
bindSSBOData(_program, "ssbo_star_map", _ssboStarDataBinding, _ssboStarKDTree);
}
void RenderableBlackHole::render(const RenderData& renderData, RendererTasks&) {
_program->activate();
bindFramebuffer();
glDisable(GL_DEPTH_TEST);
ghoul::opengl::TextureUnit enviromentUnit;
if (!bindTexture(_uniformCache.environmentTexture, enviromentUnit, _environmentTexture)) {
LWARNING("UniformCache is missing 'environmentTexture'");
}
ghoul::opengl::TextureUnit viewGridUnit;
if (!bindTexture(_uniformCache.viewGrid, viewGridUnit, _viewport.viewGrid)) {
LWARNING("UniformCache is missing 'viewGrid'");
}
ghoul::opengl::TextureUnit colorBVMapUnit;
if (!bindTexture(_uniformCache.colorBVMap, colorBVMapUnit, _colorBVMapTexture)) {
LWARNING("UniformCache is missing 'colorBVMap'");
}
#ifdef M_Kerr
ghoul::opengl::TextureUnit accretionDiskUnit;
if (!bindTexture(_uniformCache.accretionDisk, accretionDiskUnit, _accretionDiskTexture)) {
LWARNING("UniformCache is missing 'accretionDisk'");
}
#endif // M_Kerr
SendSchwarzschildTableToShader();
SendStarKDTreeToShader();
interaction::OrbitalNavigator::CameraRotationDecomposition camRot = global::navigationHandler->orbitalNavigator().decomposeCameraRotationSurface(
CameraPose{renderData.camera.positionVec3(), renderData.camera.rotationQuaternion()},
*parent()
);
// Calculate the camera planes rotation to make sure fisheye works correcly (dcm in sgct projection.cpp)
glm::mat4 invViewPlaneTranslationMatrix = glm::translate(
glm::mat4(1.f), glm::vec3(static_cast<float>(renderData.camera.eyePositionVec3().x))
);
glm::mat4 viewMatrix = renderData.camera.viewMatrix();
glm::mat4 const CameraPlaneRotation = glm::inverse(viewMatrix * invViewPlaneTranslationMatrix);
_program->setUniform(
_uniformCache.cameraRotationMatrix,
glm::mat4(glm::mat4_cast(camRot.localRotation)) * CameraPlaneRotation
);
#if !M_Kerr
_program->setUniform(
_uniformCache.worldRotationMatrix,
glm::mat4(glm::mat4_cast(camRot.globalRotation))
);
if (_uniformCache.r_0 != -1) {
_program->setUniform(
_uniformCache.r_0,
_rCamera
);
}
#endif
drawQuad();
glEnable(GL_DEPTH_TEST);
_program->deactivate();
glBindTexture(GL_TEXTURE_2D, 0);
}
void RenderableBlackHole::SendSchwarzschildTableToShader()
{
glBindBuffer(GL_SHADER_STORAGE_BUFFER, _ssboBlackHoleWarpTable);
const size_t indexBufferSize = _blackHoleWarpTable.size() * sizeof(float);
glBufferData(
GL_SHADER_STORAGE_BUFFER,
indexBufferSize,
_blackHoleWarpTable.data(),
GL_STREAM_DRAW
);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
void RenderableBlackHole::SendStarKDTreeToShader()
{
glBindBuffer(GL_SHADER_STORAGE_BUFFER, _ssboStarKDTree);
size_t indexBufferSize = _starKDTree.mapsSize() * sizeof(float);
glBufferData(
GL_SHADER_STORAGE_BUFFER,
indexBufferSize,
_starKDTree.mapsData(),
GL_STREAM_DRAW
);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, _ssboStarKDTreeIndices);
indexBufferSize = _starKDTree.indicesSize() * sizeof(int);
glBufferData(
GL_SHADER_STORAGE_BUFFER,
indexBufferSize,
_starKDTree.indicesData(),
GL_STREAM_DRAW
);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
void RenderableBlackHole::setupShaders() {
#if M_Kerr
_program = BaseModule::ProgramObjectManager.request(
"KerrBlackHoleProgram",
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
return global::renderEngine->buildRenderProgram(
"KerrBlackHoleProgram",
absPath("${MODULE_BLACKHOLE}/shaders/kerr_vs.glsl"),
absPath("${MODULE_BLACKHOLE}/shaders/kerr_fs.glsl")
);
}
);
ghoul::opengl::updateUniformLocations(*_program, _uniformCache);
#else
_program = BaseModule::ProgramObjectManager.request(
"BlackHoleProgram",
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
return global::renderEngine->buildRenderProgram(
"BlackHoleProgram",
absPath("${MODULE_BLACKHOLE}/shaders/schwarzschild_vs.glsl"),
absPath("${MODULE_BLACKHOLE}/shaders/schwarzschild_fs.glsl")
);
}
);
ghoul::opengl::updateUniformLocations(*_program, _uniformCache);
#endif
}
void RenderableBlackHole::setupQuad() {
glGenVertexArrays(1, &_quadVao);
glBindVertexArray(_quadVao);
glGenBuffers(1, &_quadVbo);
glBindBuffer(GL_ARRAY_BUFFER, _quadVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(QuadVtx), QuadVtx.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), reinterpret_cast<void*>(2 * sizeof(GLfloat)));
}
void RenderableBlackHole::loadEnvironmentTexture() {
//const std::string texturePath = "${MODULE_BLACKHOLE}/rendering/uv.png";
const std::string texturePath = "${BASE}/sync/http/milkyway_textures/2/DarkUniverse_mellinger_8k.jpg";
//const std::string texturePath = "${MODULE_BLACKHOLE}/rendering/skybox.jpg";
_environmentTexture = ghoul::io::TextureReader::ref().loadTexture(absPath(texturePath), 2);
if (_environmentTexture) {
_environmentTexture->uploadTexture();
}
else {
LWARNING(std::format("Failed to load environment texture from path '{}'", absPath(texturePath).string()));
}
_colorBVMapTexture = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorBVMapTexturePath), 1);
if (_colorBVMapTexture) {
_colorBVMapTexture->uploadTexture();
}
else {
LWARNING(std::format("Failed to load environment texture from path '{}'", absPath(_colorBVMapTexturePath).string()));
}
#if M_Kerr
_accretionDiskTexture = ghoul::io::TextureReader::ref().loadTexture(absPath("${MODULE_BLACKHOLE}/rendering/accretion_disk.png"), 1);
if (_accretionDiskTexture) {
_accretionDiskTexture->uploadTexture();
}
#endif
}
void RenderableBlackHole::bindFramebuffer() {
const GLint defaultFBO = ghoul::opengl::FramebufferObject::getActiveObject();
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
}
bool RenderableBlackHole::bindTexture(GLint chacheRegistry, ghoul::opengl::TextureUnit& textureUnit, std::unique_ptr<ghoul::opengl::Texture>& texture) {
if (!texture) return false;
textureUnit.activate();
texture->bind();
_program->setUniform(chacheRegistry, textureUnit);
return true;
}
void RenderableBlackHole::drawQuad() {
glBindVertexArray(_quadVao);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
void RenderableBlackHole::bindSSBOData(
ghoul::opengl::ProgramObject* program,
const std::string& ssboName,
std::unique_ptr<ghoul::opengl::BufferBinding<ghoul::opengl::bufferbinding::Buffer::ShaderStorage>>& ssboBinding,
GLuint& ssboID
)
{
if (ssboID == 0) {
glGenBuffers(1, &ssboID);
LDEBUG(std::format(
"Generating Data Shader Storage Buffer Object id '{}'", ssboID
));
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboID);
ssboBinding = std::make_unique<ghoul::opengl::BufferBinding<
ghoul::opengl::bufferbinding::Buffer::ShaderStorage>
>();
glBindBufferBase(
GL_SHADER_STORAGE_BUFFER,
ssboBinding->bindingNumber(),
ssboID
);
program->setSsboBinding(ssboName, ssboBinding->bindingNumber());
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
documentation::Documentation RenderableBlackHole::Documentation() {
return documentation::Documentation();
}
} // namespace openspace