- Home /
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 :)
Actually, that seems pretty clever. Dunno how well it'll scale depending on what all else you're doing, but kudos for the inventive approach. I was gonna suggest disabling renderers as I was reading your question. Perhaps renderer visibility is already established by the time you get to PreCull().
Could you store render results between renderings, then combine the results at the end of each frame? This method might allow a single camera to be used, changing parameters (and objects' layers) between manual render calls to achieve whatever multiple cameras gives you. I don't know what you're trying to achieve, so I'm guessing.
Best of luck,
I agree with @AlwaysSunny's comments. Inventive approach. I just ran a quick test, and enabling/disabling the renderer does not work, but your approach does. It seems to me that you are going to too much work. That you want to set the localScale of all the objects to (0,0,0) in the beginning (or when they are created), then do:
void OnPreCull () {
targetObject.transform.localScale = new Vector3(1f, 1f, 1f);
}
void OnPostRender ( ) {
targetObject.transform.localScale = new Vector3(0f, 0f, 0f);
}
}
...so each object is enabled just for the camera that is going to render it without touching any other game objects.
Answer by thehen2 · Aug 31, 2014 at 02:08 PM
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!
Answer by BMayne · Aug 31, 2014 at 01:09 AM
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.
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 :)
If you have any other questions feel free to ask.
Regards,
@B$$anonymous$$ayne - Your approach will work as long as he has less than around 28 or so cameras. There is a maximum of 31 layers, but some of them are taken and likely cannot be used (like 'nothing'). His question said "indeter$$anonymous$$ate number of cameras".
@robertbu if you are using more then 24 cameras in a scene you are going about things very wrong. Rendering is what kills a game's frame rate and cameras are by no means quick.
Thanks for the detailed reply, whilst not specific to my problem I learnt a lot and it will be useful for me in the future. For my particularly fringe requirements, each puzzle requires a camera. As puzzle quantity per scene is indeter$$anonymous$$ate, and I may need more layers further into development, the typical layer mask approach wouldn't be suitable. Performance isn't an issue as cameras will only be enabled when required.
I've posted a solution in the comments above, but I'd advise future readers to carefully check their requirements as this answer will fulfil 95% of requirements.