-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
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
Expose objects' frustum culled state #15339
Comments
What is your use case for such a feature? |
Yeeees! I need this feature tooooo. |
Beware that the frustum culling test is conservative, so there may be objects which pass the cull test, yet are not in view. |
What you are describing sounds an awful lot like a "visibility set". I have an explicit frustum-based visibility set in my engine for similar reasons - to avoid extra computation. When computation becomes a bottleneck - you should also look towards a different solution for building the visibility set in the first place, such as using a spatial index. |
@Mugen87 In my case I wanted to do some collision detection, and I wanted to start with objects in the view instead of all objects in the scene. I figured that if the renderer is already computing this and exposed it then it'd prevent me from computing it a second time outside of the renderer. |
I think you should just use some decent physics engine with a decent broad-phase. Just my opinion. Also, make sure you allow bodies to sleep - it reduces number of checks. |
@Usnul That may be a bit much for my case. I'm working on a 3D editing program (business-specific) where I simply want to snap some points to other points, and figured I could at least iterate only the points that are in view. Code for finding the objects in view looks like follows (code ommited, but you get the idea): const frustum = new THREE.Frustum
const projScreenMatrix = new THREE.Matrix4
const {camera} = this.props
projScreenMatrix.multiplyMatrices( camera!.projectionMatrix, camera!.matrixWorldInverse )
frustum.setFromMatrix( projScreenMatrix )
this.getMarkersInFrustum(frustum)
// ...
getMarkersInFrustum(frustum: THREE.Frustum): Marker[] {
return this.markers
.filter(marker =>
marker.children.some(n => {
let hasMeshInView = false
const hasGeom = hasGeometry(n) // f.e. n.geometry
if (hasGeom && frustum.intersectsObject(n))
hasMeshInView = true
n.traverse(n => {
const hasGeom = hasGeometry(n)
if (hasGeom && frustum.intersectsObject(n))
hasMeshInView = true
})
return hasMeshInView
})
)
} then after that I'm checking to see which marker is closest by checking distance from the edited marker It's not terribly complicated, but seems like Three.js could expose this information which it already has. |
In my current case I'm doing the in-view detection only once when the user stops dragging an object, and not every frame, so performance is not as important in my current case, but I could see this being important if the detection needs to happen every frame, especially if there's many objects in the scene. |
@trusktr I run tens of queries per frame on my spatial index in the game I'm working on and it amounts to a couple of milliseconds, that's considering that my dataset has about 20,000 objects. I also run a picking ray query from the pointer every frame and most of the time it doesn't even register when i do profiling as it runs in trivial amount of time. A decent spatial index gives you O(log(n)) complexity for most fixed-volume queries and similar for rays, compared to O(n) for naive traversal a-la For the sake of completeness - i should also mention GC considerations. Most of my hot code generates little to no garbage, most of three.js is written in that way too, the reason being - garbage accumulates and collection cycles cause hick-ups in your frame-rate. |
I guess you can currently do this hack: object1.onBeforeRender = function () { this.userData.inView = true } );
// render loop
scene.traverse( function ( child ) { child.userData.inView = false } );
renderer.render( scene, camera ); But I agree with @Usnul. I would use cannon.js or something. |
Nice workaround, but doesn't seem to work on some GLTF files. It works on a scene I exported from Blender 2.8, but not firing onBeforeRender using this example:
However this code worked correctly as it doesn't rely on the onRender function and traverses the full GLTF children:
|
I'm afraid this line does not work since |
In case someone is interested:
I got a patch for gltf loader that works with the new js zip version.
|
A solution based on a spatial index like described in #15339 (comment) seems more appropriate for the OPs use case. If for some reasons users need all objects inside the view frustum as a query, they have to implement this on app level. The code of #15339 (comment) is a good start. For a more precise solution, a stricter test is required anyway. |
Another use case here: Decide which tiles to subdivide in a web mercator map quadtree for level-of-detail control. Think Google Earth. Simple approach with 4x - 10x overhead, depending on frustom: Subdivide all tiles 'close' to the camera |
Have you tried creating your own I'm aware it would be 2x the amount of traverses, but should be as least faster than |
Description of the problem
The WebGLRenderer already calculates whether an object is frustum culled, using an internal Frustum.
It'd be nice if this information was exposed so that we don't have to duplicate the Scene traversal and calculations with our own Frustum outside of the renderer.
A couple ideas:
onFrustumCullChange
property that isundefined
ornull
by default. If it is set to a function, then when the frustum culling for the object changes the function can be called with true or false.onFrustumCullChange
idea.Any other ways to do it without requiring a second traversal and calculations?
Three.js version
Browser
OS
The text was updated successfully, but these errors were encountered: