src/shaders/shaders.data.fragment.js

Summary

Maintainability
A
3 hrs
Test Coverage
import shadersInterpolation from './interpolation/shaders.interpolation';

export default class ShadersFragment {
  // pass uniforms object
  constructor(uniforms) {
    this._uniforms = uniforms;
    this._functions = {};
    this._main = '';
  }

  functions() {
    if (this._main === '') {
      // if main is empty, functions can not have been computed
      this.main();
    }

    let content = '';
    for (let property in this._functions) {
      content += this._functions[property] + '\n';
    }

    return content;
  }

  uniforms() {
    let content = '';
    for (let property in this._uniforms) {
      let uniform = this._uniforms[property];
      content += `uniform ${uniform.typeGLSL} ${property}`;

      if (uniform && uniform.length) {
        content += `[${uniform.length}]`;
      }

      content += ';\n';
    }

    return content;
  }

  main() {
    // need to pre-call main to fill up the functions list
    this._main = `
void main(void) {

  // draw border if slice is cropped
  // float uBorderDashLength = 10.;

  if( uCanvasWidth > 0. &&
      ((gl_FragCoord.x > uBorderMargin && (gl_FragCoord.x - uBorderMargin) < uBorderWidth) ||
       (gl_FragCoord.x < (uCanvasWidth - uBorderMargin) && (gl_FragCoord.x + uBorderMargin) > (uCanvasWidth - uBorderWidth) ))){
    float valueY = mod(gl_FragCoord.y, 2. * uBorderDashLength);
    if( valueY < uBorderDashLength && gl_FragCoord.y > uBorderMargin && gl_FragCoord.y < (uCanvasHeight - uBorderMargin) ){
      gl_FragColor = vec4(uBorderColor, 1.);
      return;
    }
  }

  if( uCanvasHeight > 0. &&
      ((gl_FragCoord.y > uBorderMargin && (gl_FragCoord.y - uBorderMargin) < uBorderWidth) ||
       (gl_FragCoord.y < (uCanvasHeight - uBorderMargin) && (gl_FragCoord.y + uBorderMargin) > (uCanvasHeight - uBorderWidth) ))){
    float valueX = mod(gl_FragCoord.x, 2. * uBorderDashLength);
    if( valueX < uBorderDashLength && gl_FragCoord.x > uBorderMargin && gl_FragCoord.x < (uCanvasWidth - uBorderMargin) ){
      gl_FragColor = vec4(uBorderColor, 1.);
      return;
    }
  }

  // get texture coordinates of current pixel
  vec4 dataValue = vec4(0.);
  vec3 gradient = vec3(1.); // gradient calculations will be skipped if it is equal to vec3(1.) 
  float steps = floor(uThickness / uSpacing + 0.5);

  if (steps > 1.) {
    vec3 origin = vPos - uThickness * 0.5 * vNormal;
    vec4 dataValueAcc = vec4(0.);
    for (float step = 0.; step < 128.; step++) {
      if (step >= steps) {
        break;
      }

      vec4 dataCoordinates = uWorldToData * vec4(origin + step * uSpacing * vNormal, 1.);
      vec3 currentVoxel = dataCoordinates.xyz;
      ${shadersInterpolation(this, 'currentVoxel', 'dataValueAcc', 'gradient')};

      if (step == 0.) {
        dataValue.r = dataValueAcc.r;
        continue;
      }

      if (uThicknessMethod == 0) {
        dataValue.r = max(dataValueAcc.r, dataValue.r);
      }
      if (uThicknessMethod == 1) {
        dataValue.r += dataValueAcc.r;
      }
      if (uThicknessMethod == 2) {
        dataValue.r = min(dataValueAcc.r, dataValue.r);
      }
    }

    if (uThicknessMethod == 1) {
      dataValue.r /= steps;
    }
  } else {
    vec4 dataCoordinates = uWorldToData * vec4(vPos, 1.);
    vec3 currentVoxel = dataCoordinates.xyz;
    ${shadersInterpolation(this, 'currentVoxel', 'dataValue', 'gradient')}
  }

  if(uNumberOfChannels == 1){
    // rescale/slope
    float realIntensity = dataValue.r * uRescaleSlopeIntercept[0] + uRescaleSlopeIntercept[1];
  
    // threshold
    if (realIntensity < uLowerUpperThreshold[0] || realIntensity > uLowerUpperThreshold[1]) {
      discard;
    }
  
    // normalize
    float windowMin = uWindowCenterWidth[0] - uWindowCenterWidth[1] * 0.5;
    float normalizedIntensity =
      ( realIntensity - windowMin ) / uWindowCenterWidth[1];
    dataValue.r = dataValue.g = dataValue.b = normalizedIntensity;
    dataValue.a = 1.;

    // apply LUT
    if(uLut == 1){
      // should opacity be grabbed there?
      dataValue = texture2D( uTextureLUT, vec2( normalizedIntensity , 1.0) );
    }
  
    // apply segmentation
    if(uLutSegmentation == 1){
      // should opacity be grabbed there?
      //
      float textureWidth = 256.;
      float textureHeight = 128.;
      float min = 0.;
      // start at 0!
      int adjustedIntensity = int(floor(realIntensity + 0.5));
  
      // Get row and column in the texture
      int colIndex = int(mod(float(adjustedIntensity), textureWidth));
      int rowIndex = int(floor(float(adjustedIntensity)/textureWidth));
  
      float texWidth = 1./textureWidth;
      float texHeight = 1./textureHeight;
    
      // Map row and column to uv
      vec2 uv = vec2(0,0);
      uv.x = 0.5 * texWidth + (texWidth * float(colIndex));
      uv.y = 1. - (0.5 * texHeight + float(rowIndex) * texHeight);
  
      dataValue = texture2D( uTextureLUTSegmentation, uv );
    }
  }

  if(uInvert == 1){
    dataValue.xyz = vec3(1.) - dataValue.xyz;
  }

  dataValue.a = dataValue.a*uOpacity;

  gl_FragColor = dataValue;
}
   `;
  }

  compute() {
    return `
// uniforms
${this.uniforms()}

// varying (should fetch it from vertex directly)
varying vec3 vPos;
varying vec3 vNormal;

// tailored functions
${this.functions()}

// main loop
${this._main}
      `;
  }
}