//pref overlayVolume|set|2 boundThresh|float|0.0|0.5|0.95 edgeBoundMix|float|0|0|1 diffuse|float|0.0|0.2|1.0 specular|float|0.0|0.3|1 shininess|float|0.01|20.0|30 overDistance|float|0.0|0.3|1 overAlpha|float|0.0|1.6|2.0 overShade|float|0.0|0.3|1.0 overClip|bool|true clipThick|float|0.01|0.7|1.0 Overlay transparency independent of background.|note //frag uniform int loops, overlays; uniform float diffuse, boundThresh, edgeBoundMix, clipThick, stepSize, sliceSize, viewWidth, viewHeight; uniform vec3 clearColor,lightPosition, clipPlane; uniform sampler3D overlayVol; uniform sampler3D overlayGradientVol; uniform sampler3D intensityVol; //3DTexture containing brightness uniform sampler3D gradientVol; //3DTexture containing gradient direction and magnitude uniform sampler2D backFace; uniform float clipPlaneDepth, specular, shininess, overAlpha, overDistance, overShade; uniform bool overClip; // for online GLSL optimizer see http://zz85.github.io/glsl-optimizer/ void main() { //const vec3 materialColor = vec3(1.0,1.0,1.0); float backAlpha = 0.95; float edgeThresh = 0.01; float edgeExp = 0.5; float overAlphaFrac = overAlpha; if (overAlphaFrac > 1.0) overAlphaFrac = 1.0; float overLight = 0.5;//1.0; float diffuseDiv = diffuse / 4.0; //if (overAlphaFrac > 1.0) overLight = 1.0 * ((overAlphaFrac - 1.5)/1.0); // get normalized pixel coordinate in view port (e.g. [0,1]x[0,1]) vec2 pixelCoord = gl_FragCoord.st; pixelCoord.x /= viewWidth; pixelCoord.y /= viewHeight; // starting position of the ray is stored in the texture coordinate vec3 start = gl_TexCoord[1].xyz; vec3 backPosition = texture2D(backFace,pixelCoord).xyz; vec3 dir = backPosition - start; float len = length(dir); dir = normalize(dir); float clipStart = 0.0; float clipEnd = len; //next see if clip plane intersects ray if (clipPlaneDepth > -0.5) { gl_FragColor.rgb = vec3(1.0,0.0,0.0); //next, see if clip plane faces viewer bool frontface = (dot(dir , clipPlane) > 0.0); //next, distance from ray origin to clip plane float disBackFace = 0.0; float dis = dot(dir,clipPlane); if (dis != 0.0 ) disBackFace = (-(clipPlaneDepth-clipThick) - dot(clipPlane, start.xyz-0.5)) / dis; if (dis != 0.0 ) dis = (-clipPlaneDepth - dot(clipPlane, start.xyz-0.5)) / dis; if (overClip) { if (!frontface) { float swap = dis; dis = disBackFace; disBackFace = swap; } if (dis >= len) len = 0.0; backPosition = start + dir * disBackFace; //if ((dis > 0.0) && (dis < len)) { if (dis < len) { if (dis > 0.0) start = start + dir * dis; //backPosition = start + dir * (dis); dir = backPosition - start; len = length(dir); //if (len <= 0.0) len = 0.0; dir = normalize(dir); } else len = 0.0; } else { if (frontface) { clipStart = dis; clipEnd = disBackFace; } if (!frontface) { clipEnd = dis; clipStart = disBackFace; } } } vec3 deltaDir = dir * stepSize; vec4 overAcc = vec4(0.0,0.0,0.0,0.0); vec4 ocolorSample,gradientSample,colAcc = vec4(0.0,0.0,0.0,0.0); vec4 colorSample = vec4(0.0,0.0,0.0,0.0); float lengthAcc = 0.0; float overAtten = 0.0; int overDepth = 0; int backDepthEnd, backDepthStart = loops; //Jitter starting depth to reduce wood grain artifacts vec3 samplePos = start.xyz + deltaDir* (fract(sin(gl_FragCoord.x * 12.9898 + gl_FragCoord.y * 78.233) * 43758.5453)); vec4 prevNorm = vec4(0.0,0.0,0.0,0.0); vec4 oprevNorm = vec4(0.0,0.0,0.0,0.0); float opacityCorrection = stepSize/sliceSize; vec3 lightDirHeadOn = normalize(gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,0.0)).xyz ; float stepSizex2 = clipStart + ( sliceSize * 3.0); float boundAcc = 0.0; float boundAcc2 = 0.0; float alphaTerminate = 0.95; if ( overlays > 0 ) alphaTerminate = 2.0; //impossible value: no early termination with overlays for(int i = 0; i < loops; i++) { if ((lengthAcc <= clipStart) || (lengthAcc > clipEnd)) { colorSample.a = 0.0; } else { colorSample = texture3D(intensityVol,samplePos); if ((lengthAcc <= stepSizex2) && (colorSample.a > 0.01) ) colorSample.a = sqrt(colorSample.a); //opaque clipping surface colorSample.a = 1.0-pow((1.0 - colorSample.a), opacityCorrection); if ((colorSample.a > 0.01) && (lengthAcc > stepSizex2) ) { if (backDepthStart == loops) backDepthStart = i; backDepthEnd = i; gradientSample= texture3D(gradientVol,samplePos); //interpolate gradient direction and magnitude gradientSample.rgb = normalize(gradientSample.rgb*2.0 - 1.0); //direction saved as 0..1, rescale to -1..1 //re-use previous normal if it has larger magnitude if (gradientSample.a < prevNorm.a) gradientSample.rgb = prevNorm.rgb; prevNorm = gradientSample; //Edge shading - darken edges parallel with viewing direction float lightNormDot = dot(gradientSample.rgb, lightDirHeadOn); //with respect to viewer float edgeVal = pow(1.0-abs(lightNormDot),edgeExp) * pow(gradientSample.a,0.3); if (edgeVal >= edgeThresh) colorSample.rgb = mix(colorSample.rgb, vec3(0.0,0.0,0.0), pow((edgeVal-edgeThresh)/(1.0-edgeThresh),4.0)); //specular lightNormDot = dot(gradientSample.rgb, lightPosition); //with respect to light location if (lightNormDot > 0.0) { colorSample.rgb += (lightNormDot * diffuse) - diffuseDiv; colorSample.rgb += specular * pow(max(dot(reflect(lightPosition, gradientSample.rgb), dir), 0.0), shininess); } else colorSample.rgb -= diffuseDiv; if (gradientSample.a > boundThresh) { float lightNormDot = dot(gradientSample.rgb, lightDirHeadOn); //with respect to viewer float boundAlpha = pow(1.0-abs(lightNormDot),6.0); boundAlpha = 1.0-pow((1.0 - boundAlpha), opacityCorrection); boundAcc += (1.0 - boundAcc2) * boundAlpha; boundAcc2 += (1.0 - boundAcc2) * boundAlpha; } }; } if ( overlays > 0 ) { gradientSample= texture3D(overlayGradientVol,samplePos); //interpolate gradient direction and magnitude if (gradientSample.a > 0.01) { if (gradientSample.a < oprevNorm.a) gradientSample.rgb = oprevNorm.rgb; oprevNorm = gradientSample; gradientSample.rgb = normalize(gradientSample.rgb*2.0 - 1.0); //direction saved as 0..1, rescale to -1..1 ocolorSample = texture3D(overlayVol,samplePos); ocolorSample.a *= gradientSample.a; //modulate by gradient ocolorSample.a = sqrt(ocolorSample.a); //Edge shading - darken edges parallel with viewing direction float lightNormDot = dot(gradientSample.rgb, lightDirHeadOn); //with respect to viewer float edgeVal = pow(1.0-abs(lightNormDot),edgeExp) * pow(gradientSample.a,overShade); ocolorSample.a = pow(ocolorSample.a, 1.0 -edgeVal); ocolorSample.rgb = mix(ocolorSample.rgb, vec3(0.0,0.0,0.0), edgeVal); //if (edgeVal >= edgeThresh) // ocolorSample.rgb = mix(ocolorSample.rgb, vec3(0.0,0.0,0.0), pow((edgeVal-edgeThresh)/(1.0-edgeThresh),4.0)); //specular lightNormDot = dot(gradientSample.rgb, lightPosition); //with respect to light location if (lightNormDot > 0.0) ocolorSample.rgb += overLight * specular * pow(max(dot(reflect(lightPosition, gradientSample.rgb), dir), 0.0), shininess); //float lightAmount = 0.1 * overLight; //diffuse //if (lightAmount > 0.0) // ocolorSample.rgb += lightAmount*dot(normalize(lightPosition), gradientSample.rgb); //Diffuse lighting //float diffTerm = max(0.5 * lightNormDot + 0.5, 0.5); //Quadratic falloff of the diffusive term //diffTerm *= diffTerm; //ocolorSample.rgb *= diffuse* (diffTerm) + (1.0 - diffuse); ocolorSample.a *= overAlphaFrac; if ( ocolorSample.a > 0.2) { if (overDepth == 0) overDepth = i; float overRatio = colorSample.a/(ocolorSample.a); if (colorSample.a > 0.02) colorSample.rgb = mix( colorSample.rgb, ocolorSample.rgb, overRatio); else colorSample.rgb = ocolorSample.rgb; colorSample.a = max(ocolorSample.a, colorSample.a); } //xxx colorSample= max(ocolorSample, colorSample); //accumulate overlay color ocolorSample.a = 1.0-pow((1.0 - ocolorSample.a), opacityCorrection); overAcc= (1.0 - overAcc.a) * ocolorSample + overAcc; boundAcc2 += (1.0 - boundAcc2) * ocolorSample.a; } } colorSample.rgb *= colorSample.a; //accumulate color colAcc= (1.0 - colAcc.a) * colorSample + colAcc; samplePos += deltaDir; lengthAcc += stepSize; if ( lengthAcc >= len || colAcc.a > alphaTerminate ) break; } colAcc.a*=backAlpha; if ((edgeBoundMix > 0.0) && ((colAcc.a + boundAcc) > 0.0)) { colAcc.rgb = mix(colAcc.rgb, vec3(0.0,0.0,0.0), (edgeBoundMix * boundAcc)/(colAcc.a+(edgeBoundMix * boundAcc)) ); colAcc.a = max(colAcc.a, boundAcc); } if ((overAcc.a > 0.01) && (overAlpha > 1.0)) { colAcc.a=max(colAcc.a,overAcc.a); if ( (overDistance > 0.0) && (overDepth > backDepthStart) && (backDepthEnd > backDepthStart)) { if (overDepth > backDepthEnd) overDepth = backDepthStart; // backDepthEnd float dx = float(overDepth-backDepthStart)/ float(backDepthEnd - backDepthStart); dx = pow(1.0-dx, overDistance); dx = pow(dx, 2.0); overAcc *= dx; } //overAlphaFrac = (overAlpha - 1.0); overAlphaFrac = overAcc.a * (overAlpha - 1.0); if (overAcc.a > 0.0) colAcc.rgb=mix(colAcc.rgb, overAcc.rgb, overAlphaFrac); } if ( colAcc.a < 1.0 ) colAcc.rgb = mix(clearColor,colAcc.rgb,colAcc.a); if (len == 0.0) colAcc.rgb = clearColor; gl_FragColor = colAcc; }