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

Feature request: It would be great to add a feature to render parts of the scene selectively. #5562

Closed
OlexandrPopov opened this issue Nov 3, 2014 · 32 comments

Comments

@OlexandrPopov
Copy link

I mean something like Camera.cullingMask in Unity http://docs.unity3d.com/ScriptReference/Camera-cullingMask.html

@mrdoob
Copy link
Owner

mrdoob commented Nov 3, 2014

Interesting...
How do you suggest the API should be like?

@OlexandrPopov
Copy link
Author

I suggest you to use bitmasks.
Let's assume every object is in a layer. A layer is just a number in the range [0...31], because the bitwise operators work with 32-bit integers.

The implementation might look like this:

// THREE.Object3D constructor
this.layer = 0; // by default object is in the first layer

As for cameras they need to keep state of individual layer. Let's use bitmasks for this purpose.

// THREE.Camera costructor
this.cullingMask = -1; // -1 is the integer that is composed completely of 1 bits, so by default all the layers are visible from camera.

Also we need to check status of individual layer by ANDing them with cullingMask. I am not familiar with your code, but in my opinion it can be done in the projectObject function of the THREE.WebGLRenderer.

if(someCamera.cullingMask & (1 << someObject.layer))  {

    // render someObject
}
else  {
    // do not render someObject
}

The usage API might look like this:

Suppose there are 3 objects and 2 cameras

var object1 = new THREE.Object3D();
var object2 = new THREE.Object3D();
var object3 = new THREE.Object3D();

// I'm using THREE.Camera to abstract away from perspective and orthographic cameras
var camera1 = new THREE.Camera();
var camera2 = new THREE.Camera();

By default all the objects are in the first layer and visible from both the cameras.
But we want object1 and object2 to be invisible from the camera2.
To do that we need to put these object in a separate layer and extract this layer from the camera2.

// put object1 and object2 in the second layer
object1.layer = 1;
object2.layer = 1;

// create a flag for second layer which is represented by a sequence of bits
var lasyer2Flag = 1 << 1;

// clear the corresponding flag
camera2.cullingMask &= ~lasyer2Flag;

Now the camera2.cullingMask is 11111111111111111111111111111101. The second bit is 0, which means the layer2 is invisible from camera2.
We can easily make layer2 visible from camera2 by ORing lasyer2Flag with camera2.cullingMask

camera2.cullingMask |= lasyer2Flag;

@mrdoob
Copy link
Owner

mrdoob commented Dec 15, 2014

I think using bitwise operations is the way to go. API wise however this is too hard for the user.

How about something like this...

object1.layer = 0;
object1.layer = 1;
camera.layers.hide( 1 );

@WestLangley I feel we have talked about this before. Rings a bell?

@WestLangley
Copy link
Collaborator

I think using bitwise operations is the way to go. API wise however this is too hard for the user.

I agree.

object1.layer = 0;
object2.layer = 1;
camera.layers.hide( 1 ); // also hideAll(), showAll();

I'm not sure about show( 1 ). It is needed, but it can be interpreted as "show 1 also" or "show 1 only".

I'm also not sure if layers should be a property of the camera or of the renderer.

Rings a bell?

https://github.com/mrdoob/three.js/search?utf8=✓&q=bitmask&type=Issues

@mrdoob
Copy link
Owner

mrdoob commented Dec 15, 2014

How about...

object1.layer = 0;
object1.layer = 1;
camera.layers.disable( 1 ); // disableAll(), enableAll();

I'm also not sure if layers should be a property of the camera or of the renderer.

Hmmm, maybe the renderer...?

@WestLangley
Copy link
Collaborator

Hmmm, maybe the renderer...?

Maybe the scene?

object1.layer = 0;
object2.layer = 1;
scene.layers.disable( 1 ); // enable( 1 ), disableAll(), enableAll(), isDisabled( 1 ), isEnabled( 1 )

@OlexandrPopov
Copy link
Author

API wise however this is too hard for the user.

I know the bitwise operators are hard for modern programmers. So we need a high level API. Let me think more about it.

I'm also not sure if layers should be a property of the camera or of the renderer.

I'm sure layers should be property of the camera. Because the main goal of the feature to make objects visible from one camera and invisible from another.

@WestLangley
Copy link
Collaborator

I'm sure layers should be property of the camera.

From a design standpoint, neither the camera nor the renderer should need to know anything about the scene. I think layers should be a property of the scene. No?

@OlexandrPopov
Copy link
Author

It seems I'm missing the point.
Let's suppose we added it to the scene. And what is this property for? How to check whether an object is visible or not?

@WestLangley
Copy link
Collaborator

scene.layers.disable( 1 ); // enable( 1 ), disableAll(), enableAll(), isDisabled( 1 ), isEnabled( 1 )
renderer.render( scene, camera );

We are just debating the issue here. Your opinion is welcome.

@OlexandrPopov
Copy link
Author

Just trying to understand how to use layers if it's a property of the scene.

@mrdoob
Copy link
Owner

mrdoob commented Dec 15, 2014

