#line 102 // 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; 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 //////////////////////////////////////////////////////////////// // Simple //////////////// vec4 fetchSimpleScene(vec3 pos) { if (pos.y < 0) return vec4(0); float time = 0.75 + 0.25 * cos(iGlobalTime); float radius2 = time * 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; if (r2 > sphere.w*sphere.w) return vec4(0.0); return vec4(color.xyz, color.w * (1 - (r2 / sr2))); } 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.0, 0.0, 1.0), vec4(vec3(51.0, 77.0, 226.0) / 255.0, 0.8), 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); vec4 fetchSpheresScene(vec3 pos) { 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 *= invNumSpheres; return result; } // Texture //////////////// const int NUM_SLICES_PER_AXIS = 10; const vec2 MAP_DIM = vec2(10, 10); const vec2 INV_MAP_DIM = 1.0 / MAP_DIM; vec4 fetchTextureScene(vec3 pos) { vec3 npos = pos * 0.5 + vec3(0.5); float sliceCoord = npos.y * INV_MAP_DIM.x * INV_MAP_DIM.x; int sliceIndex = int(floor(sliceCoord)); float ycoordOffset = float(sliceIndex / NUM_SLICES_PER_AXIS); float xcoordOffset = float(sliceIndex % NUM_SLICES_PER_AXIS); vec2 texcoord2D = vec2((npos.x + xcoordOffset) * INV_MAP_DIM.x , (npos.z + ycoordOffset) * INV_MAP_DIM.y); return vec4(texcoord2D, 0.0, 0.1); float density = texture(iChannel0, texcoord2D).x; return vec4(density); } vec4 fetchVolume(vec3 pos) { //return fetchSimpleScene(pos); //return fetchSpheresScene(pos); return fetchTextureScene(pos); } //////////////////////////////////////////////////////////////// // 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 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); samplePos = rayIn + i * stepLen * rayDir; if (blendSample(value, result)) { i = int(numSteps); } tracedSteps ++; } if (result.a < 1) { value = fetchVolume(rayIn + rayLen * rayDir); blendSample(value, result); } // if ( gl_FragCoord.x > 900) return result; return vec4(vec3(tracedSteps / float(numSteps)), 1.0); } //////////////////////////////////////////////////////////////// // Setup & Main //////////////////////////////////////////////////////////////// vec4 getProceduralColor() { // discard; 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); if (rayColor.a < 0.001) { discard; } return rayColor; } float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { diffuse = vec3(1.0); vec4 result = getProceduralColor(); specular = result.xyz; shininess = 0.5; return result.a; }