aboutsummaryrefslogtreecommitdiff
path: root/Assets/Shaders
diff options
context:
space:
mode:
Diffstat (limited to 'Assets/Shaders')
-rw-r--r--Assets/Shaders/Geodesic.glsl258
1 files changed, 141 insertions, 117 deletions
diff --git a/Assets/Shaders/Geodesic.glsl b/Assets/Shaders/Geodesic.glsl
index 29f7b1c..cb435d0 100644
--- a/Assets/Shaders/Geodesic.glsl
+++ b/Assets/Shaders/Geodesic.glsl
@@ -1,9 +1,37 @@
-#version 430
-layout(local_size_x = 16, local_size_y = 16) in;
+// Geodesic ray tracer.
+//
+// Originally an OpenGL 4.3 compute shader that wrote its result with
+// imageStore(). macOS OpenGL is frozen at 4.1 and has neither compute shaders
+// nor image load/store, so this is expressed as a fullscreen vertex+fragment
+// pass (GLSL 4.10) that renders into an FBO colour attachment instead. Each
+// fragment does the work one compute invocation used to: gl_FragCoord replaces
+// gl_GlobalInvocationID, and the shading maths below is unchanged.
-layout(binding = 0, rgba8) writeonly uniform image2D outImage;
-layout(binding = 5) uniform samplerCube u_HDRIEnvironment;
-layout(std140, binding = 1) uniform Camera
+#type vertex
+#version 410 core
+
+layout(location = 0) in vec2 a_Position;
+
+void main()
+{
+ gl_Position = vec4(a_Position, 0.0, 1.0);
+}
+
+#type fragment
+#version 410 core
+
+out vec4 fragColor;
+
+// Compute-resolution in pixels; supplied by the host each frame (replaces the
+// compute shader's imageSize(outImage)).
+uniform vec2 u_Resolution;
+
+// GLSL 4.10 does not allow explicit binding qualifiers on uniform blocks or
+// samplers; the host associates these with binding points via
+// glUniformBlockBinding / glUniform1i.
+uniform samplerCube u_HDRIEnvironment;
+
+layout(std140) uniform Camera
{
vec3 camPos; float _pad0;
vec3 camRight; float _pad1;
@@ -15,7 +43,7 @@ layout(std140, binding = 1) uniform Camera
int _pad4;
} cam;
-layout(std140, binding = 2) uniform Disk
+layout(std140) uniform Disk
{
float disk_r1;
float disk_r2;
@@ -24,15 +52,15 @@ layout(std140, binding = 2) uniform Disk
float disk_density;
};
-layout(std140, binding = 3) uniform Objects
+layout(std140) uniform Objects
{
int numObjects;
vec4 objPosRadius[16];
vec4 objColor[16];
- float mass[16];
+ float mass[16];
};
-layout(std140, binding = 4) uniform Simulation
+layout(std140) uniform Simulation
{
int maxStepsMoving;
int maxStepsStatic;
@@ -42,7 +70,7 @@ layout(std140, binding = 4) uniform Simulation
const float SagA_rs = 1.269e10;
const float D_LAMBDA = 1e7;
-const double ESCAPE_R = 1e30;
+const float ESCAPE_R = 1e30;
const int DEFAULT_MAX_STEPS_MOVING = 12000;
const int DEFAULT_MAX_STEPS_STATIC = 8000;
@@ -61,7 +89,7 @@ vec3 SampleHDRI(vec3 direction)
return texture(u_HDRIEnvironment, direction).rgb;
}
-float hash(float p)
+float hash(float p)
{
p = fract(p * 0.1031);
p *= p + 33.33;
@@ -69,27 +97,27 @@ float hash(float p)
return fract(p);
}
-float hash(vec2 p)
+float hash(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973));
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
-float hash(vec3 p)
+float hash(vec3 p)
{
p = fract(p * vec3(0.1031, 0.1030, 0.0973));
p += dot(p, p.yxz + 33.33);
return fract((p.x + p.y) * p.z);
}
-float noise(vec3 x)
+float noise(vec3 x)
{
vec3 i = floor(x);
vec3 frac = fract(x);
-
+
vec3 u = frac * frac * (3.0 - 2.0 * frac);
-
+
float a = hash(i);
float b = hash(i + vec3(1.0, 0.0, 0.0));
float c = hash(i + vec3(0.0, 1.0, 0.0));
@@ -98,19 +126,19 @@ float noise(vec3 x)
float f = hash(i + vec3(1.0, 0.0, 1.0));
float g = hash(i + vec3(0.0, 1.0, 1.0));
float h = hash(i + vec3(1.0, 1.0, 1.0));
-
+
return mix(mix(mix(a, b, u.x), mix(c, d, u.x), u.y),
mix(mix(e, f, u.x), mix(g, h, u.x), u.y), u.z);
}
-float fbm(vec3 x, int octaves)
+float fbm(vec3 x, int octaves)
{
float v = 0.0;
float a = 0.5;
float f = 1.0;
vec3 shift = vec3(100, 200, 300);
-
- for (int i = 0; i < octaves; ++i)
+
+ for (int i = 0; i < octaves; ++i)
{
v += a * noise(x * f);
x = x * 2.0 + shift;
@@ -120,54 +148,54 @@ float fbm(vec3 x, int octaves)
return v;
}
-float GetCloudDensity(vec3 pos)
+float GetCloudDensity(vec3 pos)
{
float r_cyl = length(vec2(pos.x, pos.z));
float r_norm = (r_cyl - disk_r1) / (disk_r2 - disk_r1);
-
- if (r_norm < 0.0 || r_norm > 1.0)
+
+ if (r_norm < 0.0 || r_norm > 1.0)
return 0.0;
-
+
float h_norm = abs(pos.y) / thickness;
float vertical_falloff = exp(-h_norm * h_norm * 3.0);
float radial_density = 1.0 - r_norm * 0.5;
-
+
vec3 noise_pos = pos * 1e-10;
float keplerian_speed = 1.0 / sqrt(r_norm + 0.1);
-
+
float rotation_angle = time * keplerian_speed * 0.5;
vec3 rotated_pos = vec3(
pos.x * cos(rotation_angle) - pos.z * sin(rotation_angle),
pos.y,
pos.x * sin(rotation_angle) + pos.z * cos(rotation_angle)
) * 1e-10;
-
+
float large_turbulence = fbm(rotated_pos * 1.2, 5);
-
+
float medium_wisps = fbm(rotated_pos * 2.5, 4);
float small_detail = fbm(rotated_pos * 6.0, 3);
float fine_detail = fbm(rotated_pos * 10.0, 2);
-
- float noise_mask = large_turbulence * 0.4 +
- medium_wisps * 0.3 +
- small_detail * 0.2 +
+
+ float noise_mask = large_turbulence * 0.4 +
+ medium_wisps * 0.3 +
+ small_detail * 0.2 +
fine_detail * 0.1;
-
+
noise_mask = smoothstep(0.25, 0.75, noise_mask);
-
+
float angle = atan(pos.z, pos.x);
float rotated_angle = angle + time * 0.5;
-
+
float spiral_arms = sin(rotated_angle * 3.0 + r_norm * 15.0) * 0.15 + 0.85;
-
+
float orbital_angle = angle + time * keplerian_speed * 0.8;
float orbital_pattern = sin(orbital_angle * 2.0 + r_norm * 8.0) * 0.2 + 0.8;
-
+
float density = vertical_falloff * radial_density * noise_mask * spiral_arms * orbital_pattern;
return density * disk_density;
}
-struct Ray
+struct Ray
{
float x, y, z;
float r, theta, phi;
@@ -175,11 +203,11 @@ struct Ray
float E, L;
};
-Ray InitRay(vec3 pos, vec3 dir)
+Ray InitRay(vec3 pos, vec3 dir)
{
Ray ray;
- ray.x = pos.x;
- ray.y = pos.y;
+ ray.x = pos.x;
+ ray.y = pos.y;
ray.z = pos.z;
ray.r = length(pos);
ray.theta = acos(pos.z / ray.r);
@@ -189,21 +217,21 @@ Ray InitRay(vec3 pos, vec3 dir)
float dy = dir.y;
float dz = dir.z;
- ray.dr = sin(ray.theta)*cos(ray.phi)*dx +
- sin(ray.theta)*sin(ray.phi)*dy +
+ 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 -
+ 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.dphi = (-sin(ray.phi)*dx + cos(ray.phi)*dy) /
(ray.r * sin(ray.theta));
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 +
+ 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;
@@ -211,22 +239,22 @@ Ray InitRay(vec3 pos, vec3 dir)
return ray;
}
-bool Intercept(Ray ray, float rs)
+bool Intercept(Ray ray, float rs)
{
return ray.r <= rs;
}
-bool InterceptObject(Ray ray)
+bool InterceptObject(Ray ray)
{
vec3 P = vec3(ray.x, ray.y, ray.z);
-
- for (int i = 0; i < numObjects; ++i)
+
+ 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)
+ if (distSq > radius * radius * 4.0)
continue;
if (distSq <= radius * radius)
@@ -241,7 +269,7 @@ bool InterceptObject(Ray ray)
return false;
}
-void GeodesicRHS(Ray ray, out vec3 d1, out vec3 d2)
+void GeodesicRHS(Ray ray, out vec3 d1, out vec3 d2)
{
float r = ray.r;
float theta = ray.theta;
@@ -259,7 +287,7 @@ void GeodesicRHS(Ray ray, out vec3 d1, out vec3 d2)
d2.z = -2.0*dr*dphi/r - 2.0*cos(theta)/(sin(theta)) * dtheta * dphi;
}
-void RK4Step(inout Ray ray, float dL)
+void RK4Step(inout Ray ray, float dL)
{
vec3 k1a, k1b;
GeodesicRHS(ray, k1a, k1b);
@@ -276,7 +304,7 @@ void RK4Step(inout Ray ray, float dL)
ray.z = ray.r * cos(ray.theta);
}
-bool IsInDiskVolume(vec3 pos)
+bool IsInDiskVolume(vec3 pos)
{
float r_cyl = length(vec2(pos.x, pos.z));
return (r_cyl >= disk_r1 && r_cyl <= disk_r2 && abs(pos.y) <= thickness);
@@ -286,83 +314,79 @@ vec4 SampleDiskColor(vec3 pos)
{
float r_cyl = length(vec2(pos.x, pos.z));
float r_norm = (r_cyl - disk_r1) / (disk_r2 - disk_r1);
-
+
vec3 innerColor = vec3(1.0, 0.9, 0.5);
vec3 midColor = vec3(1.0, 0.6, 0.2);
vec3 outerColor = vec3(0.9, 0.3, 0.1);
-
+
vec3 baseColor;
if (r_norm < 0.5)
baseColor = mix(innerColor, midColor, r_norm * 2.0);
else
baseColor = mix(midColor, outerColor, (r_norm - 0.5) * 2.0);
-
+
float r_norm_rot = (r_cyl - disk_r1) / (disk_r2 - disk_r1);
float keplerian_speed = 1.0 / sqrt(r_norm_rot + 0.1);
-
+
vec3 noise_pos = pos * 1e-10;
-
+
float color_rotation_angle = time * keplerian_speed * 0.3;
vec3 rotated_color_pos = vec3(
pos.x * cos(color_rotation_angle) - pos.z * sin(color_rotation_angle),
pos.y,
pos.x * sin(color_rotation_angle) + pos.z * cos(color_rotation_angle)
) * 1e-10;
-
+
float large_color = fbm(rotated_color_pos * 1.8, 4);
float medium_color = fbm(rotated_color_pos * 4.0, 3);
float small_color = fbm(rotated_color_pos * 8.0, 2);
float colorVariation = (large_color * 0.5 + medium_color * 0.3 + small_color * 0.2) * 0.6;
baseColor = baseColor * (1.0 + colorVariation);
-
+
float density = GetCloudDensity(pos);
vec3 brightness_noise_pos = pos * 1e-10;
-
+
float brightness_rotation_angle = time * keplerian_speed * 0.7;
vec3 rotated_brightness_pos = vec3(
pos.x * cos(brightness_rotation_angle) - pos.z * sin(brightness_rotation_angle),
pos.y,
pos.x * sin(brightness_rotation_angle) + pos.z * cos(brightness_rotation_angle)
) * 1e-10;
-
+
float brightness_large = fbm(rotated_brightness_pos * 3.0, 3);
float brightness_medium = fbm(rotated_brightness_pos * 5.0, 2);
float brightness_small = fbm(rotated_brightness_pos * 7.0, 2);
float brightness_noise = (brightness_large * 0.6 + brightness_medium * 0.3 + brightness_small * 0.1);
-
+
// Enhanced brightness with glow effect
float baseBrightness = 1.0 + density * 1.5; // Increased density contribution
float glowBrightness = brightness_noise * 0.8; // Enhanced noise contribution
float brightness = baseBrightness + glowBrightness;
-
+
return vec4(baseColor * brightness, density);
}
-float CalculateAdaptiveStepSize(Ray ray, float baseStepSize)
+float CalculateAdaptiveStepSize(Ray ray, float baseStepSize)
{
float r_factor = clamp(ray.r / (SagA_rs * 10.0), 0.1, 1.0);
float curvature = length(vec3(ray.dr, ray.dtheta * ray.r, ray.dphi * ray.r * sin(ray.theta)));
float curvature_factor = clamp(1e12 / (curvature + 1e6), 0.1, 2.0);
-
+
return clamp(baseStepSize * r_factor * curvature_factor, MIN_STEP_SIZE, MAX_STEP_SIZE);
}
-void main()
+void main()
{
- ivec2 pix = ivec2(gl_GlobalInvocationID.xy);
- int WIDTH = imageSize(outImage).x;
- int HEIGHT = imageSize(outImage).y;
-
- if (pix.x >= WIDTH ||
- pix.y >= HEIGHT)
- return;
+ ivec2 pix = ivec2(gl_FragCoord.xy);
+ int WIDTH = int(u_Resolution.x);
+ int HEIGHT = int(u_Resolution.y);
- float u = (2.0 * (pix.x + 0.5) / WIDTH - 1.0) *
+ 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) *
+ float v = (1.0 - 2.0 * (pix.y + 0.5) / HEIGHT) *
cam.tanHalfFov;
- vec3 dir = normalize(u * cam.camRight -
- v * cam.camUp +
+ vec3 dir = normalize(u * cam.camRight -
+ v * cam.camUp +
cam.camForward);
Ray ray = InitRay(cam.camPos, dir);
@@ -372,15 +396,15 @@ void main()
bool hitBlackHole = false;
bool hitObject = false;
-
+
vec4 accumulatedColor = vec4(0.0);
float transmittance = 1.0;
int maxSteps = cam.moving ? maxStepsMoving : maxStepsStatic;
-
+
if (maxSteps <= 0)
maxSteps = cam.moving ? DEFAULT_MAX_STEPS_MOVING : DEFAULT_MAX_STEPS_STATIC;
-
+
float cameraDistance = length(cam.camPos);
if (cameraDistance > 2e12)
maxSteps = maxSteps / 2;
@@ -388,83 +412,83 @@ void main()
maxSteps = int(maxSteps * 0.75);
float initialEscapeVelocity = sqrt(2.0 * SagA_rs / ray.r);
- if (ray.dr > initialEscapeVelocity * 0.95 &&
+ if (ray.dr > initialEscapeVelocity * 0.95 &&
ray.r > SagA_rs * 200.0)
maxSteps = maxSteps / 2;
float currentStepSize = D_LAMBDA;
int objectCheckInterval = 5;
- for (int i = 0; i < maxSteps; ++i)
+ for (int i = 0; i < maxSteps; ++i)
{
float exitDistance = earlyExitDistance > 0.0 ? earlyExitDistance : DEFAULT_EARLY_EXIT_DISTANCE;
- if (ray.r > exitDistance)
+ if (ray.r > exitDistance)
+ break;
+ if (ray.r > ESCAPE_R)
break;
- if (ray.r > ESCAPE_R)
+
+ if (Intercept(ray, SagA_rs))
+ {
+ hitBlackHole = true;
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 (IsInDiskVolume(newPos))
+
+ if (IsInDiskVolume(newPos))
{
vec4 diskSample = SampleDiskColor(newPos);
float density = diskSample.a;
vec3 diskColor = diskSample.rgb;
-
+
float stepLength = currentStepSize * 1e-8;
-
+
float absorption = density * stepLength * 0.8;
float scattering = density * stepLength * 1.5;
float extinction = absorption + scattering;
-
+
float stepTransmittance = exp(-extinction);
-
+
vec3 emission = diskColor * density * stepLength * 4.0 * sqrt(disk_density);
-
+
vec3 glowColor = mix(diskColor, vec3(1.0, 0.8, 0.6), 0.3);
float glowIntensity = density * stepLength * 2.0;
vec3 atmosphericGlow = glowColor * glowIntensity * 0.8;
-
+
vec3 totalEmission = emission + atmosphericGlow;
accumulatedColor.rgb += totalEmission * transmittance;
-
+
transmittance *= stepTransmittance;
-
+
if (transmittance < 0.01)
{
accumulatedColor.a = 1.0 - transmittance;
break;
}
}
-
- if (i % objectCheckInterval == 0 && InterceptObject(ray))
- {
- hitObject = true;
- break;
+
+ if (i % objectCheckInterval == 0 && InterceptObject(ray))
+ {
+ hitObject = true;
+ break;
}
-
+
prevPos = newPos;
-
+
if (ray.dr > 0.0 && ray.r > SagA_rs * 100.0 && lambda > 2e8)
break;
}
accumulatedColor.a = 1.0 - transmittance;
-
+
if (hitBlackHole)
color = vec4(0.0, 0.0, 0.0, 1.0);
- else if (hitObject)
+ else if (hitObject)
{
vec3 P = vec3(ray.x, ray.y, ray.z);
vec3 N = normalize(P - hitCenter);
@@ -484,5 +508,5 @@ void main()
color = vec4(mix(accumulatedColor.rgb, hdriColor, 1.0 - accumulatedColor.a), 1.0);
}
- imageStore(outImage, pix, color);
-} \ No newline at end of file
+ fragColor = color;
+}