// (c) 2017 Jakob Weiss /** ** Prerequisites: ** - _samplingStepSize - _jitterStepSizeMultiplier - _lightSource - vec4 getSampleColor(vec3 sampleTexPos) - float getSampleAlpha(vec3 sampleTexPos) - vec3 getSampleGradient(vec3 sampleTexPos) If ENABLE_SHADOWING is set: - _lightSource - _shadowIntensity If ENABLE_SHADING is set: - _lightSource - _cameraPosition */ /** * Performs the raycasting and returns the final fragment color. */ vec4 performRaycasting(in vec3 entryPoint, in vec3 exitPoint, in vec2 texCoords) { vec4 result = vec4(0.0); float firstHitT = -1.0; // calculate ray parameters vec3 direction = exitPoint.rgb - entryPoint.rgb; float t = 0.0; float tend = length(direction); direction = normalize(direction); jitterEntryPoint(entryPoint, direction, _samplingStepSize * _jitterStepSizeMultiplier); #ifdef ENABLE_SHADOWING vec3 lightSourcePositionInTex = worldToTexture(_volumeTextureParams, _lightSource._position); #endif while (t < tend) { // compute sample position vec3 samplePosition = entryPoint.rgb + t * direction; // lookup intensity and TF vec4 color = getSampleColor(samplePosition); #ifdef ENABLE_SHADOWING float shadowSamplingStepSize = 2 * _samplingStepSize; // compute direction from sample to light vec3 L = normalize(lightSourcePositionInTex - samplePosition) * shadowSamplingStepSize; // simple and expensive implementation of hard shadows if (color.a > 0.01) { bool finished = false; vec3 shadowSamplePosition = samplePosition + L; jitterEntryPoint(shadowSamplePosition, L, _jitterStepSizeMultiplier); int lightSamples = SHADOW_STEPS; float shadowFactor = 0.0; // traverse ray from sample to light while (! finished) { // grab transparency for the shadow sample shadowFactor += getSampleAlpha(shadowSamplePosition); shadowSamplePosition += L; finished = //(shadowFactor > 0.95) --lightSamples < 0 || any(lessThan(shadowSamplePosition, vec3(0.0, 0.0, 0.0))) || any(greaterThan(shadowSamplePosition, vec3(1.0, 1.0, 1.0))); } // apply shadow to color color.rgb *= exp(- shadowFactor * shadowSamplingStepSize * _shadowIntensity * SAMPLING_BASE_INTERVAL_RCP); } #endif // ENABLE_SHADOWS // perform compositing if (color.a > 0.0) { #ifdef ENABLE_SHADING // compute gradient (needed for shading and normals) vec3 gradient = getSampleGradient(samplePosition); vec4 worldPos = _volumeTextureParams._textureToWorldMatrix * vec4(samplePosition, 1.0); // calling textureToWorld here crashes Intel HD driver and nVidia driver in debug mode, hence, let's calc it manually... vec3 normal = -normalize(vec4(gradient, 0.0)).xyz; // negated because gradient is along increasing intensity but we want a surface normal color.rgb = calculatePhongShading(worldPos.xyz / worldPos.w, _lightSource, _cameraPosition, normal, color.rgb); #endif // accomodate for variable sampling rates color.a = 1.0 - pow(1.0 - color.a, _samplingStepSize * SAMPLING_BASE_INTERVAL_RCP); blendUnder(result, color); } // save first hit ray parameter for depth value calculation if (firstHitT < 0.0 && result.a > 0.3) { firstHitT = t; out_FHP = vec4(samplePosition, 1.0); out_FHN = vec4(-normalize(getSampleGradient(samplePosition)), 1.0); } // early ray termination if (result.a > 0.975) { result.a = 1.0; t = tend; } // advance to the next evaluation point along the ray t += _samplingStepSize; } // calculate depth value from ray parameter gl_FragDepth = 1.0; if (firstHitT >= 0.0) { float depthEntry = texture(_entryPointsDepth, texCoords).z; float depthExit = texture(_exitPointsDepth, texCoords).z; gl_FragDepth = calculateDepthValue(firstHitT/tend, depthEntry, depthExit); } return result; }