diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6b27b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# CMake build directory +build/ + +# CMake generated files +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +Makefile +*.cmake + +# Executables +*.out +*.exe +black_hole_3d +black_hole_2d +cpu_geodesic + +# Object files +*.o +*.obj +*.o.d + +# IDE-specific files +.vscode/ + +# Temporary files +*~ +*.swp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5f7725b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,70 @@ +# Set the minimum required version of CMake and define the project +cmake_minimum_required(VERSION 3.10) +project(BlackHoleSim) + +# Set the C++ standard to C++17 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Set the OpenGL policy to prefer modern GLVND +cmake_policy(SET CMP0072 NEW) + +# Find required packages +find_package(OpenGL REQUIRED) +find_package(GLEW REQUIRED) +find_package(glfw3 3.3 REQUIRED) +find_package(glm REQUIRED) + +# --- Define the main 3D simulation executable --- +add_executable(black_hole_3d src/black_hole.cpp) + +# Link libraries to the 3D simulation +target_link_libraries(black_hole_3d PRIVATE + GLEW::GLEW + glfw + OpenGL::GL + glm::glm +) + +# --- Define the 2D lensing executable --- +add_executable(black_hole_2d src/2D_lensing.cpp) +target_link_libraries(black_hole_2d PRIVATE + GLEW::GLEW + glfw + OpenGL::GL + glm::glm +) + +# --- Define the CPU geodesic executable --- +add_executable(cpu_geodesic src/CPU-geodesic.cpp) +target_link_libraries(cpu_geodesic PRIVATE + GLEW::GLEW + glfw + OpenGL::GL + glm::glm +) + +# --- Copy shader files to the build directory --- +# This ensures the executable can find them at runtime. +add_custom_command( + TARGET black_hole_3d POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/src/shaders/geodesic.comp + ${CMAKE_CURRENT_SOURCE_DIR}/src/shaders/grid.vert + ${CMAKE_CURRENT_SOURCE_DIR}/src/shaders/grid.frag + # The destination must be the LAST argument + $ +) + +# --- Installation --- +# This section is optional but good practice. +install(TARGETS black_hole_3d black_hole_2d cpu_geodesic + RUNTIME DESTINATION bin) + +set(SHADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/src/shaders/geodesic.comp + ${CMAKE_CURRENT_SOURCE_DIR}/src/shaders/grid.vert + ${CMAKE_CURRENT_SOURCE_DIR}/src/shaders/grid.frag +) +install(FILES ${SHADER_FILES} + DESTINATION bin) diff --git a/Gravity_Sim.zip b/Gravity_Sim.zip deleted file mode 100644 index 098c42d..0000000 Binary files a/Gravity_Sim.zip and /dev/null differ diff --git a/README.md b/README.md index aae4faf..678ef7f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,86 @@ +# Black Hole Simulation + +This project is a C++ and OpenGL-based simulation of a black hole. It visually demonstrates gravitational lensing, spacetime curvature, and accretion disks. The simulation is accelerated using GPU compute shaders for real-time performance. + +## Features + +* **3D Black Hole Simulation**: A real-time 3D simulation of a black hole with a compute shader for calculating geodesics. +* **2D Gravitational Lensing**: A 2D demonstration of how a black hole bends light. +* **CPU-based Geodesic Calculation**: A CPU implementation for calculating null geodesics. +* **Spacetime Grid Visualization**: Visualizes the curvature of spacetime around the black hole. + +## Getting Started + +### Prerequisites + +Here's the requirements btw: +* A C++17 compatible compiler (like GCC or Clang) +* CMake (version 3.10 or higher) +* OpenGL +* The following libraries: + * GLEW (The OpenGL Extension Wrangler Library) + * GLFW (A multi-platform library for OpenGL, OpenGL ES and Vulkan) + * GLM (OpenGL Mathematics) + +On many Linux distributions, you can install these with your package manager. For example, on Ubuntu (debian based distross): +```bash +sudo apt-get update +sudo apt-get install build-essential cmake libglew-dev libglfw3-dev libglm-dev +``` + +### Building the Project + +1. Clone the repository: + ```bash + git clone + cd black_hole + ``` +2. Create a build directory and navigate into it: + ```bash + mkdir build + cd build + ``` +3. Run CMake to configure the project: + ```bash + cmake .. + ``` +4. Compile the project using Make: + ```bash + make + ``` +This will create three executables in the `build` directory: `black_hole_3d`, `black_hole_2d`, and `cpu_geodesic`. + +## Usage + +After building the project, you can run the simulations from the `build` directory. + +* **3D Simulation**: `./black_hole_3d` +* **2D Lensing Demo**: `./black_hole_2d` +* **CPU Geodesic Demo**: `./cpu_geodesic` +(the Geodesic and 2D lensing does not work somehow) + +### 3D Simulation Controls + +* **Orbit**: Click and drag the left mouse button to orbit the camera around the black hole. +* **Pan**: Click and drag the right mouse button to pan the camera. +* **Zoom**: Use the mouse scroll wheel to zoom in and out. +* **Toggle Gravity info**: Press the `G` key to print velocity data about gravity. + +## How it Works + +The simulation uses different techniques to visualize the black hole. + +* **2D Lensing (`black_hole_2d`)**: This is a simplified 2D simulation that shows how light rays are bent by the black hole's gravity. It uses a 4th-order Runge-Kutta (RK4) integrator to solve the geodesic equations. + +* **3D Simulation (`black_hole_3d`)**: This is the main simulation. It uses a compute shader (`geodesic.comp`) to trace a large number of light rays in parallel on the GPU. This allows for real-time rendering of gravitational lensing effects in 3D. The camera and object data are passed to the GPU using Uniform Buffer Objects (UBOs). + +* **CPU Geodesic (`cpu_geodesic`)**: This provides a CPU-based implementation for calculating the paths of light rays (null geodesics) in 3D space around the black hole. It serves as a reference and educational tool for understanding the underlying physics calculations. + +--- +*Original README* + +--- + # black_hole Black hole simulation project Here is the black hole raw code, everything will be inside a src bin incase you want to copy the files diff --git a/black_hole.exe b/black_hole.exe deleted file mode 100644 index 5d3325c..0000000 Binary files a/black_hole.exe and /dev/null differ diff --git a/ray_tracing.cpp b/ray_tracing.cpp deleted file mode 100644 index 4479c30..0000000 --- a/ray_tracing.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -using namespace glm; - -// global vars -const int WIDTH = 800; -const int HEIGHT = 600; - -// functions - -// structures and classes :D -class Engine{ -public: - // -- Quad & Texture render - GLFWwindow* window; - GLuint quadVAO; - GLuint texture; - GLuint shaderProgram; - - Engine(){ - this->window = StartGLFW(); - this->shaderProgram = CreateShaderProgram(); - - auto result = QuadVAO(); - this->quadVAO = result[0]; - this->texture = result[1]; - } - GLFWwindow* StartGLFW(){ - if(!glfwInit()){ - std::cerr<<"glfw failed init, PANIC PANIC!"< QuadVAO(){ - float quadVertices[] = { - // positions // texCoords - -1.0f, 1.0f, 0.0f, 1.0f, // top left - -1.0f, -1.0f, 0.0f, 0.0f, // bottom left - 1.0f, -1.0f, 1.0f, 0.0f, // bottom right - - -1.0f, 1.0f, 0.0f, 1.0f, // top left - 1.0f, -1.0f, 1.0f, 0.0f, // bottom right - 1.0f, 1.0f, 1.0f, 1.0f // top right - - }; - - GLuint VAO, VBO; - glGenVertexArrays(1, &VAO); - glGenBuffers(1, &VBO); - - glBindVertexArray(VAO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); - - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); - glEnableVertexAttribArray(1); - - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - std::vector VAOtexture = {VAO, texture}; - return VAOtexture; - } - void renderScene(std::vector pixels) { - // update texture w/ ray-tracing results - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, - GL_UNSIGNED_BYTE, pixels.data()); - - // clear screen and draw textured quad - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glUseProgram(shaderProgram); - - GLint textureLocation = glGetUniformLocation(shaderProgram, "screenTexture"); - glUniform1i(textureLocation, 0); - - glBindVertexArray(quadVAO); - glDrawArrays(GL_TRIANGLES, 0, 6); - - glfwSwapBuffers(window); - glfwPollEvents(); - }; -}; - -struct Ray{ - vec3 direction; - vec3 origin; - Ray(vec3 o, vec3 d) : origin(o), direction(normalize(d)){} -}; -struct Material{ - vec3 color; - float specular; - float emission; - Material(vec3 c, float s, float e) : color(c), specular(s), emission(e) {} -}; -struct Object{ - vec3 centre; - float radius; - Material material; - - Object(vec3 c, float r, Material m) : centre(c), radius(r), material(m) {} - - bool Intersect(Ray &ray, float &t){ - vec3 oc = ray.origin - centre; - float a = glm::dot(ray.direction, ray.direction); // ray direction scale by t - float b = 2.0f * glm::dot(oc, ray.direction); // - float c = glm::dot(oc, oc) - radius * radius; // adjustment by sphere radius - double discriminant = b*b - 4*a*c; - if(discriminant < 0){return false;} // no intersection with sphere - - float intercept = (-b - sqrt(discriminant)) / (2.0f*a); - if(intercept < 0){ - intercept = (-b + sqrt(discriminant)) / (2.0f*a); - if(intercept<0){return false;} // intersection is behind origin - } - t = intercept; - return true; - }; - - vec3 getNormal(vec3 &point) const{ - return normalize(point - centre); - } -}; - -class Scene { -public: - std::vector objs; - vec3 lightPos; - Scene() : lightPos(5.0f, 5.0f, 5.0f) {} - - vec3 trace(Ray &ray){ - float closest = INFINITY; - const Object* hitObj = nullptr; - - for(auto& obj : objs){ - float t; // distance to intersection - if(obj.Intersect(ray, t)){ - if(t < closest) { - closest = t; - hitObj = &obj; - } - } - }; - if(hitObj){ - vec3 hitPoint = ray.origin + ray.direction * closest; // point on obj hit by ray - vec3 normal = hitObj->getNormal(hitPoint); - vec3 lightDir = normalize(lightPos - hitPoint); // direction light to hitpoint - - float diff = std::max(glm::dot(normal, lightDir), 0.0f); // diffuse lighting - - Ray shadowRay(hitPoint + normal * 0.001f, lightDir); // slightly up to avoid errors ;P - // check if is in shadow - bool inShadow = false; - - // Actually check for shadows by testing if any object blocks light - for(auto& obj : objs) { - float t; - if(obj.Intersect(shadowRay, t)) { - inShadow = true; - break; - } - } - - vec3 color = hitObj->material.color; - float ambient = 0.1f; // minimum light level - - if (inShadow) { - return color * ambient; - } - - return color * (ambient + diff * 0.9f); - } - - return vec3(0.0f, 0.0f, 0.1f); - } -}; - - -// --- main loop ---- // -int main(){ - Engine engine; - Scene scene; - - scene.objs = { - Object(vec3(0.0f, 0.0f, -5.0f), 2.0f, Material(vec3(1.0f, 0.2f, 0.2f), 0.5f, 0.0f)), // Moved further back and made bigger - Object(vec3(3.0f, 0.0f, -7.0f), 1.5f, Material(vec3(0.2f, 1.0f, 0.2f), 0.5f, 0.0f)) // Adjusted position and size - }; - // -- loop -- // - std::vector pixels(WIDTH * HEIGHT * 3); - while(!glfwWindowShouldClose(engine.window)){ - glClear(GL_COLOR_BUFFER_BIT); - - // render texture (pxl by pxl) - for(int y = 0; y < HEIGHT; ++y){ - for(int x = 0; x < WIDTH; ++x){ - float aspectRatio = float(WIDTH) / float(HEIGHT); - float u = float(x) / float(WIDTH); - float v = float(y) / float(HEIGHT); - - // direction of ray threw camera - vec3 direction( - (2.0f * u - 1.0f) * aspectRatio, - -(2.0f * v - 1.0f), // Flipped to correct orientation - -1.0f // Forward direction (negative z) - ); - Ray ray(vec3(0.0f, 0.0f, 0.0f), normalize(direction)); - vec3 color = scene.trace(ray); - - int index = (y * WIDTH + x) * 3; - pixels[index + 0] = static_cast(color.r * 255); - pixels[index + 1] = static_cast(color.g * 255); - pixels[index + 2] = static_cast(color.b * 255); - } - } - - engine.renderScene(pixels); - } - - glfwTerminate(); -} - - -// func dec's - - - - - diff --git a/2D_lensing.cpp b/src/2D_lensing.cpp similarity index 100% rename from 2D_lensing.cpp rename to src/2D_lensing.cpp diff --git a/CPU-geodesic.cpp b/src/CPU-geodesic.cpp similarity index 100% rename from CPU-geodesic.cpp rename to src/CPU-geodesic.cpp diff --git a/black_hole.cpp b/src/black_hole.cpp similarity index 97% rename from black_hole.cpp rename to src/black_hole.cpp index 8999365..4ec4d20 100644 --- a/black_hole.cpp +++ b/src/black_hole.cpp @@ -707,4 +707,4 @@ int main() { glfwDestroyWindow(engine.window); glfwTerminate(); return 0; -} +} \ No newline at end of file diff --git a/geodesic.comp b/src/shaders/geodesic.comp similarity index 100% rename from geodesic.comp rename to src/shaders/geodesic.comp diff --git a/src/shaders/grid.frag b/src/shaders/grid.frag new file mode 100644 index 0000000..54cdcf0 --- /dev/null +++ b/src/shaders/grid.frag @@ -0,0 +1,8 @@ +#version 330 core +out vec4 FragColor; + +void main() +{ + // A semi-transparent white is a good default for a grid + FragColor = vec4(1.0, 1.0, 1.0, 0.5); +} diff --git a/src/shaders/grid.vert b/src/shaders/grid.vert new file mode 100644 index 0000000..463235a --- /dev/null +++ b/src/shaders/grid.vert @@ -0,0 +1,9 @@ +#version 330 core +layout (location = 0) in vec3 aPos; + +uniform mat4 viewProj; + +void main() +{ + gl_Position = viewProj * vec4(aPos, 1.0); +} diff --git a/vs_code/c_cpp_properties.json b/vs_code/c_cpp_properties.json deleted file mode 100644 index 13ff85a..0000000 --- a/vs_code/c_cpp_properties.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "configurations": [ - { - "name": "Win32", - "includePath": [ - "${workspaceFolder}/**", - "C:/msys64/mingw64/include", - "C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.8/include" - ], - "defines": [], - "compilerPath": "C:/msys64/mingw64/bin/g++.exe", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "windows-gcc-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/vs_code/launch.json b/vs_code/launch.json deleted file mode 100644 index 4d1a665..0000000 --- a/vs_code/launch.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "g++ Debug", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/bin/black_hole.exe", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}/src", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "C:/msys64/mingw64/bin/gdb.exe", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ], - "preLaunchTask": "build" - }, - { - "name": "CUDA Debug", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/bin/black_hole.exe", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}/src", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "C:/msys64/mingw64/bin/gdb.exe", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ], - "preLaunchTask": "build-cuda" - } - ] -} \ No newline at end of file diff --git a/vs_code/settings.json b/vs_code/settings.json deleted file mode 100644 index 145c23d..0000000 --- a/vs_code/settings.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "files.associations": { - "ostream": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "*.tcc": "cpp", - "cctype": "cpp", - "charconv": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "compare": "cpp", - "concepts": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "string": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "exception": "cpp", - "algorithm": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "memory_resource": "cpp", - "numeric": "cpp", - "optional": "cpp", - "random": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "format": "cpp", - "initializer_list": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "new": "cpp", - "numbers": "cpp", - "span": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "text_encoding": "cpp", - "cinttypes": "cpp", - "typeinfo": "cpp", - "variant": "cpp", - "chrono": "cpp", - "ratio": "cpp", - "iomanip": "cpp", - "semaphore": "cpp", - "sstream": "cpp", - "stop_token": "cpp", - "thread": "cpp", - "cstring": "cpp", - "ios": "cpp", - "list": "cpp", - "xfacet": "cpp", - "xhash": "cpp", - "xiosbase": "cpp", - "xlocale": "cpp", - "xlocinfo": "cpp", - "xlocnum": "cpp", - "xmemory": "cpp", - "xstddef": "cpp", - "xstring": "cpp", - "xtr1common": "cpp", - "xutility": "cpp" - } -} \ No newline at end of file diff --git a/vs_code/tasks.json b/vs_code/tasks.json deleted file mode 100644 index b9a6a36..0000000 --- a/vs_code/tasks.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "type": "cppbuild", - "command": "C:/msys64/mingw64/bin/g++.exe", - "args": [ - "-fdiagnostics-color=always", - "-g", - "${workspaceFolder}/src/black_hole.cpp", - "-o", - "${workspaceFolder}/bin/black_hole.exe", - "-IC:/msys64/mingw64/include", - "-LC:/msys64/mingw64/lib", - "-lglfw3", - "-lglew32", - "-lopengl32", - "-lgdi32" - ], - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": [ - "$gcc" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "detail": "compiler: C:/msys64/mingw64/bin/g++.exe" - }, - { - "label": "build-cuda", - "type": "shell", - "command": "\"C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.8/bin/nvcc.exe\"", - "args": [ - "-o", - "${workspaceFolder}/bin/black_hole_cuda.exe", - "${workspaceFolder}/src/black_hole.cu", - "-IC:/msys64/mingw64/include", - "-IC:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.8/include", - "-LC:/msys64/mingw64/lib", - "-LC:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.8/lib/x64", - "-lglfw3", - "-lglew32", - "-lopengl32", - "-lgdi32", - "-lcudart" - ], - "options": { - "cwd": "${workspaceFolder}" - }, - "group": "build" - } - ] -} \ No newline at end of file