#line 102 uniform vec4 iControlMode = vec4(0.0); // Grabbed from the binaries, the code that exposes the TransformCamera struct TransformCamera { mat4 _view; mat4 _viewInverse; mat4 _projectionViewUntranslated; mat4 _projection; mat4 _projectionInverse; vec4 _viewport; }; layout(std140) uniform transformCameraBuffer { TransformCamera _camera; }; TransformCamera getTransformCamera() { return _camera; } vec2 windowToClipSpace(TransformCamera cam, vec2 viewportPos) { vec2 viewportSize = cam._viewport.zw; // Detect stereo case if (gl_FragCoord.x > viewportSize.x) { return ((viewportPos - vec2(viewportSize.x,0.0) - cam._viewport.xy) / cam._viewport.zw) * 2.0 - vec2(1.0); } else { return ((viewportPos - cam._viewport.xy) / cam._viewport.zw) * 2.0 - vec2(1.0); } } vec3 clipToEyeSpace(TransformCamera cam, vec3 clipPos) { return vec3(cam._projectionInverse * vec4(clipPos.xyz, 1.0)); } vec3 eyeToWorldSpace(TransformCamera cam, vec3 eyePos) { return vec3(cam._viewInverse * vec4(eyePos.xyz, 1.0)); } vec3 eyeToWorldSpaceDir(TransformCamera cam, vec3 eyePos) { return vec3(cam._viewInverse * vec4(eyePos.xyz, 0.0)); } //////////////////////////////////////////////////////////////// // Math Primitives //////////////////////////////////////////////////////////////// void evalRayToBoxCords( in vec3 rayO, in vec3 rayD, out vec3 T0, out vec3 T1) { vec3 C0 = vec3(-1.0); vec3 C1 = vec3(1.0); T0 = (C0 - rayO) / rayD; T1 = (C1 - rayO) / rayD; } vec2 evalRayToBoxInOut(in vec3 rayO, in vec3 rayD) { vec3 T0; vec3 T1; evalRayToBoxCords(rayO, rayD, T0, T1); vec3 Tm = min(T0, T1); vec3 TM = max(T0, T1); vec2 io; if (Tm.x > Tm.y) { if (Tm.x > Tm.z) io.x = (Tm.x); else io.x = (Tm.z); } else { if (Tm.y > Tm.z) io.x = (Tm.y); else io.x = (Tm.z); } if (TM.x < TM.y) { if (TM.x < TM.z) io.y = (TM.x); else io.y = (TM.z); } else { if (TM.y < TM.z) io.y = (TM.y); else io.y = (TM.z); } return io; } //////////////////////////////////////////////////////////////// // Volume Shape //////////////////////////////////////////////////////////////// const float VOLUME_MAX_LEN = 2.0; const float VOLUME_SCALE = 1.0; vec3 worldToVolume(vec3 wpos) { return (wpos - iWorldPosition) * VOLUME_SCALE / (0.5 * iWorldScale); } vec4 worldToVolumeAndRadius(vec4 wpos) { return vec4((wpos.xyz - iWorldPosition) * VOLUME_SCALE / (0.5 * iWorldScale), wpos.w); } void evalBoxVolumeRay(in vec3 fragPos, out vec3 rayIn, out vec3 rayOut, out vec3 rayDir, out float rayLen) { TransformCamera camera = getTransformCamera(); vec3 oriPosW = iWorldPosition; vec2 pixPos = windowToClipSpace(camera, gl_FragCoord.xy); vec3 eyePosE = clipToEyeSpace(camera, vec3(pixPos, 0.0)); vec3 eyePosW = eyeToWorldSpace(camera, eyePosE); vec3 eyeDirW = eyeToWorldSpaceDir(camera, eyePosE); vec3 fragPosV = _position.xyz * VOLUME_SCALE; vec3 oriToEyeW = eyePosW - oriPosW; vec3 eyePosV = oriToEyeW * VOLUME_SCALE / (0.5 * iWorldScale); rayDir = normalize(eyeDirW); vec2 rayIO = evalRayToBoxInOut(eyePosV, rayDir); // detect if inside the volume if (rayIO.x < 0) rayIO.x = 0; rayIn = eyePosV + rayIO.x * rayDir; rayOut = eyePosV + rayIO.y * rayDir; rayLen = rayIO.y - rayIO.x; } //////////////////////////////////////////////////////////////// // Volume Scene //////////////////////////////////////////////////////////////// uniform vec4 iHandPos = vec4(0.0, 0.0, 0.0, -1.0); // Simple //////////////// vec4 fetchSimpleScene(vec3 pos, float time) { pos.y += 1.0; pos.y *= 0.5; float ltime = 0.75 + 0.25 * time; float radius2 = ltime * 1.0 - dot(pos, pos); return vec4(pos * radius2, radius2); } // Spheres //////////////// vec4 fetchSphere(vec3 pos, vec4 sphere, vec4 color) { vec3 posS = pos - sphere.xyz; float r2 = dot(posS,posS); float sr2 = sphere.w*sphere.w; float distance = (r2 - sphere.w*sphere.w); return step(0, -distance) * vec4(color.xyz, color.w * (1 - (r2 / sr2))); } vec4 fetchTorus(vec3 p, vec2 t, vec4 color) { vec2 q = vec2(length(p.xz)-t.x,p.y); float distance = length(q)-t.y; return step(0, -distance) * vec4(color.xyz, color.w); } const int numSpheres = 6; const float invNumSpheres = 1.0 / float(numSpheres); const int numVec4PerSphere = 2; const vec4 spheres[numSpheres * numVec4PerSphere] = vec4[numSpheres * numVec4PerSphere]( vec4(0.5, 0.5, 0.5, 0.3), vec4(1.0, 1.0, 0.0, 1.0), vec4(-0.5, 0.5, 0.5, 0.2), vec4(1.0, 0.0, 0.0, 1.0), vec4(0.5, -0.5, 0.5, 0.1), vec4(0.0, 1.0, 0.0, 1.0), vec4(-0.5, -0.5, -0.5, 0.5), vec4(vec3(51.0, 226.0, 77.0) / 255.0, 0.9), vec4(0.0, 0.4, 0.0, 1.0), vec4(vec3(51.0, 77.0, 226.0) / 255.0, 0.7), vec4(0.5, 0.5, -0.5, 0.3), vec4(0.0, 1.0, 1.0, 1.0) ); const vec4 oneSphere = vec4(0.5, 0.5, 0.5, 0.3); const vec2 aTorus = vec2 (0.9, 0.05); vec4 fetchSpheresScene(vec3 pos, float time) { if (pos.z > time) return vec4(0.0); vec4 result = vec4(0.0); for (int s = 0; s < numSpheres; s++) { //for (int s = 0; s < 2; s++) { //result += fetchSphere(pos, spheres[s * numVec4PerSphere], spheres[s * numVec4PerSphere + 1]); result = max(result, fetchSphere(pos, spheres[s * numVec4PerSphere], spheres[s * numVec4PerSphere + 1])); } result = max( result, fetchTorus(pos, aTorus, vec4(vec3(0.4),1.0))); result *= invNumSpheres + 1; return result; } // Texture //////////////// const float NUM_SLICES = 96; const int NUM_SLICES_PER_AXIS = 10; const float NUM_SLICES_PER_RAW = 10.0; const vec2 MAP_DIM = vec2(10, 10); const vec2 INV_MAP_DIM = 1.0 / MAP_DIM; uniform vec4 iTransferFunctionRange = vec4(1.0, 0.0, 1.0, 0.0); float fetchTextureDensity(vec3 pos) { vec3 npos = pos * 0.5 + vec3(0.5); float sliceCoord = (1 - npos.y) * NUM_SLICES; // FLIP the Y axis float slice0 = floor(sliceCoord); float sliceZ = fract(sliceCoord); float slice1 = slice0 + 1.0; float s0OffsetX = float(int(slice0) % NUM_SLICES_PER_AXIS); float s0OffsetY = float(int(slice0) / NUM_SLICES_PER_AXIS); float s1OffsetX = float(int(slice1) % NUM_SLICES_PER_AXIS); float s1OffsetY = float(int(slice1) / NUM_SLICES_PER_AXIS); vec2 texcoordS0 = vec2((npos.x + s0OffsetX) * INV_MAP_DIM.x , (npos.z + s0OffsetY) * INV_MAP_DIM.y); vec2 texcoordS1 = vec2((npos.x + s1OffsetX) * INV_MAP_DIM.x , (npos.z + s1OffsetY) * INV_MAP_DIM.y); float densityS0 = texture(iChannel0, texcoordS0).x; float densityS1 = texture(iChannel0, texcoordS1).x; return mix(densityS0, densityS1, sliceZ); } vec4 transferFunction(float density) { density = clamp(density * step(iTransferFunctionRange.y, density) * (1.0 - step(iTransferFunctionRange.z, density)), 0, 1); if (iTransferFunctionRange.x > 0) return texture(iChannel1, vec2(density, 0.5)); else return vec4(density); } vec4 fetchTextureScene(vec3 pos, float time) { if (pos.y > time) return vec4(0.0); float density = fetchTextureDensity(pos); return transferFunction(density); } vec4 fetchVolume(vec3 pos, float time) { if (iHandPos.w > 0) { //vec4 handPos = worldToVolumeAndRadius(iHandPos); vec4 handPos = (iHandPos) - vec4(pos, 0); if (dot(handPos.xyz,handPos.xyz) < handPos.w * handPos.w) { return vec4(0.0); } } if (iControlMode.x >= 3) return fetchTextureScene(pos, time); else if (iControlMode.x >= 2) return fetchSpheresScene(pos, time); else if (iControlMode.x >= 1) return fetchSimpleScene(pos, time); else return vec4(0.0); } //////////////////////////////////////////////////////////////// // Ray tracing //////////////////////////////////////////////////////////////// const float MAX_NUM_STEPS = 100; const float STEP_LEN = VOLUME_MAX_LEN / MAX_NUM_STEPS; // worse case is 2 / MAX_NUM_STEPS const float ALPHA_TRESHOLD = 0.1; bool blendSample(vec4 value, inout vec4 result) { if (value.a >= ALPHA_TRESHOLD) { result.rgb += (1.0-result.a)*value.a*value.rgb; result.a += (1.0-result.a)*value.a; if (result.a >= 1.0) { result.a = 1; return true; } } return false; } vec4 rayTrace(vec3 rayIn, vec3 rayDir, float rayLen, vec3 rayOut, float time) { float numSteps = min(floor(rayLen / STEP_LEN), float(MAX_NUM_STEPS)); float stepLen = STEP_LEN; vec4 result = vec4(0.0); vec4 value = vec4(0.0); vec3 samplePos = rayIn; int tracedSteps = 0; for (int i=0; i < numSteps; i++) { value = fetchVolume(samplePos, time); samplePos = rayIn + i * stepLen * rayDir; if (blendSample(value, result)) { i = int(numSteps); } tracedSteps ++; } if (result.a < 1) { value = fetchVolume(rayIn + rayLen * rayDir, time); blendSample(value, result); } // if ( gl_FragCoord.x > 900) return result; return vec4(vec3(tracedSteps / float(numSteps)), 1.0); } //////////////////////////////////////////////////////////////// // Setup & Main //////////////////////////////////////////////////////////////// vec4 getProceduralColor() { // Define time float time = cos(iControlMode.y * iGlobalTime); // Define volume ray vec3 rayInV; vec3 rayOutV; vec3 rayDirV; float rayLenV; evalBoxVolumeRay(_position.xyz, rayInV, rayOutV, rayDirV, rayLenV); // now step in the volume vec4 rayColor = rayTrace(rayInV, rayDirV, rayLenV, rayOutV, time); if (rayColor.a < 0.001) { discard; } return rayColor; } float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { vec4 result = getProceduralColor(); // diffuse = vec3(1.0); diffuse = result.xyz; specular = vec3(1.0); shininess = 0.5; return result.a; }