Render one object to each camera

Hi all,

I have an indeterminate number of cameras, needing to each render a single object to a render texture. I can’t use a LayerMask, as camera and rendered object creation is dynamic.

Using OnPreCull and OnPostRender I can achieve some interesting results, but it seems incredibly hacky.

Is there a better way of approaching this?

	void OnPreCull () {
		foreach (GameObject obj in allObjects) {
			obj.transform.localScale = Vector3.zero;
		}
		targetObject.transform.localScale = new Vector3(1f, 1f, 1f);
	}

	void OnPostRender ( ) {
		foreach (GameObject obj in allObjects) {
			obj.transform.localScale = new Vector3(1f, 1f, 1f);
		}
	}

I’m essentially scaling unwanted objects to 0 OnPreCull and restoring OnPostRender. I tried changing renderer.enabled, but that didn’t work so I resorted to scaling to 0.

Any help would be super :slight_smile:

Hey There,

Good new everyone! You can totally do this all in code. Just follow my steps.


##First step
the easy one


gameObject.Layer = 0 // Default Layer

Why does 0 equal the default layer?

I am glad you asked. If you look below you can see a screen shot of the layers in Unity. Each one has a number starting with 0 (the default layer). All you have to do to assign a layer is set that int value.

31795-layers.png


Second Step

a lot harder to understand


Since everyone object can only have 1 layer you have to send it an int which is easy. The thing is with Cameras they can view some, all, none, or just one layer. The question is how do you set that value in code?

There is a few things you have to understand first.

Enum Flags

An enum is a very handy thing to have. It allows you to have a list of elements where you can pick one of them. The thing is what if you want to pick one, two, or every option at the same time? By default you can’t but there is something that can. It’s an attribute called [Flag] (not going to explain attributes here since it’s not related). This allows you to pick any number of elements in a enum at the same time.

This is amazing to have but the syntax is super not very great. It’s very challenging for new users (took me a while to learn by myself) but if you follow my example below I will make it easy for you.

Bitshifting

I am going to give a super super basic example of how this works. I am going to make a guess and say you how Binary (01100110 computer numbers).

The number 87 in binary is 1010111

Why are you talking about binary I just want to know about flags?

Think of the enum as a list of binary numbers. Layer 0 is position 0, Layer 1 is position 1, and so on and so on.

Example

Our layers

Layer: 0 Name: Default

Layer: 1 Name: TransparentFX

Layer: 2 Name: Ignore Raycasts

Say we wanted a camera to only render the Default and Ignore Raycasts layers we could build a binary number.

Super Binary Number: 101

As you can see the middle number is a zero so it is not “on”. That is the second number in the row which links up to the TransparentFX layer.


How do I make a binary number?


The very easy way to do it is this

1 << 0 (default layer)
0 << 1 (Transparent FX layer)
1 << 2 (ingore raycast layer)

Now you just have to combine them by going

Camera.main.camera.cullingMask = ( 1 << 0 | 1 << 2 );

the | means or. So the camera will render layer zero (default) and layer 2 (Ignore raycasts)

The << is what we call bitshifting. There are tons of post about it on MSDN that I suggest looking up. You can do some super cool stuff with it.


End end :slight_smile:

If you have any other questions feel free to ask.

Regards,

Thank you all for the replies, it’s been very helpful!

I decided to take @agorobertbu’s approach, but had the idea of using an ‘invisible’ layer, which I could easily move objects to, rather than scaling them to zero. I did some benchmarking with 1000 randomly spawned spheres/cameras and I was seeing a 50% increase in performance.

This works perfectly and seems to have minimal overhead.

	void OnPreCull () {
		targetObject.layer = LayerMask.NameToLayer("Default");
	}
	
	void OnPostRender ( ) {
		targetObject.layer = LayerMask.NameToLayer("Invisible");
	}

Thanks for the help, working this out was fun!