I am trying to do clipping with the help of Clipping Plane API used in this example Clipping Plane.
I made the changes in the Code, but it’s not working. I am sure I am missing something somewhere both in shaders and in init(). Trying both local and global but none of them are working.
I am not sure whether to make changes or add something so that my clipping starts happening.
Here is the full code. I am using code by http://www.lebarba.com/
function init() {
//Parameters that can be modified.
guiControls = new function() {
this.model = 'foot';
this.steps = 256.0;
this.alphaCorrection = 1.0;
this.color1 = "#00FA58";
this.stepPos1 = 0.1;
this.color2 = "#CC6600";
this.stepPos2 = 0.7;
this.color3 = "#F2F200";
this.stepPos3 = 1.0;
};
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.01, 3000.0 );
camera.position.set(0,1.3,3);
controls = new THREE.OrbitControls( camera, container );
controls.center.set( 0.0, 0.0, 0.0 );
//Load the 2D texture containing the Z slices.
cubeTextures['foot'] = THREE.ImageUtils.loadTexture('foot.raw.png');
//Don't let it generate mipmaps to save memory and apply linear filtering to prevent use of LOD.
cubeTextures['foot'].generateMipmaps = false;
cubeTextures['foot'].minFilter = THREE.LinearFilter;
cubeTextures['foot'].magFilter = THREE.LinearFilter;
var transferTexture = updateTransferFunction();
var screenSize = new THREE.Vector2( window.innerWidth, window.innerHeight );
//Use NearestFilter to eliminate interpolation. At the cube edges, interpolated world coordinates
//will produce bogus ray directions in the fragment shader, and thus extraneous colors.
rtTexture = new THREE.WebGLRenderTarget( screenSize.x, screenSize.y,
{ minFilter: THREE.NearestFilter,
magFilter: THREE.NearestFilter,
wrapS: THREE.ClampToEdgeWrapping,
wrapT: THREE.ClampToEdgeWrapping,
format: THREE.RGBFormat,
type: THREE.FloatType,
generateMipmaps: false} );
// ***** Clipping planes: *****
var localPlane = new THREE.Plane( new THREE.Vector3( 0, - 1, 0 ), 0.8 ),
globalPlane = new THREE.Plane( new THREE.Vector3( - 1, 0, 0 ), 0.1 );
var materialFirstPass = new THREE.ShaderMaterial( {
vertexShader: document.getElementById( 'vertexShaderFirstPass' ).textContent,
fragmentShader: document.getElementById( 'fragmentShaderFirstPass' ).textContent,
side: THREE.BackSide,
// ***** Clipping setup (material): *****
clippingPlanes: [ localPlane ],
clipShadows: true
} );
materialSecondPass = new THREE.ShaderMaterial( {
vertexShader: document.getElementById( 'vertexShaderSecondPass' ).textContent,
fragmentShader: document.getElementById( 'fragmentShaderSecondPass' ).textContent,
side: THREE.FrontSide,
// ***** Clipping setup (material): *****/
clippingPlanes: [ localPlane ],
clipShadows: true,
uniforms: { tex: { type: "t", value: rtTexture },
cubeTex: { type: "t", value: cubeTextures['bonsai'] },
transferTex: { type: "t", value: transferTexture },
steps : {type: "1f" , value: guiControls.steps },
alphaCorrection : {type: "1f" , value: guiControls.alphaCorrection }}
});
sceneFirstPass = new THREE.Scene();
sceneSecondPass = new THREE.Scene();
var boxGeometry = new THREE.BoxGeometry(1.0, 1.0, 1.0);
boxGeometry.doubleSided = true;
var meshFirstPass = new THREE.Mesh( boxGeometry, materialFirstPass );
var meshSecondPass = new THREE.Mesh( boxGeometry, materialSecondPass );
sceneFirstPass.add( meshFirstPass );
sceneSecondPass.add( meshSecondPass );
// ***** Clipping setup (renderer): *****
var globalPlanes = [ globalPlane ],
Empty = Object.freeze( [] );
renderer = new THREE.WebGLRenderer();
renderer.clippingPlanes = Empty; // GUI sets it to globalPlanes
renderer.localClippingEnabled = true;
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
var gui = new dat.GUI();
var modelSelected = gui.add(guiControls, 'model', [ 'bonsai', 'foot', 'teapot' ] );
gui.add(guiControls, 'steps', 0.0, 512.0);
gui.add(guiControls, 'alphaCorrection', 0.01, 5.0).step(0.01);
modelSelected.onChange(function(value) { materialSecondPass.uniforms.cubeTex.value = cubeTextures[value]; } );
//Setup transfer function steps.
var step1Folder = gui.addFolder('Step 1');
var controllerColor1 = step1Folder.addColor(guiControls, 'color1');
var controllerStepPos1 = step1Folder.add(guiControls, 'stepPos1', 0.0, 1.0);
controllerColor1.onChange(updateTextures);
controllerStepPos1.onChange(updateTextures);
var step2Folder = gui.addFolder('Step 2');
var controllerColor2 = step2Folder.addColor(guiControls, 'color2');
var controllerStepPos2 = step2Folder.add(guiControls, 'stepPos2', 0.0, 1.0);
controllerColor2.onChange(updateTextures);
controllerStepPos2.onChange(updateTextures);
var step3Folder = gui.addFolder('Step 3');
var controllerColor3 = step3Folder.addColor(guiControls, 'color3');
var controllerStepPos3 = step3Folder.add(guiControls, 'stepPos3', 0.0, 1.0);
controllerColor3.onChange(updateTextures);
controllerStepPos3.onChange(updateTextures);
step1Folder.open();
step2Folder.open();
step3Folder.open();
var folderLocal = gui.addFolder( "Local Clipping" ),
propsLocal = {
get 'Enabled'() { return renderer.localClippingEnabled; },
set 'Enabled'( v ) { renderer.localClippingEnabled = v; },
get 'Plane'() { return localPlane.constant; },
set 'Plane'( v ) { localPlane.constant = v }
},
folderGlobal = gui.addFolder( "Global Clipping" ),
propsGlobal = {
get 'Enabled'() { return renderer.clippingPlanes !== Empty; },
set 'Enabled'( v ) {
renderer.clippingPlanes = v ? globalPlanes : Empty; },
get 'Plane'() { return globalPlane.constant; },
set 'Plane'( v ) { globalPlane.constant = v; }
};
folderLocal.add( propsLocal, 'Enabled' );
folderLocal.add( propsLocal, 'Plane', 0.3, 1.25 );
folderGlobal.add( propsGlobal, 'Enabled' );
folderGlobal.add( propsGlobal, 'Plane', -0.4, 3 );
onWindowResize();
window.addEventListener( 'resize', onWindowResize, false );
}
function updateTextures(value)
{
materialSecondPass.uniforms.transferTex.value = updateTransferFunction();
}
function updateTransferFunction()
{
var canvas = document.createElement('canvas');
canvas.height = 20;
canvas.width = 256;
var ctx = canvas.getContext('2d');
var grd = ctx.createLinearGradient(0, 0, canvas.width -1 , canvas.height - 1);
grd.addColorStop(guiControls.stepPos1, guiControls.color1);
grd.addColorStop(guiControls.stepPos2, guiControls.color2);
grd.addColorStop(guiControls.stepPos3, guiControls.color3);
ctx.fillStyle = grd;
ctx.fillRect(0,0,canvas.width -1 ,canvas.height -1 );
var img = document.getElementById("transferFunctionImg");
img.src = canvas.toDataURL();
img.style.width = "256 px";
img.style.height = "128 px";
transferTexture = new THREE.Texture(canvas);
transferTexture.wrapS = transferTexture.wrapT = THREE.ClampToEdgeWrapping;
transferTexture.needsUpdate = true;
return transferTexture;
}
function onWindowResize( event ) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
var delta = clock.getDelta();
//Render first pass and store the world space coords of the back face fragments into the texture.
renderer.render( sceneFirstPass, camera, rtTexture, true );
//Render the second pass and perform the volume rendering.
renderer.render( sceneSecondPass, camera );
materialSecondPass.uniforms.steps.value = guiControls.steps;
materialSecondPass.uniforms.alphaCorrection.value = guiControls.alphaCorrection;
}
<script id="fragmentShaderFirstPass" type="x-shader/x-fragment">
varying vec3 worldSpaceCoords;
void main()
{
//The fragment's world space coordinates as fragment output.
gl_FragColor = vec4( worldSpaceCoords.x , worldSpaceCoords.y, worldSpaceCoords.z, 1 );
}
</script>
<script id="vertexShaderFirstPass" type="x-shader/x-vertex">
varying vec3 worldSpaceCoords;
void main()
{
//Set the world space coordinates of the back faces vertices as output.
worldSpaceCoords = position + vec3(0.5, 0.5, 0.5); //move it from [-0.5;0.5] to [0,1]
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShaderSecondPass" type="x-shader/x-fragment">
varying vec3 worldSpaceCoords;
varying vec4 projectedCoords;
uniform sampler2D tex, cubeTex, transferTex;
uniform float steps;
uniform float alphaCorrection;
// The maximum distance through our rendering volume is sqrt(3).
// The maximum number of steps we take to travel a distance of 1 is 512.
// ceil( sqrt(3) * 512 ) = 887
// This prevents the back of the image from getting cut off when steps=512 & viewing diagonally.
const int MAX_STEPS = 887;
//Acts like a texture3D using Z slices and trilinear filtering.
vec4 sampleAs3DTexture( vec3 texCoord )
{
vec4 colorSlice1, colorSlice2;
vec2 texCoordSlice1, texCoordSlice2;
//The z coordinate determines which Z slice we have to look for.
//Z slice number goes from 0 to 255.
float zSliceNumber1 = floor(texCoord.z * 255.0);
//As we use trilinear we go the next Z slice.
float zSliceNumber2 = min( zSliceNumber1 + 1.0, 255.0); //Clamp to 255
//The Z slices are stored in a matrix of 16x16 of Z slices.
//The original UV coordinates have to be rescaled by the tile numbers in each row and column.
texCoord.xy /= 16.0;
texCoordSlice1 = texCoordSlice2 = texCoord.xy;
//Add an offset to the original UV coordinates depending on the row and column number.
texCoordSlice1.x += (mod(zSliceNumber1, 16.0 ) / 16.0);
texCoordSlice1.y += floor((255.0 - zSliceNumber1) / 16.0) / 16.0;
texCoordSlice2.x += (mod(zSliceNumber2, 16.0 ) / 16.0);
texCoordSlice2.y += floor((255.0 - zSliceNumber2) / 16.0) / 16.0;
//Get the opacity value from the 2D texture.
//Bilinear filtering is done at each texture2D by default.
colorSlice1 = texture2D( cubeTex, texCoordSlice1 );
colorSlice2 = texture2D( cubeTex, texCoordSlice2 );
//Based on the opacity obtained earlier, get the RGB color in the transfer function texture.
colorSlice1.rgb = texture2D( transferTex, vec2( colorSlice1.a, 1.0) ).rgb;
colorSlice2.rgb = texture2D( transferTex, vec2( colorSlice2.a, 1.0) ).rgb;
//How distant is zSlice1 to ZSlice2. Used to interpolate between one Z slice and the other.
float zDifference = mod(texCoord.z * 255.0, 1.0);
//Finally interpolate between the two intermediate colors of each Z slice.
return mix(colorSlice1, colorSlice2, zDifference) ;
}
void main( void ) {
//Transform the coordinates it from [-1;1] to [0;1]
vec2 texc = vec2(((projectedCoords.x / projectedCoords.w) + 1.0 ) / 2.0,
((projectedCoords.y / projectedCoords.w) + 1.0 ) / 2.0 );
//The back position is the world space position stored in the texture.
vec3 backPos = texture2D(tex, texc).xyz;
//The front position is the world space position of the second render pass.
vec3 frontPos = worldSpaceCoords;
//Using NearestFilter for rtTexture mostly eliminates bad backPos values at the edges
//of the cube, but there may still be no valid backPos value for the current fragment.
if ((backPos.x == 0.0) && (backPos.y == 0.0))
{
gl_FragColor = vec4(0.0);
return;
}
//The direction from the front position to back position.
vec3 dir = backPos - frontPos;
float rayLength = length(dir);
//Calculate how long to increment in each step.
float delta = 1.0 / steps;
//The increment in each direction for each step.
vec3 deltaDirection = normalize(dir) * delta;
float deltaDirectionLength = length(deltaDirection);
//Start the ray casting from the front position.
vec3 currentPosition = frontPos;
//The color accumulator.
vec4 accumulatedColor = vec4(0.0);
//The alpha value accumulated so far.
float accumulatedAlpha = 0.0;
//How long has the ray travelled so far.
float accumulatedLength = 0.0;
//If we have twice as many samples, we only need ~1/2 the alpha per sample.
//Scaling by 256/10 just happens to give a good value for the alphaCorrection slider.
float alphaScaleFactor = 25.6 * delta;
vec4 colorSample;
float alphaSample;
//Perform the ray marching iterations
for(int i = 0; i < MAX_STEPS; i++)
{
//Get the voxel intensity value from the 3D texture.
colorSample = sampleAs3DTexture( currentPosition );
//Allow the alpha correction customization.
alphaSample = colorSample.a * alphaCorrection;
//Applying this effect to both the color and alpha accumulation results in more realistic transparency.
alphaSample *= (1.0 - accumulatedAlpha);
//Scaling alpha by the number of steps makes the final color invariant to the step size.
alphaSample *= alphaScaleFactor;
//Perform the composition.
accumulatedColor += colorSample * alphaSample;
//Store the alpha accumulated so far.
accumulatedAlpha += alphaSample;
//Advance the ray.
currentPosition += deltaDirection;
accumulatedLength += deltaDirectionLength;
//If the length traversed is more than the ray length, or if the alpha accumulated reaches 1.0 then exit.
if(accumulatedLength >= rayLength || accumulatedAlpha >= 1.0 )
break;
}
gl_FragColor = accumulatedColor;
}
</script>
<script id="vertexShaderSecondPass" type="x-shader/x-vertex">
varying vec3 worldSpaceCoords;
varying vec4 projectedCoords;
void main()
{
worldSpaceCoords = (modelMatrix * vec4(position + vec3(0.5, 0.5,0.5), 1.0 )).xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
projectedCoords = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>