Well, in Blender seems like the layers concept is in the scene.

@OlexandrPopov
Copy link
Author

Here is how it works in Unity https://www.youtube.com/watch?v=iYaOkbC58W4

@mrdoob
Copy link
Owner

mrdoob commented Dec 15, 2014

Hmmm, yep! I like that use case ^^

@OlexandrPopov
Copy link
Author

Certainly the layers belong to the scene. And it's possible to use them not only in scope of this feature. For instance you can use layers with lights, raycasts etc. Here is one more sample of layers usage in Unity https://www.youtube.com/watch?v=HR-Tf_6uzwc

@WestLangley
Copy link
Collaborator

Hmmm... the behavior is going to get a bit complicated, I'm afraid... There are a number of cases...

var parent = new THREE.Object3D(); // this, itself, is never visible because it has no geometry
parent.visible = false; // or true
parent.layer = 0; // or 1
scene.add( parent );

var child = new THREE.Mesh( geometry, material );
child.visible = true; // or false
child.layer = 1; // or 0
parent.add( child );

@mrdoob
Copy link
Owner

mrdoob commented Dec 15, 2014

Hmmm... It's actually not too bad. Yes, I would expect that if I set parent.visible to false the descendants will get invisible too. What this will now allow is to hide the parent while the descendants are still visible.

If anything, this collides with material.visible...

@WestLangley
Copy link
Collaborator

Is this the intent? A renderable object is visible only if

  1. object.visible = true, and
  2. all the object's parents have .visible = true, and
  3. object.layer is enabled, and
  4. object.material.visible = true. // and its opacity > 0

@mrdoob
Copy link
Owner

mrdoob commented Dec 15, 2014

Yes. And if I had to remove one of those, I would remove the last one, so that way all the visibility logic is in the scene graph and not in the material.

@WestLangley
Copy link
Collaborator

Yeah, you never have liked the material.visible property. : - )

@mrdoob
Copy link
Owner

mrdoob commented Dec 15, 2014

;)

@mrdoob
Copy link
Owner

mrdoob commented Dec 16, 2014

For instance you can use layers with lights, raycasts etc.

Ah yeah, I remember discussing this for lights. Implementing this for lights will be more difficult though. But starting from the camera should be easy.

@OlexandrPopov
Copy link
Author

It's would be user friendly to use the similar API for lights in future.

@tsone
Copy link

tsone commented Jan 18, 2015

Is somebody implementing this? If not, I could give it a shot.

Personally, I'd follow Unity's design. However instad of object.layerMask I'd have object.layerBits to signify it's a bitfield. A mask is the other half of the masking operation. I think camera.cullingMask is a valid name and I'd use that.

Object will be culled if (object.layerBits & camera.cullingMask) === 0. I assume initially object.layerBits = 0x00000001 and camera.cullingMask = 0xFFFFFFFF (=all layers enabled).

I'm not sure however if Unity culls the whole branch (=the object in question, its children, grandchildren etc.), but I'd rather have the behavior to cull the entire branch -- at least initially. Later we could have material.layerBits for material-based/object-only culling -- but I don't really want to think about this right now...

@tsone
Copy link

tsone commented Jan 18, 2015

API-wise, I don't want the proposed utility functions or classes because I think it bloats the code. These could be added later if somebody requests them. I however do understand that artists may not know how bitmasking works, but anyway the complexity of this feature is at the level that any artist attempting to use it should know what they are doing and try to study how bitmasks really work.

tsone pushed a commit to tsone/three.js that referenced this issue Jan 19, 2015
…erBits to set objects to layers. Cubemap dynamic2 example to use layers. New example to demostrate layers with shadowmaps. Answers feature request mrdoob#5562
@tsone
Copy link

tsone commented Jan 19, 2015

Here you go: #5931

@mrdoob
Copy link
Owner

mrdoob commented Jan 19, 2015

I'm not sure however if Unity culls the whole branch (=the object in question, its children, grandchildren etc.), but I'd rather have the behavior to cull the entire branch -- at least initially.

I don't think we should be culling the whole branch. That's what object.visible = false is for.

@tsone
Copy link

tsone commented Jan 20, 2015

@mrdoob Hmm, how about having both Object3D.layerMask and Material.layerMask just like with visible?

@mrdoob
Copy link
Owner

mrdoob commented Jan 20, 2015

Lets first sort out object level first :)

@Yugloocamai
Copy link
Contributor

Yugloocamai commented Jun 14, 2018

It looks like layers are ignored when using toJSON(). This is a total bummer, since we're using layers and importing/exporting lots of scenes...

@mrdoob
Copy link
Owner

mrdoob commented Jun 16, 2018

@Yugloocamai That should be easy to fix. Do you mind taking a look @Mugen87?

@Mugen87
Copy link
Collaborator

Mugen87 commented Mar 17, 2021

Since THREE.Layers (formerly THREE.Channels) is part of the project since r73(see d36df79), this issue can be closed.

@Mugen87 Mugen87 closed this as completed Mar 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants