diff options
| author | hachem <im@hachem.wtf> | 2025-08-17 00:01:03 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2025-08-17 00:01:03 +0200 |
| commit | d4ed53752cb3d078e0b79444a3e9a6015c6ff679 (patch) | |
| tree | d9f9fa94aea2ad781a534175d81ce2daed40b0c7 /docs | |
| parent | df98427825234b24cfa87ad9f9d8a0d738b3ea13 (diff) | |
[doc]: Add explanation and documentation
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/configuration.md | 335 | ||||
| -rw-r--r-- | docs/mathematical-theory.md | 169 | ||||
| -rw-r--r-- | docs/numerical-methods.md | 50 | ||||
| -rw-r--r-- | docs/ray-tracing-implementation.md | 258 |
4 files changed, 812 insertions, 0 deletions
diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..293a4a6 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,335 @@ +# Configuration +## Configuration Files + +### Settings File Location + +The application uses TOML format for configuration files: + +- **Primary location**: `config/settings.toml` +- **User settings**: Loaded at startup, saved on exit + +### TOML Format + +The configuration uses TOML format: + +```toml +[simulation] +max_steps_static = 15000 +max_steps_moving = 30000 +compute_height = 256 +target_fps = 60 +early_exit_distance = 5e+12 +gravity_enabled = true + +[graphics] +render_api = "OpenGL" +vsync_enabled = true +enable_anti_aliasing = true +show_fps = true +show_performance_metrics = true +show_debug_info = false +selected_theme = "Dark" +``` + +## Simulation Parameters + +### Ray Tracing Settings + +#### Max Steps (Static) +- **Description**: Maximum number of integration steps when camera is stationary +- **Range**: 1,000 - 30,000 +- **Default**: 15,000 +- **Impact**: Higher values = better accuracy, lower performance + +```toml +max_steps_static = 15000 +``` + +#### Max Steps (Moving) +- **Description**: Maximum number of integration steps when camera is moving +- **Range**: 1,000 - 60,000 +- **Default**: 30,000 +- **Impact**: Higher values = smoother motion, lower performance + +```toml +max_steps_moving = 30000 +``` + +#### Early Exit Distance +- **Description**: Distance at which ray marching stops to improve performance +- **Range**: 1×10¹¹ - 1×10¹³ meters +- **Default**: 5×10¹² meters +- **Impact**: Lower values = faster rendering, may miss distant objects + +```toml +early_exit_distance = 5e+12 +``` + +### Performance Settings + +#### Target FPS +- **Description**: Target frame rate for the application +- **Range**: 30 - 120 FPS +- **Default**: 60 FPS +- **Impact**: Higher values = smoother animation, higher CPU usage + +```toml +target_fps = 60 +``` + +#### Compute Height +- **Description**: Resolution of the compute shader (height component) +- **Range**: 64 - 2,048 pixels +- **Default**: 256 pixels +- **Impact**: Higher values = better quality, lower performance + +```toml +compute_height = 256 +``` + +### Physics Settings + +#### Gravity Enabled +- **Description**: Enable/disable gravitational interactions between objects +- **Type**: Boolean +- **Default**: true +- **Impact**: Affects object motion and orbital dynamics + +```toml +gravity_enabled = true +``` + +## Graphics Settings + +### Rendering API + +#### Render API +- **Description**: Graphics API to use for rendering +- **Options**: "OpenGL", "Vulkan" +- **Default**: "OpenGL" +- **Impact**: Affects performance and feature availability + +```toml +render_api = "OpenGL" +``` + +### Display Settings + +#### V-Sync Enabled +- **Description**: Enable vertical synchronization +- **Type**: Boolean +- **Default**: true +- **Impact**: Prevents screen tearing, may limit frame rate + +```toml +vsync_enabled = true +``` + +#### Enable Anti-Aliasing +- **Description**: Enable anti-aliasing for smoother edges +- **Type**: Boolean +- **Default**: true +- **Impact**: Better visual quality, slight performance cost + +```toml +enable_anti_aliasing = true +``` + +### UI Settings + +#### Show FPS +- **Description**: Display frame rate counter +- **Type**: Boolean +- **Default**: true +- **Impact**: Performance monitoring, minimal overhead + +```toml +show_fps = true +``` + +#### Show Performance Metrics +- **Description**: Display detailed performance information +- **Type**: Boolean +- **Default**: true +- **Impact**: Debug information, minimal overhead + +```toml +show_performance_metrics = true +``` + +#### Show Debug Info +- **Description**: Display debug information +- **Type**: Boolean +- **Default**: false +- **Impact**: Development information, may impact performance + +```toml +show_debug_info = false +``` + +#### Selected Theme +- **Description**: UI theme selection +- **Options**: "Dark", "Light" +- **Default**: "Dark" +- **Impact**: Visual appearance only + +```toml +selected_theme = "Dark" +``` + +## Performance Tuning + +### Preset-Performance + +The configuration system allows users to balance quality and performance: + +#### High Quality Settings +```toml +[simulation] +max_steps_static = 30000 +max_steps_moving = 60000 +compute_height = 1024 +early_exit_distance = 1e+13 +``` + +#### Balanced Settings +```toml +[simulation] +max_steps_static = 15000 +max_steps_moving = 30000 +compute_height = 512 +early_exit_distance = 5e+12 +``` + +#### Performance Settings +```toml +[simulation] +max_steps_static = 5000 +max_steps_moving = 10000 +compute_height = 256 +early_exit_distance = 2e+12 +``` + +### Adaptive Performance + +The application automatically adjusts performance based on conditions: + +```glsl +// Reduce steps for distant cameras +float cameraDistance = length(cam.camPos); +if (cameraDistance > 2e12) + maxSteps = maxSteps / 2; +else if (cameraDistance > 1e12) + maxSteps = int(maxSteps * 0.75); + +// Reduce steps for escaping rays +float initialEscapeVelocity = sqrt(2.0 * SagA_rs / ray.r); +if (ray.dr > initialEscapeVelocity * 0.95 && ray.r > SagA_rs * 200.0) + maxSteps = maxSteps / 2; +``` + +## Configuration Interface + +### GUI Configuration + +The application provides a graphical interface for configuration: + +```cpp +void ConfigState::OnImGuiRender() +{ + ImGui::Begin("Configuration"); + + // Simulation settings + ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Simulation"); + ImGui::Separator(); + + ImGui::SliderInt("Max Steps (Static)", &m_MaxStepsStatic, 1000, 30000, "%d"); + ImGui::SliderInt("Max Steps (Moving)", &m_MaxStepsMoving, 1000, 60000, "%d"); + ImGui::SliderFloat("Early Exit Distance", &m_EarlyExitDistance, 1e11f, 1e13f, "%.2e"); + ImGui::SliderInt("Compute Height", &m_ComputeHeight, 64, 2048, "%d px"); + ImGui::SliderInt("Target FPS", &m_TargetFPS, 30, 120, "%d"); + + // Physics settings + ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Physics"); + ImGui::Separator(); + + ImGui::Checkbox("Enable Gravity", &m_GravityEnabled); + + // Graphics settings + ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Graphics"); + ImGui::Separator(); + + ImGui::Checkbox("V-Sync", &m_VSyncEnabled); + ImGui::Checkbox("Anti-Aliasing", &m_AntiAliasingEnabled); + ImGui::Checkbox("Show FPS", &m_ShowFPS); + ImGui::Checkbox("Performance Metrics", &m_ShowPerformanceMetrics); + + ImGui::End(); +} +``` + + +## Default Configurations + +### Preset Configurations + +The application includes several preset configurations: + +#### Ultra Quality +```toml +[simulation] +max_steps_static = 50000 +max_steps_moving = 100000 +compute_height = 2048 +early_exit_distance = 1e+13 +target_fps = 30 +``` + +#### High Quality +```toml +[simulation] +max_steps_static = 30000 +max_steps_moving = 60000 +compute_height = 1024 +early_exit_distance = 8e+12 +target_fps = 60 +``` + +#### Balanced +```toml +[simulation] +max_steps_static = 15000 +max_steps_moving = 30000 +compute_height = 512 +early_exit_distance = 5e+12 +target_fps = 60 +``` + +#### Performance +```toml +[simulation] +max_steps_static = 5000 +max_steps_moving = 10000 +compute_height = 256 +early_exit_distance = 2e+12 +target_fps = 120 +``` + +## Troubleshooting + +### Common Issues + +#### Performance Problems +- **Symptom**: Low frame rate, stuttering +- **Solution**: Reduce `max_steps_moving`, `max_steps_static`, or `compute_height` +- **Alternative**: Increase `early_exit_distance` + +#### Quality Issues +- **Symptom**: Poor image quality, artifacts +- **Solution**: Increase `compute_height` and step counts +- **Alternative**: Reduce `early_exit_distance` + +#### Configuration Errors +- **Symptom**: Application crashes on startup +- **Solution**: Delete configuration file to reset to defaults +- **Alternative**: Check TOML syntax in settings file
\ No newline at end of file diff --git a/docs/mathematical-theory.md b/docs/mathematical-theory.md new file mode 100644 index 0000000..4c73425 --- /dev/null +++ b/docs/mathematical-theory.md @@ -0,0 +1,169 @@ +# Mathematical Theory of Black Hole Geodesics + +### Einstein’s Field Equations + +General Relativity describes gravity not as a force, but as the curvature of spacetime caused by mass and energy. This curvature is captured by **Einstein’s field equations**: + +$$ +G_{\mu\nu} = \frac{8\pi G}{c^4} T_{\mu\nu} +$$ + +Here: + +* $G_{\mu\nu}$ is the **Einstein tensor**, describing spacetime curvature. +* $T_{\mu\nu}$ is the **stress-energy tensor**, representing matter and energy. +* $G$ is Newton’s gravitational constant, and $c$ is the speed of light. + +### Spacetime Metric + +Distances in spacetime are described using the **metric tensor** $g_{\mu\nu}$: + +$$ +ds^2 = g_{\mu\nu} dx^\mu dx^\nu +$$ + +where $ds^2$ is the spacetime interval between two events. + +## Schwarzschild Metric + +For a **spherically symmetric, non-rotating mass** (like a static black hole), the Schwarzschild solution gives the spacetime geometry: + +$$ +ds^2 = -\left(1 - \frac{2GM}{c^2 r}\right) dt^2 + \left(1 - \frac{2GM}{c^2 r}\right)^{-1} dr^2 + r^2 (d\theta^2 + \sin^2\theta \, d\phi^2) +$$ + +* $M$ = mass of the black hole +* $r, \theta, \phi$ = spherical coordinates +* $t$ = time coordinate + +The **Schwarzschild radius** $r_s$ marks the event horizon: + +$$ +r_s = \frac{2GM}{c^2} +$$ + +Inside $r_s$, not even light can escape. + +We often write the metric using the **lapse function** $f(r)$: + +$$ +ds^2 = -f(r) dt^2 + f(r)^{-1} dr^2 + r^2 (d\theta^2 + \sin^2\theta \, d\phi^2), \quad f(r) = 1 - \frac{r_s}{r} +$$ + +## Geodesics: Paths of Free-Falling Particles and Light + +A **geodesic** is the path that a particle follows when moving under gravity alone. For light rays, $ds^2 = 0$ (null geodesics). + +### Lagrangian Formulation + +We can derive the geodesic equations from a Lagrangian: + +$$ +L = \frac{1}{2} g_{\mu\nu} \dot{x}^\mu \dot{x}^\nu, \quad \dot{x}^\mu = \frac{dx^\mu}{d\lambda} +$$ + +where $\lambda$ is an affine parameter along the geodesic. + +### Conserved Quantities + +Because the Schwarzschild metric is **time-independent** and **spherically symmetric**, we have two key conserved quantities: + +1. **Energy** (from time translation symmetry): + +$$ +E = - g_{tt} \frac{dt}{d\lambda} = f(r) \frac{dt}{d\lambda} +$$ + +2. **Angular Momentum** (from rotational symmetry): + +$$ +L = g_{\phi\phi} \frac{d\phi}{d\lambda} = r^2 \sin^2 \theta \frac{d\phi}{d\lambda} +$$ + +### Derivation of the Geodesic Equations + +Geodesics satisfy the **Euler-Lagrange equations**: + +$$ +\frac{d}{d\lambda} \left(\frac{\partial L}{\partial \dot{x}^\mu}\right) - \frac{\partial L}{\partial x^\mu} = 0 +$$ + +#### 1. Radial Motion + +For the Schwarzschild metric: + +$$ +L = \frac{1}{2} \left[-f(r) \dot{t}^2 + f(r)^{-1} \dot{r}^2 + r^2 (\dot{\theta}^2 + \sin^2\theta \, \dot{\phi}^2)\right] +$$ + +The radial Euler-Lagrange equation becomes: + +$$ +\ddot{r} = -\frac{GM}{r^2} (\dot{t})^2 + \frac{GM}{r^2 f(r)} (\dot{r})^2 + r f(r) \left(\dot{\theta}^2 + \sin^2\theta \, \dot{\phi}^2\right) +$$ + +#### 2. Angular Motion + +$$ +\ddot{\theta} = -\frac{2}{r} \dot{r} \dot{\theta} + \sin\theta \cos\theta \, \dot{\phi}^2 +$$ + +$$ +\ddot{\phi} = -\frac{2}{r} \dot{r} \dot{\phi} - 2 \cot\theta \, \dot{\theta} \dot{\phi} +$$ + +Here, $\dot{}$ denotes derivative with respect to $\lambda$. + +These equations fully describe how light or particles move around a Schwarzschild black hole. + +### Numerical Implementation + +In a shader or simulation, we integrate these equations using: + +```glsl +void GeodesicRHS(Ray ray, out vec3 d1, out vec3 d2) +{ + float r = ray.r; + float theta = ray.theta; + float dr = ray.dr; + float dtheta = ray.dtheta; + float dphi = ray.dphi; + float f = 1.0 - SagA_rs / r; + float dt_dL = ray.E / f; + + d1 = vec3(dr, dtheta, dphi); + d2.x = - (SagA_rs / (2.0 * r*r)) * f * dt_dL * dt_dL + + (SagA_rs / (2.0 * r*r * f)) * dr * dr + + r * (dtheta*dtheta + sin(theta)*sin(theta)*dphi*dphi); + d2.y = -2.0*dr*dtheta/r + sin(theta)*cos(theta)*dphi*dphi; + d2.z = -2.0*dr*dphi/r - 2.0*cos(theta)/(sin(theta)) * dtheta * dphi; +} +``` + +### Conserved Quantities in Code + +```glsl +ray.E = f * dt_dL; // Energy +ray.L = ray.r * ray.r * sin(ray.theta) * ray.dphi; // Angular momentum +``` + +### Effective Potential + +The **radial motion** can be described using an effective potential: + +$$ +V_\text{eff}(r) = \left(1 - \frac{r_s}{r}\right) \frac{L^2}{r^2} +$$ + +This potential defines the possible orbits of light or particles. + +### Relativistic Effects Around Black Holes + +* **Gravitational Lensing:** Light bends around the black hole, producing Einstein rings, multiple images, or distorted images. +* **Event Horizon:** Located at $r = r_s$, where nothing escapes. +* **Photon Sphere:** At $r = 1.5 r_s$, light can orbit in unstable circular paths. + +### Numerical Considerations + +* **Event Horizon:** Integration becomes singular at $r = r_s$. Use adaptive step sizes or terminate integration near the horizon. +* **Coordinate Poles:** Spherical coordinates have singularities at $\theta = 0, \pi$. Avoid direct integration through these points or use transformations. diff --git a/docs/numerical-methods.md b/docs/numerical-methods.md new file mode 100644 index 0000000..4e102e6 --- /dev/null +++ b/docs/numerical-methods.md @@ -0,0 +1,50 @@ +## Runge-Kutta 4 (RK4) Integration — Explained + +When we talk about geodesics in curved spacetime, we’re dealing with a system of differential equations that describe how a particle—or in our case, a ray of light—moves. These equations are usually too complicated to solve exactly, so we turn to numerical methods. One of the most popular choices is the **fourth-order Runge-Kutta method (RK4)**. + +Think of RK4 like taking careful steps along a winding mountain trail. At each step, instead of just looking straight ahead, RK4 takes a few “sneak peeks” along the way to estimate the path more accurately. + +### The Idea in Simple Terms + +Suppose you know where you are at a particular moment and you know the slope of your path (the derivative). A naive method like **Euler’s method** would take a single step using that slope and call it a day. But if the slope changes a lot along your step, Euler can easily go off-track. + +RK4 improves on this by taking **four evaluations** of the slope at carefully chosen points: + +1. **Start of the step** — check the slope right where you are (`k1`). +2. **Halfway in, using the first slope** — imagine taking a mid-step to see if the slope changes (`k2`). +3. **Halfway in, using the second slope** — another mid-step with a slightly better estimate (`k3`). +4. **End of the step** — take a peek at the slope at the far end of your step (`k4`). + +Then RK4 combines all these slopes in a weighted average: + +$$ +y_{n+1} = y_n + \frac{h}{6} (k_1 + 2 k_2 + 2 k_3 + k_4) +$$ + +This weighted combination gives a very accurate estimate of where you should be at the next step. + +### Why RK4 Works Well for Geodesics + +In the context of geodesics: + +* Each ray has six “pieces of information”: position `(r, θ, φ)` and velocity `(dr/dλ, dθ/dλ, dφ/dλ)`. +* The RK4 method allows us to update all six components **simultaneously**, while keeping the accumulated error small. +* Because spacetime curvature can change dramatically near a black hole, RK4 is especially helpful: it’s stable enough to handle strong curvature without requiring tiny steps everywhere. + +### A Visual Analogy + +Imagine you’re rowing a boat down a twisting river: + +* **Euler**: You look at the current direction, row a fixed distance, and hope for the best. You’ll likely drift off course if the river bends sharply. +* **RK4**: You peek ahead four times along your intended path and adjust your stroke accordingly. You stay much closer to the true river path, even around tight bends. + +### Accuracy + +* RK4 is called **fourth-order** because the error per step scales with $h^5$, and the total accumulated error scales roughly with $h^4$ (where $h$ is your step size). +* This means you can take reasonably large steps without losing accuracy, which is critical when simulating millions of rays efficiently. + +### Connecting to Code + +In your code, each `k` evaluation corresponds to calculating how the ray’s position and velocity would change at different “guesses” along the step. The final weighted combination moves the ray forward accurately in spacetime. + +By using RK4, we’re essentially giving each ray a **very careful and informed nudge**, instead of blindly pushing it along, which is why the results are both stable and accurate—even near the extreme curvature of a black hole.
\ No newline at end of file diff --git a/docs/ray-tracing-implementation.md b/docs/ray-tracing-implementation.md new file mode 100644 index 0000000..89778b7 --- /dev/null +++ b/docs/ray-tracing-implementation.md @@ -0,0 +1,258 @@ +# Ray Tracing in Curved Spacetime: A Detailed Overview + +This ray tracer simulates how light behaves near a black hole, including the effects of curved spacetime and interactions with objects like stars and an accretion disk. The implementation follows a step-by-step approach, which includes initializing rays, tracing their paths through spacetime, detecting intersections with objects, and finally rendering the scene. + +## 1. Ray Initialization + +The first step in ray tracing is to create rays originating from the camera that will eventually traverse through spacetime. Each ray represents a possible path of light. + +### Camera Setup + +The camera is positioned in three-dimensional space and is defined by its orientation and field of view. It has the following parameters: + +```glsl +layout(std140, binding = 1) uniform Camera +{ + vec3 camPos; float _pad0; + vec3 camRight; float _pad1; + vec3 camUp; float _pad2; + vec3 camForward; float _pad3; + float tanHalfFov; // Field of view + float aspect; // Aspect ratio + bool moving; // Whether the camera is moving + int _pad4; +} cam; +``` + +- `camPos`: The 3D position of the camera. +- `camRight`, `camUp`, `camForward`: Orthonormal vectors defining the camera's orientation. +- `tanHalfFov` and `aspect`: Determine the camera’s field of view and the shape of the image plane. + +### Ray Generation per Pixel + +For each pixel on the image, we calculate the corresponding direction in world space and generate a ray pointing in that direction: + +```glsl +float u = (2.0 * (pix.x + 0.5) / WIDTH - 1.0) * cam.aspect * cam.tanHalfFov; +float v = (1.0 - 2.0 * (pix.y + 0.5) / HEIGHT) * cam.tanHalfFov; +vec3 dir = normalize(u * cam.camRight - v * cam.camUp + cam.camForward); +Ray ray = InitRay(cam.camPos, dir); +``` + +Here, `u` and `v` are normalized coordinates on the image plane. The `InitRay` function takes the camera position and the computed direction to create a ray in both Cartesian and spherical coordinates. + +### Ray Structure + +Each ray contains not only the standard 3D Cartesian position but also spherical coordinates and velocity components, which are necessary for simulating curved spacetime: + +```glsl +struct Ray +{ + float x, y, z; // Cartesian coordinates + float r, theta, phi; // Spherical coordinates + float dr, dtheta, dphi; // Radial and angular velocities + float E, L; // Conserved quantities in Schwarzschild geometry +}; +``` + +### Converting Cartesian to Spherical Coordinates + +The `InitRay` function converts the position and direction of the ray into spherical coordinates and computes initial velocities: + +```glsl +Ray InitRay(vec3 pos, vec3 dir) +{ + Ray ray; + ray.x = pos.x; + ray.y = pos.y; + ray.z = pos.z; + + // Spherical coordinates + ray.r = length(pos); + ray.theta = acos(pos.z / ray.r); + ray.phi = atan(pos.y, pos.x); + + // Convert direction vector to spherical velocities + float dx = dir.x; + float dy = dir.y; + float dz = dir.z; + + ray.dr = sin(ray.theta)*cos(ray.phi)*dx + sin(ray.theta)*sin(ray.phi)*dy + cos(ray.theta)*dz; + ray.dtheta = (cos(ray.theta)*cos(ray.phi)*dx + cos(ray.theta)*sin(ray.phi)*dy - sin(ray.theta)*dz) / ray.r; + ray.dphi = (-sin(ray.phi)*dx + cos(ray.phi)*dy) / (ray.r * sin(ray.theta)); + + // Calculate conserved quantities (energy E and angular momentum L) + ray.L = ray.r * ray.r * sin(ray.theta) * ray.dphi; + float f = 1.0 - SagA_rs / ray.r; + float dt_dL = sqrt((ray.dr*ray.dr)/f + ray.r*ray.r*(ray.dtheta*ray.dtheta + sin(ray.theta)*sin(ray.theta)*ray.dphi*ray.dphi)); + ray.E = f * dt_dL; + + return ray; +} +``` + +## 2. Geodesic Integration + +Once the ray is initialized, we trace its path through curved spacetime using the Schwarzschild metric. This requires solving the geodesic equations for the ray. + +### Integration Loop + +The ray is advanced step by step using a numerical integrator (Runge-Kutta 4th order). The loop continues until the ray either escapes the scene, falls into the black hole, or intersects an object. + +```glsl +for (int i = 0; i < maxSteps; ++i) +{ + if (ray.r > exitDistance) break; + if (ray.r > ESCAPE_R) break; + if (Intercept(ray, SagA_rs)) { hitBlackHole = true; break; } + + currentStepSize = CalculateAdaptiveStepSize(ray, D_LAMBDA); + RK4Step(ray, currentStepSize); + lambda += currentStepSize; + + vec3 newPos = vec3(ray.x, ray.y, ray.z); + if (CrossesEquatorialPlane(prevPos, newPos)) { hitDisk = true; break; } + if (i % objectCheckInterval == 0 && InterceptObject(ray)) { hitObject = true; break; } + + prevPos = newPos; +} +``` + +### Runge-Kutta 4 (RK4) + +The RK4 integrator provides high accuracy by computing intermediate slopes and averaging them to advance the ray: + +```glsl +void RK4Step(inout Ray ray, float dL) +{ + vec3 k1a, k1b; + GeodesicRHS(ray, k1a, k1b); + + ray.r += dL * k1a.x; + ray.theta += dL * k1a.y; + ray.phi += dL * k1a.z; + ray.dr += dL * k1b.x; + ray.dtheta += dL * k1b.y; + ray.dphi += dL * k1b.z; + + // Update Cartesian coordinates + ray.x = ray.r * sin(ray.theta) * cos(ray.phi); + ray.y = ray.r * sin(ray.theta) * sin(ray.phi); + ray.z = ray.r * cos(ray.theta); +} +``` + +### Geodesic Derivatives + +The function `GeodesicRHS` computes the derivatives needed for integration: + +```glsl +void GeodesicRHS(Ray ray, out vec3 d1, out vec3 d2) +{ + float r = ray.r; + float theta = ray.theta; + float dr = ray.dr; + float dtheta = ray.dtheta; + float dphi = ray.dphi; + float f = 1.0 - SagA_rs / r; + float dt_dL = ray.E / f; + + d1 = vec3(dr, dtheta, dphi); + + d2.x = - (SagA_rs / (2.0 * r*r)) * f * dt_dL * dt_dL + + (SagA_rs / (2.0 * r*r * f)) * dr * dr + + r * (dtheta*dtheta + sin(theta)*sin(theta)*dphi*dphi); + d2.y = -2.0*dr*dtheta/r + sin(theta)*cos(theta)*dphi*dphi; + d2.z = -2.0*dr*dphi/r - 2.0*cos(theta)/(sin(theta)) * dtheta * dphi; +} +``` + +## 3. Intersection Testing + +Rays can intersect three types of entities: the black hole, spherical objects, and the accretion disk. + +### Black Hole Intersection + +A ray hitting the event horizon is considered absorbed: + +```glsl +bool Intercept(Ray ray, float rs) +{ + return ray.r <= rs; +} +``` + +### Object Intersection + +The scene can contain multiple spherical objects such as stars or planets: + +```glsl +bool InterceptObject(Ray ray) +{ + vec3 P = vec3(ray.x, ray.y, ray.z); + + for (int i = 0; i < numObjects; ++i) + { + vec3 center = objPosRadius[i].xyz; + float radius = objPosRadius[i].w; + + float distSq = dot(P - center, P - center); + if (distSq > radius * radius * 4.0) continue; + if (distSq <= radius * radius) + { + objectColor = objColor[i]; + hitCenter = center; + hitRadius = radius; + return true; + } + } + return false; +} +``` + +### Accretion Disk Intersection + +The accretion disk lies in the equatorial plane and is checked by detecting if the ray crosses this plane: + +```glsl +bool CrossesEquatorialPlane(vec3 oldPos, vec3 newPos) +{ + bool crossed = (oldPos.y * newPos.y <0.0); + if (crossed) { diskIntersection = newPos; } + return crossed; +} +``` + +## 4. Shading and Color Computation + +Once an intersection is found, we compute the color of the pixel based on the object hit and relativistic effects such as gravitational redshift and Doppler shift. + +```glsl +vec3 ComputeColor(Ray ray) +{ + if (hitBlackHole) return vec3(0.0); // Black hole is black + if (hitDisk) return SampleDiskTexture(diskIntersection); + if (hitObject) return ApplyLighting(ray, objectColor, hitCenter, hitRadius); + + return SampleBackground(ray); // Background stars, etc. +} +```` + +This ensures that each pixel reflects both the geometrical position and relativistic effects along the ray. + +## 5. Rendering Loop + +Finally, the main rendering loop iterates over every pixel on the screen, traces a ray, and stores the computed color: + +```glsl +for (int y = 0; y < HEIGHT; ++y) +{ + for (int x = 0; x < WIDTH; ++x) + { + Ray ray = InitRayForPixel(x, y); + TraceRay(ray); + vec3 color = ComputeColor(ray); + framebuffer[y*WIDTH + x] = vec4(color, 1.0); + } +} +```
\ No newline at end of file |
