#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp sampler3D;
precision highp float;
uniform sampler3D noise3DTexture;
uniform vec2 resolution;
uniform vec2 mouselerp;
uniform float time;
out vec4 fragColor;
const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const int marchSteps = 256;
const int sdfDetectSteps = 64;
const float maxDist = 250.0;
const float minDist = 0.2;
struct Box { vec3 Position; vec3 EdgeLength; };
Box mBox = Box(vec3(0.0), vec3(16.0));
struct Camera { vec3 Position; vec3 LookAt; float ImageHeight; float FocalDistance; };
Camera mCamera = Camera(vec3(120.0, 20.0, -165.0), vec3(0.0), 2.0, 7.0 );
float sdBox(vec3 p, vec3 b) {
vec3 q = abs(p) - b;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)),0.0);
}
float volumeDistField(vec3 pos) {
float sdfValue = sdBox(pos - mBox.Position, mBox.EdgeLength);
return sdfValue;
}
float intersectRayMarch(vec3 rayOrigin, vec3 rayDirection) {
float d = 0.0;
for (int i = 0; i < sdfDetectSteps; i++) {
float dist = volumeDistField(rayOrigin + rayDirection * d);
if (dist < minDist || d > maxDist) { break; }
d += dist;
}
return d >= maxDist ? -1.0 : d;
}
vec3 camOrbit(float speedRatio) {
float theta = time * speedRatio;
float radius = 165.0;
vec2 m = (length(mouselerp) > 0.001)
? mouselerp
: vec2(sin(theta), cos(theta) * sin(theta)) * 0.5;
return vec3(
radius * cos(m.x * PI),
radius * cos((m.y * 0.5 + 0.5) * PI),
radius * sin(m.x * PI)
);
}
void setCam(vec2 uv, float ar, out vec3 rayOrigin, out vec3 rayDir) {
float imageWidth = mCamera.ImageHeight * ar;
vec3 position = camOrbit(0.3);
vec3 camView = mCamera.LookAt - position;
float camViewLength = length(camView);
vec3 camViewDir = camView / camViewLength;
vec3 camRight = cross(camViewDir, vec3(0.0, 1.0, 0.0));
vec3 camUp = cross(camRight, camViewDir);
vec3 focalPoint = position - mCamera.FocalDistance * camViewDir;
vec3 point = position;
point += camRight * (uv.x * 2.0 - 1.0) * imageWidth * 0.5;
point += camUp * (uv.y * 2.0 - 1.0) * mCamera.ImageHeight * 0.5;
rayOrigin = focalPoint;
rayDir = normalize(point - focalPoint);
}
vec3 getLight(void) { return vec3(0.03); }
vec3 render(vec3 rayOrigin, vec3 rayDirection) {
float volumeDepth = intersectRayMarch(rayOrigin, rayDirection);
vec3 volumetricColor = vec3(0.0);
if (volumeDepth > 0.0) {
float signedDistance = 0.0;
for (int i = 0; i < marchSteps; i++) {
volumeDepth += max(minDist, signedDistance);
vec3 position = rayOrigin + volumeDepth * rayDirection;
signedDistance = volumeDistField(position);
if (signedDistance < 0.0) {
float scale = 32.0;
vec3 conner = mBox.Position - mBox.EdgeLength;
float value = texture(noise3DTexture, (position - conner) / scale).x;
float target = 0.5;
if (value < target || value > target + (minDist / 4.0)) { value = 0.0; }
volumetricColor += value * getLight();
}
}
}
return min(volumetricColor, 1.0);
}
void main(void) {
vec2 uv = gl_FragCoord.xy / resolution.xy;
float ar = resolution.x / resolution.y;
float i = (floor((sin(time) * 0.5 + 0.5) * 32.0) + 0.5) / 32.0;
vec3 bgCol = texture(noise3DTexture,vec3(uv * vec2(1.0, ar) * vec2(2.02, 1.0), i)).rrr;
vec4 bg = vec4(bgCol, 1.0) * 0.1;
vec3 rayOrigin, rayDirection;
setCam(uv, ar, rayOrigin, rayDirection);
vec3 color = render(rayOrigin, rayDirection);
fragColor = bg + vec4(color, 1.0);
}