Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clipping planes #8465

Merged
merged 13 commits into from
Apr 7, 2016
Merged

Clipping planes #8465

merged 13 commits into from
Apr 7, 2016

Conversation

tschw
Copy link
Contributor

@tschw tschw commented Mar 25, 2016

Also includes my cleanup PR (#8464) - I will take it out, when it turns out necessary.
Edit: Took it out.

Adds a property to the render .userClippingPlanes, an array that can be filled with THREE.Plane objects. All fragments in one half-space defined by each plane are discarded, semantically pretty much like glClipPlane (present in fixed-function desktop OpenGL).

See #8462

@tschw tschw force-pushed the ClippingPlanes branch 2 times, most recently from 4f56fd2 to dc72f2d Compare March 25, 2016 05:03
@tschw
Copy link
Contributor Author

tschw commented Mar 25, 2016

Example (not linked, because it's more of a test and not super pretty-looking) is webgl_clipping_planes.html.

@erichlof
Copy link
Contributor

@tschw This is awesome, thank you!

@mrdoob We could really use this, as this is the best solution to clipping objects against an arbitrary user-defined plane.

@WestLangley
Copy link
Collaborator

@tschw Sweet!

http://ci.threejs.org/api/pullrequests/8465/examples/webgl_clipping_planes.html

I think it would be particularly helpful at some point to show in the demo how to specify the clipping plane in world-space, camera-space, and object space.

@tschw
Copy link
Contributor Author

tschw commented Mar 25, 2016

Talking about coordinate systems, shadows won't work correctly, I guess:

I do it all in view space (because minimum precision in the fragment shader is mediump), so needs a transform of the planes for every shadow camera.

@WestLangley
Copy link
Collaborator

I do it all in view space

I think in your example, the clipping planes are specified in world space. No?

@tschw
Copy link
Contributor Author

tschw commented Mar 25, 2016

Yes, the specification is world space and then I do tmp.copy(plane).applyMatrix(view) and write the data into a flat array for the uniform array.

@WestLangley
Copy link
Collaborator

I think it would be particularly helpful at some point to show in the demo how to specify the clipping plane in world-space, camera-space, and object space.

Changed my mind. World-space only is adequate, IMO, which is what you have done.

@EliasHasle
Copy link
Contributor

Awesome! I was thinking about this just yesterday, and here it is! :-)

@WestLangley Shouldn't it be possible to clip a specific object only? Say, to clip a ship while leaving the ocean and skybox alone.

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.userClippingPlanes.push( plane1, plane2 );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call it renderer.clippingPlanes? I like the simple name. Or customClippingPlanes if we need to be more specific.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I did. It's consistent and not so lengthy.

@bhouston
Copy link
Contributor

Thank you @tschw. My comments are not at all requirements, just random feedback. This PR is amazing and we need to get it in.

@tschw
Copy link
Contributor Author

tschw commented Mar 25, 2016

@WestLangley

I think it would be particularly helpful at some point to show in the demo how to specify the clipping plane in world-space, camera-space, and object space.

Changed my mind. World-space only is adequate, IMO, which is what you have done.

World space is the only thing that makes sense API-wise (unless we let the user specify the coordinate system, but that would be over-engineered IMO) and is fairly easy to reach from either of the spaces thanks to Plane.applyMatrix.

Use cases do exist in any of the spaces:

Model space: Cut an object for whatever reason - visualization, artistic stuff, or debugging.
World space: Portal culling (reminds me I should check the planes in bounding geometry tests as well, so it's performance-wise fit for that application).
View space: Removing the one-eye-clipped region before the convergence plane of a stereo viewport.

Shadows must be switchable. In some of these cases we'd want occluders to be cut, in others we wouldn't.

@@ -49,6 +49,10 @@ THREE.WebGLRenderer = function ( parameters ) {

this.sortObjects = true;

// user-defined clipping

this.userClippingPlanes = [];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand, this is mainly uniforms. Wouldn't it be better to set this array in the material instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand, this is mainly uniforms. Wouldn't it be better to set this array in the material instead?

This array holds THREE.Plane objects in world space.

Right now, clipping planes completely mask all rendering - not for a single object, not just for meshes: Points, sprites, meshes, whatever with a built-in shader. Clipping for shadow occlusion must be optional, as discussed above.

The state is global and the resulting uniform value is shared between all materials - similar as we do for the lights (please note that the implementation of the update mechanics are still half-baked in the code here right now - I'm still hardening it).

There are several use cases where it would be cool to allow it on a per object basis. I don't think it really belongs onto the material, but it would be the "reasonably close approximation right now" to avoid issues like #8379 and keep this PR focussed (I guess that's why you are suggesting it?).

Again, as with lighting, there is a conversion to view space in the renderer. This way, there are no problems when running on a platform with reduced fragment shader precision (floats scale nicely around the origin. When they are offset, the exponent gets defined by the translation and there are fewer bits to work with for fine-grained stuff. The nice thing about view space is, that we carry the origin around with us, and everything closely visible is near the origin and thus gets decent precision). So there's a dependence on the camera and putting clipping planes on the material may end up a complication rather than a simplification.

Clipping planes effectively restrict the view frustum, so it's worthwhile to consider putting them on the camera object.

The dream API would have global clipping planes on the camera to mask the viewport - ones that do not affect occluders when rendering a shadow map (just because you can't see the object doesn't mean you can't see its shadow). Then there could be additional ones on an object that semantically cut the object and therefore quite logically also its shadow.

One could then also unify frustum culling (should be a separate PR) by always having six entries minimum - the clipping planes of the camera's view frustum.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to investigate what is possible without major changes the renderer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left the planes array on the renderer for now (renamed to .clippingPlanes). I could fairly easily put the interface on the main camera, but I'm still undecided.

There are several use cases where it would be cool to allow it on a per object basis. I don't think it really belongs onto the material, but it would be the "reasonably close approximation right now" to avoid issues like #8379 and keep this PR focussed (I guess that's why you are suggesting it?).

I guess, I might have guessed wrong. Per-material planes would allow clipping a whole group of objects at once...

Now that the update paths are all working can shape it. In a while - I need to read over it again with a clear mind, maybe clean up a bit and think out how to best implement per-material clipping.

@tschw tschw force-pushed the ClippingPlanes branch 2 times, most recently from 13415dd to 4babd52 Compare March 26, 2016 04:51
@tschw
Copy link
Contributor Author

tschw commented Mar 26, 2016

Here is the revised changeset.

What's new?

  • .frustumCulled objects are not rendered when outside the clipping planes.
  • renderer.clipShadows controls whether shadows are clipped or not.
  • All updating works properly.

Fun test:

  • Open webgl_shadowmap_viewer
  • On the browser console:
> plane = new THREE.Plane( new THREE.Vector3( 0, 0, -1 ), 1 )
> renderer.clippingPlanes.push( plane )
> renderer.clipShadows = true
> plane.constant = 0

@WestLangley
Copy link
Collaborator

@EliasHasle

@WestLangley Shouldn't it be possible to clip a specific object only? Say, to clip a ship while leaving the ocean and skybox alone.

You should be able to do that with separate render passes, I would think.

@tschw

Maybe we should have a renderer.clippingPlanesEnabled flag.

Fun test:

: - )

@tschw
Copy link
Contributor Author

tschw commented Mar 26, 2016

@EliasHasle

Shouldn't it be possible to clip a specific object only? Say, to clip a ship while leaving the ocean and skybox alone.

@WestLangley

You should be able to do that with separate render passes, I would think.

I agree that it would be preferable to somehow be able to clip specific objects. Shadows would at least be tricky to get right with a multipass approach.

I think a THREE.ClipGroup that applies clipping to all contained objects in that scene graph node would probably be the best API for modeling purposes. These would be the ones that clip shadows (global clipping that does not affect shadows can be put onto the camera).

It's currently rather tricky to implement and better done after some internal issues are resolved (programs must be decoupled from the materials, see #8379). Being able to set extra clipping planes on a material, as suggested by @mrdoob above, would be within reasonable reach, I guess.

@WestLangley

Maybe we should have a renderer.clippingPlanesEnabled flag.

What is your motivation of suggesting it? Efficiency or ease of use? Right now, it's technically clippingPlanes.length and its fine to change the identity of the array:

renderer.clippingPlanes = [];
// ...
renderer.clippingPlanes = myPortalPlanes;

Efficiency-wise, I guess it would be nicer to wrap the code in render into if blocks. It doesn't save a large overhead, but also makes the source more readable.

https://github.com/tschw/three.js/blob/ClippingPlanes/src/renderers/WebGLRenderer.js#L1188-L1193
https://github.com/tschw/three.js/blob/ClippingPlanes/src/renderers/WebGLRenderer.js#L1204-L1209

However, an enable flag would require another test in the hot spot (that is frustum culling) with clipping enabled.

https://github.com/tschw/three.js/blob/ClippingPlanes/src/renderers/WebGLRenderer.js#L1346-L1361

So in case the suggestion is about efficiency, I'd opt for

var clippingPlanesEnabled = this.clippingPlanes.length !== 0;

in render to wrap up the code.

@@ -3,6 +3,7 @@ varying vec3 vNormal;
#include <common>
#include <morphtarget_pars_vertex>
#include <logdepthbuf_pars_vertex>
#incldue <clipping_planes_pars_vertex>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: #incldue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! Thanks for spotting.

@WestLangley
Copy link
Collaborator

@tschw

Maybe we should have a renderer.clippingPlanesEnabled flag.

What is your motivation of suggesting it?

It was a response to the use case specified by @EliasHasle above. Something like:

ocean.layers.set( 0 );
ship.layers.set( 1 );
scene.add( ocean );
scene.add( ship );

...

camera.layers.set( 0 );
renderer.render( scene, camera );

camera.layers.set( 1 );
renderer.clippingEnabled = true;
renderer.render( scene, camera );
renderer.clippingEnabled = false;

@bhouston
Copy link
Contributor

BTW I'd advocate getting this in similar to what @tschw has implemented and then adding further complexity later. I think he has done a good job on the implementation. Yes, it isn't perfectly flexible for all cases but we can add that in later PRs.

@tschw
Copy link
Contributor Author

tschw commented Mar 26, 2016

We have object-level clipping, now. API:

renderer.clippingPlanes // global clipping planes, do not clip shadows
renderer.localClippingEnabled // enabled local clipping (default false for zero cost)
material.clippingPlanes // local clipping planes...
material.clipShadows // ...clip shadows when this one is set to true (default false)
shaderMaterial.clipping // requests the uniform to be fed to the custom shader

@tschw
Copy link
Contributor Author

tschw commented Mar 26, 2016

@bhouston

BTW I'd advocate getting this in similar to what @tschw has implemented and then adding further complexity later. I think he has done a good job on the implementation. Yes, it isn't perfectly flexible for all cases but we can add that in later PRs.

I was about to suggest that, but it would have been half-baked for certain applications, so couldn't resist. I put some effort into having a clean internal encapsulation to keep it adaptable to future changes.

Anyone know whether we have an accessible cloneObjectArray routine somewhere in the codebase to replace this code

https://github.com/tschw/three.js/blob/ClippingPlanes/src/materials/Material.js#L282-L295

??

@tschw
Copy link
Contributor Author

tschw commented Apr 2, 2016

@WestLangley

Global clipping does not appear to clip shadows -- but I expect it is not supposed to for the use cases envisioned?

Yes, it was intentional, as the typical use would be to clip the view.

Adding an option in the example for local clip planes to be defined in local space (rather than world space) would be nice. (I know the material sharing is an issue, but clipping in local space seems more natural to me. JMHO.)

I want to get away from the material-based interface, once the material sharing issue has been resolved. I've been thinking about it for a while now, and believe I can pull it off in a nice way, that is also cleaning up the renderer. But that's clearly something for a different PR - after this one has been merged.

So,instead of improving the example, I'd like to improve the API. We could then have a THREE.ClippingGroup similar to THREE.Group, where the planes are then specified in that object's space. We could even go further and introduce a THREE.ClippingPlane which would be an Object3D that clips all the objects in the same ClippingGroup.

object.updateMatrixWorld();

scene.updateMatrixWorld(), once again, no? :)

@WestLangley
Copy link
Collaborator

@tschw

I want to get away from the material-based interface

Yes. That would be even better.

@mrdoob
Copy link
Owner

mrdoob commented Apr 3, 2016

Reading...

@tschw
Copy link
Contributor Author

tschw commented Apr 6, 2016

git rebase dev

Is there actually any interest in merging this one?

@bhouston
Copy link
Contributor

bhouston commented Apr 6, 2016

I think this PR is very useful and valuable. Thank you @tschw. I've love to see it merged.

@mrdoob
Copy link
Owner

mrdoob commented Apr 7, 2016

I want to get away from the material-based interface, once the material sharing issue has been resolved. I've been thinking about it for a while now, and believe I can pull it off in a nice way, that is also cleaning up the renderer. But that's clearly something for a different PR - after this one has been merged.

So,instead of improving the example, I'd like to improve the API. We could then have a THREE.ClippingGroup similar to THREE.Group, where the planes are then specified in that object's space. We could even go further and introduce a THREE.ClippingPlane which would be an Object3D that clips all the objects in the same ClippingGroup.

That sounds great!

@mrdoob mrdoob merged commit c468461 into mrdoob:dev Apr 7, 2016
@mrdoob
Copy link
Owner

mrdoob commented Apr 7, 2016

Many thanks! And sorry for the delay 😇

@mrdoob
Copy link
Owner

mrdoob commented Apr 7, 2016

Btw, we should consider move all the relevant code to a WebGLClipping class. Otherwise we'll end up with a monter WebGLRenderer again 😅

@tschw
Copy link
Contributor Author

tschw commented Apr 7, 2016

Btw, we should consider move all the relevant code to a WebGLClipping class. Otherwise we'll end up with a monter WebGLRenderer again

I'd say WebGLRenderer still is a monster, although we try dieting it :).

I described what I'd like do in the next PR in #8552. The factorization would fit in there quite nicely, probably together with other render state stuff currently floating around in locals - we'll see how that one plays out.

@arunkkarepu
Copy link

@tschw Hi I am using r77 version. I want to use tranformcontrols for clipping the object by translating or rotating the plane geometry against the object. But the problem is clippingPlanes is taking only THREE.Planes but not Three.Mesh (PlaneGeometry) for which transform controls are not applicable. Is there any solution so that i can translate Three.Plane using transform controls.
It should look similar to the attachment which i have taken from google source.
3d tranform cont

@tschw
Copy link
Contributor Author

tschw commented Jul 5, 2016

@arunkkarepu
Actually an SO question: Mathematically a plane is a homogeneous row vector that is right-multiplied with a 4x4 transform, where x,y,z resemble the normal (from "normal direction of the plane", doesn't have to be normalized) and w is the "constant" (the origin offset along the normal given at similar scale).

For putting it all together you may want to study the source of the webgl_clipping_advanced example.

@patrickmatte
Copy link

Is it possible to make the material clippingPlanes property work with a ShaderMaterial?

@tschw
Copy link
Contributor Author

tschw commented Oct 18, 2016

Yes, and there's even basic docs. See changes to ShaderMaterial.html in "Changed Files" of this issue and the shader code you'd have to add to your custom shader, of course. The feature has just been updated, so for the next generation of shader code see #9470.

@patrickmatte
Copy link

Looked in "Changed files" and found a mention of the boolean clipping parameter but just adding that to my shader material didn't have any effect. I couldn't find the information about the shader code that I need to add to my shader. Do I need to add a uniform for the clipping planes?

@tschw
Copy link
Contributor Author

tschw commented Oct 19, 2016

Do I need to add a uniform for the clipping planes?

Yes, as for any engine functionality in Three.js, your custom shader needs to implement support for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants