Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented user-defined clipping planes.
Browse files Browse the repository at this point in the history
tschw committed Mar 26, 2016
1 parent 600417d commit 733630d
Showing 35 changed files with 340 additions and 14 deletions.
94 changes: 94 additions & 0 deletions examples/webgl_clipping_planes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - clipping planes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
</style>
</head>
<body>

<script src="../build/three.js"></script>

<script>

var camera, scene, renderer;
var mesh, plane1, plane2, startTime;

init();
animate();

function init() {

camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 400;

scene = new THREE.Scene();

var texture = new THREE.TextureLoader().load( 'textures/crate.gif' );

var geometry = new THREE.BoxBufferGeometry( 200, 200, 200 );
var material = new THREE.MeshBasicMaterial( { map: texture, side: THREE.DoubleSide } );

mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

//

var N2 = Math.SQRT1_2;
var N3 = Math.sqrt( 1 / 3 );
plane1 = new THREE.Plane( new THREE.Vector3( - N3, - N3, - N3 ), 120 );
plane2 = new THREE.Plane( new THREE.Vector3( 0 , N2 , - N2 ), 90 );
renderer.clippingPlanes.push( plane1, plane2 );

//

window.addEventListener( 'resize', onWindowResize, false );

startTime = Date.now();

}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

var currentTime = Date.now(),
time = ( currentTime - startTime ) / 1000;

requestAnimationFrame( animate );

plane2.constant = Math.cos( time * 4 ) * 20 + 80;

mesh.rotation.x = 0.4 + time * 0.2;
mesh.rotation.y = -1.3 + time * 0.3;

renderer.render( scene, camera );

mesh.material.needsUpdate = true;


}

</script>

