{"version":3,"file":"VolumeShader.cjs","sources":["../../src/shaders/VolumeShader.ts"],"sourcesContent":["import { Vector2, Vector3 } from 'three'\n\n/**\n * Shaders to render 3D volumes using raycasting.\n * The applied techniques are based on similar implementations in the Visvis and Vispy projects.\n * This is not the only approach, therefore it's marked 1.\n */\n\nexport const VolumeRenderShader1 = {\n uniforms: {\n u_size: { value: /* @__PURE__ */ new Vector3(1, 1, 1) },\n u_renderstyle: { value: 0 },\n u_renderthreshold: { value: 0.5 },\n u_clim: { value: /* @__PURE__ */ new Vector2(1, 1) },\n u_data: { value: null },\n u_cmdata: { value: null },\n },\n vertexShader: /* glsl */ `\n\t\tvarying vec4 v_nearpos;\n\t\tvarying vec4 v_farpos;\n\t\tvarying vec3 v_position;\n\n\t\tvoid main() {\n\t\t\t// Prepare transforms to map to \"camera view\". See also:\n\t\t\t// https://threejs.org/docs/#api/renderers/webgl/WebGLProgram\n\t\t\tmat4 viewtransformf = modelViewMatrix;\n\t\t\tmat4 viewtransformi = inverse(modelViewMatrix);\n\n\t\t\t// Project local vertex coordinate to camera position. Then do a step\n\t\t\t// backward (in cam coords) to the near clipping plane, and project back. Do\n\t\t\t// the same for the far clipping plane. This gives us all the information we\n\t\t\t// need to calculate the ray and truncate it to the viewing cone.\n\t\t\tvec4 position4 = vec4(position, 1.0);\n\t\t\tvec4 pos_in_cam = viewtransformf * position4;\n\n\t\t\t// Intersection of ray and near clipping plane (z = -1 in clip coords)\n\t\t\tpos_in_cam.z = -pos_in_cam.w;\n\t\t\tv_nearpos = viewtransformi * pos_in_cam;\n\n\t\t\t// Intersection of ray and far clipping plane (z = +1 in clip coords)\n\t\t\tpos_in_cam.z = pos_in_cam.w;\n\t\t\tv_farpos = viewtransformi * pos_in_cam;\n\n\t\t\t// Set varyings and output pos\n\t\t\tv_position = position;\n\t\t\tgl_Position = projectionMatrix * viewMatrix * modelMatrix * position4;\n\t\t}\n `,\n fragmentShader: /* glsl */ `\n\t\tprecision highp float;\n\t\tprecision mediump sampler3D;\n\n\t\tuniform vec3 u_size;\n\t\tuniform int u_renderstyle;\n\t\tuniform float u_renderthreshold;\n\t\tuniform vec2 u_clim;\n\n\t\tuniform sampler3D u_data;\n\t\tuniform sampler2D u_cmdata;\n\n\t\tvarying vec3 v_position;\n\t\tvarying vec4 v_nearpos;\n\t\tvarying vec4 v_farpos;\n\n\t// The maximum distance through our rendering volume is sqrt(3).\n\t\tconst int MAX_STEPS = 887;\t// 887 for 512^3, 1774 for 1024^3\n\t\tconst int REFINEMENT_STEPS = 4;\n\t\tconst float relative_step_size = 1.0;\n\t\tconst vec4 ambient_color = vec4(0.2, 0.4, 0.2, 1.0);\n\t\tconst vec4 diffuse_color = vec4(0.8, 0.2, 0.2, 1.0);\n\t\tconst vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);\n\t\tconst float shininess = 40.0;\n\n\t\tvoid cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);\n\t\tvoid cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);\n\n\t\tfloat sample1(vec3 texcoords);\n\t\tvec4 apply_colormap(float val);\n\t\tvec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray);\n\n\t\tvoid main() {\n\t// Normalize clipping plane info\n\t\t\tvec3 farpos = v_farpos.xyz / v_farpos.w;\n\t\t\tvec3 nearpos = v_nearpos.xyz / v_nearpos.w;\n\n\t// Calculate unit vector pointing in the view direction through this fragment.\n\t\t\tvec3 view_ray = normalize(nearpos.xyz - farpos.xyz);\n\n\t// Compute the (negative) distance to the front surface or near clipping plane.\n\t// v_position is the back face of the cuboid, so the initial distance calculated in the dot\n\t// product below is the distance from near clip plane to the back of the cuboid\n\t\t\tfloat distance = dot(nearpos - v_position, view_ray);\n\t\t\tdistance = max(distance, min((-0.5 - v_position.x) / view_ray.x,\n\t\t\t\t\t\t\t\t\t\t(u_size.x - 0.5 - v_position.x) / view_ray.x));\n\t\t\tdistance = max(distance, min((-0.5 - v_position.y) / view_ray.y,\n\t\t\t\t\t\t\t\t\t\t(u_size.y - 0.5 - v_position.y) / view_ray.y));\n\t\t\tdistance = max(distance, min((-0.5 - v_position.z) / view_ray.z,\n\t\t\t\t\t\t\t\t\t\t(u_size.z - 0.5 - v_position.z) / view_ray.z));\n\n\t// Now we have the starting position on the front surface\n\t\t\tvec3 front = v_position + view_ray * distance;\n\n\t// Decide how many steps to take\n\t\t\tint nsteps = int(-distance / relative_step_size + 0.5);\n\t\t\tif ( nsteps < 1 )\n\t\t\t\tdiscard;\n\n\t// Get starting location and step vector in texture coordinates\n\t\t\tvec3 step = ((v_position - front) / u_size) / float(nsteps);\n\t\t\tvec3 start_loc = front / u_size;\n\n\t// For testing: show the number of steps. This helps to establish\n\t// whether the rays are correctly oriented\n\t//gl_FragColor = vec4(0.0, float(nsteps) / 1.0 / u_size.x, 1.0, 1.0);\n\t//return;\n\n\t\t\tif (u_renderstyle == 0)\n\t\t\t\tcast_mip(start_loc, step, nsteps, view_ray);\n\t\t\telse if (u_renderstyle == 1)\n\t\t\t\tcast_iso(start_loc, step, nsteps, view_ray);\n\n\t\t\tif (gl_FragColor.a < 0.05)\n\t\t\t\tdiscard;\n\t\t}\n\n\t\tfloat sample1(vec3 texcoords) {\n\t\t\t/* Sample float value from a 3D texture. Assumes intensity data. */\n\t\t\treturn texture(u_data, texcoords.xyz).r;\n\t\t}\n\n\t\tvec4 apply_colormap(float val) {\n\t\t\tval = (val - u_clim[0]) / (u_clim[1] - u_clim[0]);\n\t\t\treturn texture2D(u_cmdata, vec2(val, 0.5));\n\t\t}\n\n\t\tvoid cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {\n\n\t\t\tfloat max_val = -1e6;\n\t\t\tint max_i = 100;\n\t\t\tvec3 loc = start_loc;\n\n\t// Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with\n\t// non-constant expression. So we use a hard-coded max, and an additional condition\n\t// inside the loop.\n\t\t\tfor (int iter=0; iter= nsteps)\n\t\t\t\t\tbreak;\n\t// Sample from the 3D texture\n\t\t\t\tfloat val = sample1(loc);\n\t// Apply MIP operation\n\t\t\t\tif (val > max_val) {\n\t\t\t\t\tmax_val = val;\n\t\t\t\t\tmax_i = iter;\n\t\t\t\t}\n\t// Advance location deeper into the volume\n\t\t\t\tloc += step;\n\t\t\t}\n\n\t// Refine location, gives crispier images\n\t\t\tvec3 iloc = start_loc + step * (float(max_i) - 0.5);\n\t\t\tvec3 istep = step / float(REFINEMENT_STEPS);\n\t\t\tfor (int i=0; i= nsteps)\n\t\t\t\t\tbreak;\n\n\t// Sample from the 3D texture\n\t\t\t\tfloat val = sample1(loc);\n\n\t\t\t\tif (val > low_threshold) {\n\t// Take the last interval in smaller steps\n\t\t\t\t\tvec3 iloc = loc - 0.5 * step;\n\t\t\t\t\tvec3 istep = step / float(REFINEMENT_STEPS);\n\t\t\t\t\tfor (int i=0; i u_renderthreshold) {\n\t\t\t\t\t\t\tgl_FragColor = add_lighting(val, iloc, dstep, view_ray);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tiloc += istep;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t// Advance location deeper into the volume\n\t\t\t\tloc += step;\n\t\t\t}\n\t\t}\n\n\t\tvec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray)\n\t\t{\n\t// Calculate color by incorporating lighting\n\n\t// View direction\n\t\t\tvec3 V = normalize(view_ray);\n\n\t// calculate normal vector from gradient\n\t\t\tvec3 N;\n\t\t\tfloat val1, val2;\n\t\t\tval1 = sample1(loc + vec3(-step[0], 0.0, 0.0));\n\t\t\tval2 = sample1(loc + vec3(+step[0], 0.0, 0.0));\n\t\t\tN[0] = val1 - val2;\n\t\t\tval = max(max(val1, val2), val);\n\t\t\tval1 = sample1(loc + vec3(0.0, -step[1], 0.0));\n\t\t\tval2 = sample1(loc + vec3(0.0, +step[1], 0.0));\n\t\t\tN[1] = val1 - val2;\n\t\t\tval = max(max(val1, val2), val);\n\t\t\tval1 = sample1(loc + vec3(0.0, 0.0, -step[2]));\n\t\t\tval2 = sample1(loc + vec3(0.0, 0.0, +step[2]));\n\t\t\tN[2] = val1 - val2;\n\t\t\tval = max(max(val1, val2), val);\n\n\t\t\tfloat gm = length(N); // gradient magnitude\n\t\t\tN = normalize(N);\n\n\t// Flip normal so it points towards viewer\n\t\t\tfloat Nselect = float(dot(N, V) > 0.0);\n\t\t\tN = (2.0 * Nselect - 1.0) * N;\t// ==\tNselect * N - (1.0-Nselect)*N;\n\n\t// Init colors\n\t\t\tvec4 ambient_color = vec4(0.0, 0.0, 0.0, 0.0);\n\t\t\tvec4 diffuse_color = vec4(0.0, 0.0, 0.0, 0.0);\n\t\t\tvec4 specular_color = vec4(0.0, 0.0, 0.0, 0.0);\n\n\t// note: could allow multiple lights\n\t\t\tfor (int i=0; i<1; i++)\n\t\t\t{\n\t// Get light direction (make sure to prevent zero devision)\n\t\t\t\tvec3 L = normalize(view_ray);\t//lightDirs[i];\n\t\t\t\tfloat lightEnabled = float( length(L) > 0.0 );\n\t\t\t\tL = normalize(L + (1.0 - lightEnabled));\n\n\t// Calculate lighting properties\n\t\t\t\tfloat lambertTerm = clamp(dot(N, L), 0.0, 1.0);\n\t\t\t\tvec3 H = normalize(L+V); // Halfway vector\n\t\t\t\tfloat specularTerm = pow(max(dot(H, N), 0.0), shininess);\n\n\t// Calculate mask\n\t\t\t\tfloat mask1 = lightEnabled;\n\n\t// Calculate colors\n\t\t\t\tambient_color +=\tmask1 * ambient_color;\t// * gl_LightSource[i].ambient;\n\t\t\t\tdiffuse_color +=\tmask1 * lambertTerm;\n\t\t\t\tspecular_color += mask1 * specularTerm * specular_color;\n\t\t\t}\n\n\t// Calculate final color by componing different components\n\t\t\tvec4 final_color;\n\t\t\tvec4 color = apply_colormap(val);\n\t\t\tfinal_color = color * (ambient_color + diffuse_color) + specular_color;\n\t\t\tfinal_color.a = color.a;\n\t\t\treturn final_color;\n\t\t}\n `,\n}\n"],"names":["Vector3","Vector2"],"mappings":";;;AAQO,MAAM,sBAAsB;AAAA,EACjC,UAAU;AAAA,IACR,QAAQ,EAAE,OAAuB,oBAAIA,MAAAA,QAAQ,GAAG,GAAG,CAAC,EAAE;AAAA,IACtD,eAAe,EAAE,OAAO,EAAE;AAAA,IAC1B,mBAAmB,EAAE,OAAO,IAAI;AAAA,IAChC,QAAQ,EAAE,2BAA2BC,MAAQ,QAAA,GAAG,CAAC,EAAE;AAAA,IACnD,QAAQ,EAAE,OAAO,KAAK;AAAA,IACtB,UAAU,EAAE,OAAO,KAAK;AAAA,EAC1B;AAAA,EACA;AAAA;AAAA,IAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BzB;AAAA;AAAA,IAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiO7B;;"}