"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.vertexShaderVolume = exports.fragmentShaderVolume = void 0;
// Based on the blog post by Will Usher:
// https://www.willusher.io/webgl/2019/01/13/volume-rendering-with-webgl
// The code here adds lighting, depth-based compositing of a solid surface, 
// mirroring, and little tweaks like dithering to reduce aliasing.
var vertexShaderVolume = "\nout vec3 rayDirUnnorm;\n\n// Three.js adds built-in uniforms and attributes:\n// https://threejs.org/docs/#api/en/renderers/webgl/WebGLProgram\n// attribute vec3 position;\n// // = object.matrixWorld\n// uniform mat4 modelMatrix;\n// // = camera.matrixWorldInverse * object.matrixWorld\n// uniform mat4 modelViewMatrix;\n// // = camera.projectionMatrix\n// uniform mat4 projectionMatrix;\n// // = camera position in world space\n// uniform vec3 cameraPosition;\n\nvoid main()\n{\n  rayDirUnnorm = position - cameraPosition;\n  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n";
exports.vertexShaderVolume = vertexShaderVolume;
var fragmentShaderVolume = "\nprecision mediump float;\nin vec3 rayDirUnnorm;\n\nuniform sampler2D transferTex;\nuniform highp sampler3D volumeTex;\nuniform float alphaScale;\nuniform float dtScale;\nuniform float finalGamma;\n\nuniform bool useLighting;\nuniform vec3 light0;\nuniform vec3 light1;\nuniform vec3 light2;\nuniform vec3 lightColor0;\nuniform vec3 lightColor1;\nuniform vec3 lightColor2;\nuniform highp vec3 boxSize;\n\nuniform bool useVolumeMirrorX;\n\n// Optional parameters, for when a solid surface is being drawn along with\n// the volume data.\nuniform bool useSurface;\nuniform float near;\nuniform float far;\nuniform sampler2D surfaceColorTex;\nuniform sampler2D surfaceDepthTex;\n\n// Three.js adds built-in uniforms and attributes:\n// https://threejs.org/docs/#api/en/renderers/webgl/WebGLProgram\n// uniform vec3 cameraPosition;\n\nvec2 intersectBox(vec3 orig, vec3 dir) {\n  vec3 boxMin = vec3(-0.5) * boxSize;\n  vec3 boxMax = vec3( 0.5) * boxSize;\n  vec3 invDir = 1.0 / dir;\n  vec3 tmin0 = (boxMin - orig) * invDir;\n  vec3 tmax0 = (boxMax - orig) * invDir;\n  vec3 tmin = min(tmin0, tmax0);\n  vec3 tmax = max(tmin0, tmax0);\n  float t0 = max(tmin.x, max(tmin.y, tmin.z));\n  float t1 = min(tmax.x, min(tmax.y, tmax.z));\n  return vec2(t0, t1);\n}\n\nfloat cameraDistanceFromDepth(float depth) {\n  float zN = 2.0 * depth - 1.0;\n  float z = 2.0 * near * far / (far + near - zN * (far - near));\n  return near + z;\n}\n\nvoid main(void) {\n  vec3 rayDir = normalize(rayDirUnnorm);\n\n  rayDir.x = useVolumeMirrorX ? -rayDir.x : rayDir.x;\n  vec3 cameraPositionAdjusted = cameraPosition;\n  cameraPositionAdjusted.x = useVolumeMirrorX ? -cameraPositionAdjusted.x : cameraPositionAdjusted.x;\n\n  // Find the part of the ray that intersects the box, where this part is\n  // expressed as a range of \"t\" values (with \"t\" being the traditional\n  // parameter for a how far a point is along a ray).\n  vec2 tBox = intersectBox(cameraPositionAdjusted, rayDir);\n\n  ivec2 surfaceTexSize = ivec2(0);\n  vec2 surfaceTexCoord = vec2(0);\n  if (useSurface) {\n    // If a surface is being drawn, then adjust the range of \"t\" values\n    // so the farthest value corresponds to where the surface is, which\n    // comes from the depth-buffer value in the texture.\n    surfaceTexSize = textureSize(surfaceColorTex, 0);\n    surfaceTexCoord = gl_FragCoord.xy / vec2(surfaceTexSize);\n    float depth = texture(surfaceDepthTex, surfaceTexCoord).x;\n    float dist = cameraDistanceFromDepth(depth);\n    tBox.y = min(tBox.y, dist);\n\n    // It also may be the case that the surface extends outside the volume.\n    // If it is between the camera and the volume, the starting \"t\" value\n    // must be pushed back to accomodate it (with a little extra tolerance\n    // included) or it will appear as black.\n    tBox.x = min(tBox.x, dist - 0.0001);\n  }\n\n  if (tBox.x >= tBox.y) {\n    discard;\n  }\n\n  tBox.x = max(tBox.x, 0.0);\n\n  ivec3 volumeTexSize = textureSize(volumeTex, 0);\n  vec3 dt0 = 1.0 / (vec3(volumeTexSize) * abs(rayDir));\n  float dt = min(dt0.x, min(dt0.y, dt0.z));\n\n  dt *= dtScale;\n\n  // Prevents a lost WebGL context.\n  if (dt < 0.00001) {\n    gl_FragColor = vec4(0.0);\n    return;\n  }\n\n  // Ray starting point, in the \"real\" space where the box may not be a cube.\n  vec3 p = cameraPositionAdjusted + tBox.x * rayDir;\n\n  // Dither to reduce banding (aliasing).\n  // https://www.marcusbannerman.co.uk/articles/VolumeRendering.html\n  float random = fract(sin(gl_FragCoord.x * 12.9898 + gl_FragCoord.y * 78.233) * 43758.5453);\n  random *= 5.0;\n  p += random * dt * rayDir;\n\n  // Ray starting point, and change in ray point with each step, for the space where\n  // the box has been warped to a cube, for accessing the cubical data texture.\n  // The vec3(0.5) is necessary because rays are defined in the space where the box is\n  // centered at the origin, but texture look-ups have the origin at a box corner.\n  vec3 pSized = p / boxSize + vec3(0.5);\n  vec3 dPSized = (rayDir * dt) / boxSize;\n\n  // This renderer matches VVD_Viewer when looking along the smallest axis of the volume,\n  // but looks too bright on the other axes.  So normalize alpha to reduce it on these\n  // other axes.\n  float l = length(rayDir * boxSize);\n  float lMin = min(boxSize.x, min(boxSize.y, boxSize.z));\n  float alphaNormalization = lMin / l;\n  alphaNormalization *= alphaScale;\n\n  // A step of one voxel, for computing the gradient by a central difference.\n  vec3 dg = vec3(1) / vec3(volumeTexSize);\n\n  // Most browsers do not need this initialization, but add it to be safe.\n  gl_FragColor = vec4(0.0);\n\n  for (float t = tBox.x; t < tBox.y; t += dt) {\n    float v = texture(volumeTex, pSized).r;\n    vec4 vColor = texture(transferTex, vec2(v, 0.5));\n      \n    vColor.a *= alphaNormalization;\n\n    // Compute simple lighting when the color is not fully transparent.\n    if (useLighting && vColor.a > 0.0)\n    {\n      // Gradient approximated by the central difference.\n      float dataDxA = texture(volumeTex, pSized + vec3(dg.x, 0.0,  0.0 )).r;\n      float dataDxB = texture(volumeTex, pSized - vec3(dg.x, 0.0,  0.0 )).r;\n      float dataDyA = texture(volumeTex, pSized + vec3(0.0,  dg.y, 0.0 )).r;\n      float dataDyB = texture(volumeTex, pSized - vec3(0.0,  dg.y, 0.0 )).r;\n      float dataDzA = texture(volumeTex, pSized + vec3(0.0,  0.0,  dg.z)).r;\n      float dataDzB = texture(volumeTex, pSized - vec3(0.0,  0.0,  dg.z)).r;\n      vec3 grad = vec3(dataDxA - dataDxB, dataDyA - dataDyB, dataDzA - dataDzB);  \n\n      // When using the gradient as the surface normal for shading, we always want to\n      // act as if the surface is facing the camera.  So flip the gradient if it points\n      // away from the camera (i.e., negate it if dot(grad, rayDir) > 0.0)\n      grad *= -sign(dot(grad, rayDir));\n\n      float gradLength = length(grad);\n      grad /= gradLength;\n      float gradStrength = (gradLength < 0.0001) ? 0.0 : 1.0;\n\n      vec3 lighting0 = max(dot(grad, light0), 0.0) * lightColor0;\n      vec3 lighting1 = max(dot(grad, light1), 0.0) * lightColor1;\n      vec3 lighting2 = max(dot(grad, light2), 0.0) * lightColor2;\n      vec3 lighting = min(lighting0 + lighting1 + lighting2, vec3(1.0));\n      vColor.rgb *= lighting;\n      vColor *= gradStrength;\n\n      /*\n      // Uncomment to visualize the gradient for debugging.\n      gl_FragColor.rgb = (grad + vec3(1.0)) / 2.0;\n      gl_FragColor.a = 1.0;\n      gl_FragColor.a *= gradStrength;\n      return;\n      */\n    }\n\n    // Adding this point's color is the compositing operation \"A over B\", where \n    // A is gl_FragColor (the samples in front of this one on the ray) and \n    // B is vColor (this sample), using premultiplied alpha for the B color \n    // (i.e., vColor.a * vColor.rgb).\n    // https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied\n    gl_FragColor.rgb += (1.0 - gl_FragColor.a) * vColor.a * vColor.rgb;\n    gl_FragColor.a += (1.0 - gl_FragColor.a) * vColor.a;\n\n    if (gl_FragColor.a >= 0.95) {\n      break;\n    }\n\n    // Move to the next point along the ray.\n    pSized += dPSized;\n  }\n\n  float g = 1.0 / finalGamma;\n  gl_FragColor = pow(gl_FragColor, vec4(g, g, g, 1));\n\n  if (useSurface) {\n    // If a surface is being drawn, the background color comes from the texture\n    // captured when the surface was rendered (in its own pass).\n    vec3 surfaceColor = texture(surfaceColorTex, surfaceTexCoord).rgb;\n    float surfaceAlpha = 1.0;\n    gl_FragColor.rgb += (1.0 - gl_FragColor.a) * surfaceAlpha * surfaceColor.rgb;\n    // The following line is no longer needed with the final assignment, below.\n    // gl_FragColor.a += (1.0 - gl_FragColor.a) * surfaceAlpha;\n  }\n\n  // A few browsers show some artifacts if the final alpha value is not 1.0,\n  // probably a version of the issues discussed here:\n  // https://webglfundamentals.org/webgl/lessons/webgl-and-alpha.html\n  gl_FragColor.a = 1.0;\n}\n";
exports.fragmentShaderVolume = fragmentShaderVolume;