</body>
</html>
1 change: 1 addition & 0 deletions src/materials/ShaderMaterial.js
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ THREE.ShaderMaterial = function ( parameters ) {
this.fog = false; // set to use scene fog

this.lights = false; // set to use scene lights
this.clippingPlanes = false; // set to use user-defined clipping planes

this.vertexColors = THREE.NoColors; // set to use "color" attribute stream

147 changes: 141 additions & 6 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
@@ -49,6 +49,11 @@ THREE.WebGLRenderer = function ( parameters ) {

this.sortObjects = true;

// user-defined clipping

this.clippingPlanes = [];
this.clipShadows = false;

// physically based shading

this.gammaFactor = 2.0; // for backwards compatibility
@@ -143,6 +148,15 @@ THREE.WebGLRenderer = function ( parameters ) {

},

_clippingPlanesUniform = {
type: '4fv', value: null, needsUpdate: false },
_clipIgnoreCamChanges = false,
_numClippingPlanes = 0,

_matrix3 = new THREE.Matrix3(),
_sphere = new THREE.Sphere(),
_plane = new THREE.Plane(),

// info

_infoMemory = {
@@ -1156,6 +1170,7 @@ THREE.WebGLRenderer = function ( parameters ) {
sprites.length = 0;
lensFlares.length = 0;

setupClippingPlanes( camera );
projectObject( scene, camera );

opaqueObjects.length = opaqueObjectsLastIndex + 1;
@@ -1170,12 +1185,29 @@ THREE.WebGLRenderer = function ( parameters ) {

//

if ( ! this.clipShadows ) {

setupClippingPlanes( null );
_clipIgnoreCamChanges = true;

}

setupShadows( lights );

shadowMap.render( scene, camera );

setupLights( lights, camera );


//

_clipIgnoreCamChanges = false;

// clipping was either disabled or setup for a different
// cam rendering shadow maps, so switch back to main cam
// skipping the transform
setupClippingPlanes( camera, true );

//

_infoRender.calls = 0;
@@ -1299,6 +1331,37 @@ THREE.WebGLRenderer = function ( parameters ) {

}

function objectCanBeVisible( object ) {

var geometry = object.geometry;

if ( geometry.boundingSphere === null )
geometry.computeBoundingSphere();

var sphere = _sphere.
copy( geometry.boundingSphere ).
applyMatrix4( object.matrixWorld );

if ( ! _frustum.intersectsSphere( sphere ) ) return false;
if ( _numClippingPlanes === 0 ) return true;

var planes = _this.clippingPlanes,

center = sphere.center,
negRad = - sphere.radius,
i = 0;

do {

// out when deeper than radius in the negative halfspace
if ( planes[ i ].distanceToPoint( center ) < negRad ) return false;

} while ( ++ i !== _numClippingPlanes );

return true;

}

function projectObject( object, camera ) {

if ( object.visible === false ) return;
@@ -1311,7 +1374,7 @@ THREE.WebGLRenderer = function ( parameters ) {

} else if ( object instanceof THREE.Sprite ) {

if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
if ( object.frustumCulled === false || objectCanBeVisible( object ) === true ) {

sprites.push( object );

@@ -1340,7 +1403,7 @@ THREE.WebGLRenderer = function ( parameters ) {

}

if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
if ( object.frustumCulled === false || objectCanBeVisible( object ) === true ) {

var material = object.material;

@@ -1439,7 +1502,9 @@ THREE.WebGLRenderer = function ( parameters ) {

var materialProperties = properties.get( material );

var parameters = programCache.getParameters( material, _lights, fog, object );
var parameters = programCache.getParameters(
material, _lights, fog, _numClippingPlanes, object );

var code = programCache.getProgramCode( material, parameters );

var program = materialProperties.program;
@@ -1534,10 +1599,20 @@ THREE.WebGLRenderer = function ( parameters ) {

}

materialProperties.uniformsList = [];
var uniforms = materialProperties.__webglShader.uniforms;

if ( ! ( material instanceof THREE.ShaderMaterial ) &&
! ( material instanceof THREE.RawShaderMaterial ) ||
material.clippingPlanes === true ) {

materialProperties.numClippingPlanes = _numClippingPlanes;
uniforms.clippingPlanes = _clippingPlanesUniform;

}

var uniformLocations = materialProperties.program.getUniforms();

var uniforms = materialProperties.__webglShader.uniforms,
uniformLocations = materialProperties.program.getUniforms();
materialProperties.uniformsList = [];

for ( var u in uniforms ) {

@@ -1644,6 +1719,13 @@ THREE.WebGLRenderer = function ( parameters ) {

}

if ( materialProperties.numClippingPlanes !== undefined &&
materialProperties.numClippingPlanes !== _numClippingPlanes ) {

material.needsUpdate = true;

}

if ( material.needsUpdate ) {

initMaterial( material, fog, object );
@@ -1700,6 +1782,12 @@ THREE.WebGLRenderer = function ( parameters ) {
refreshMaterial = true; // set to true on material change
refreshLights = true; // remains set until update done

if ( _clipIgnoreCamChanges === false ) {

setupClippingPlanes( camera );

}

}

// load material specific uniforms
@@ -2773,6 +2861,53 @@ THREE.WebGLRenderer = function ( parameters ) {

}

function setupClippingPlanes( camera, alreadyTransformed ) {

var planes = _this.clippingPlanes,
nPlanes = camera !== null ? planes.length : 0;

if ( nPlanes !== 0 ) {

var camProps = properties.get( camera ),
planeUniforms = camProps.clippingPlanes;

if ( alreadyTransformed !== true ) {

var flatSize = nPlanes * 4,
viewMatrix = camera.matrixWorldInverse;

if ( planeUniforms === undefined ||
planeUniforms.length < flatSize ) {

planeUniforms = new Float32Array( flatSize );
camProps.clippingPlanes = planeUniforms;

}

var viewNormalMatrix =
_matrix3.getNormalMatrix( viewMatrix );

for ( var i = 0, i4 = 0; i !== nPlanes; ++ i, i4 += 4 ) {

var plane = _plane.copy( planes[ i ] ).
applyMatrix4( viewMatrix, viewNormalMatrix );

plane.normal.toArray( planeUniforms, i4 );
planeUniforms[ i4 + 3 ] = plane.constant;

}

} // else assert( planeUniforms !=== undefined );

_clippingPlanesUniform.value = planeUniforms;
_clippingPlanesUniform.needsUpdate = true;

}

_numClippingPlanes = nPlanes;

}

// GL state setting

this.setFaceCulling = function ( cullFace, frontFaceDirection ) {
10 changes: 10 additions & 0 deletions src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#if NUM_CLIPPING_PLANES > 0

for ( int i = 0; i < NUM_CLIPPING_PLANES; ++ i ) {

vec4 plane = clippingPlanes[ i ];
if ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;

}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#if NUM_CLIPPING_PLANES > 0

#if ! defined( STANDARD ) && ! defined( PHONG )
varying vec3 vViewPosition;
#endif

uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#if NUM_CLIPPING_PLANES > 0 && ! defined( STANDARD ) && ! defined( PHONG )
varying vec3 vViewPosition;
#endif
4 changes: 4 additions & 0 deletions src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#if NUM_CLIPPING_PLANES > 0 && ! defined( STANDARD ) && ! defined( PHONG )
vViewPosition = - mvPosition.xyz;
#endif

3 changes: 3 additions & 0 deletions src/renderers/shaders/ShaderLib/cube_frag.glsl
Original file line number Diff line number Diff line change
@@ -5,9 +5,12 @@ varying vec3 vWorldPosition;

#include <common>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>

void main() {

#include <clipping_planes_fragment>

gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );

#include <logdepthbuf_fragment>
Loading

0 comments on commit 733630d

Please sign in to comment.