Create a GameObject Image Using Render Textures

Create a GameObject Image Using Render Textures

On occasion when making a video game there is a need to display a texture that contains an image of a 3D game object. For example, to display in a toolbar, or chat message or something similar. These images could be created statically by your artist, but sometimes the image needs to change based on something happening in the game, or there is some other compelling reason where runtime creation is required.

Render Textures are a straightforward method for creating an image like this at runtime. I’ll spend the rest of this post describing how I do this sort of thing. I’m not going to do a step by step tutorial. I’ll mostly just describe the actual rendering part, and you can look at the code for the rest.

First, we set up a camera that will be used to render the object. I usually make it orthographic and fill with a solid background color, but you can really do anything you want. I also set up a single light. The camera and the light all get their own layer to work on, and the main game camera has its culling mask set to ignore that layer.

Here are the settings I usually use. Note the layer and and culling mask. Also, you’ll need to remove the Audio Listener or you get complaints from Unity about having multiples.

ObjectImageCameraProperties

To render the object, we first want to clone it. This lets us change the object’s properties at will without messing with the original.

GameObject gameObject =
  GameObject.Instantiate(prefab, position, rotation) as GameObject;
gameObject.transform.localScale = scale;

We instantiate the object at the desired position and rotation, then set the scale. These values need to be set so the object is positioned in front of the rendering camera. You can really do whatever you want here, even add multiple objects and lights and set up a whole scene.

Now we need to make sure the cloned object, and any children, are in the layer that the camera renders. So we set the layer by calling this function, passing in the cloned object.

void SetLayerRecursively(GameObject o, int layer)
{
    foreach (Transform t in o.GetComponentsInChildren<Transform>(true))
        t.gameObject.layer = layer;
}

Now we get to the fun part. We could create a render texture in the editor and assign it to the camera, but Unity provides a nice way to acquire a temporary render texture with code. I believe it was designed to simplify post processing effects, but it works equally well for our needs.

camera.targetTexture = RenderTexture.GetTemporary(128, 128, 16);

RenderTexture.GetTemporary allows us to acquire a temporary render texture from a pool maintained by Unity. Here, we’re asking for a small texture with a 16-bit depth buffer. You can check out the documentation for additional parameters and other information about how it works. It’s worth mentioning that if you ask for a format that isn’t supported the texture will come back as null without raising any errors (at least it did for me). So it’s a good idea to check for a null return value and deal with it accordingly.

We assign the temporary render texture to our camera’s target texture. This means that when we tell the camera to render, the results will go to our render texture instead of the screen.

And the render comes next.

camera.Render();

This will render everything the camera sees into our render texture. To recap:

  • We’ve set up a camera on its own layer.
  • We cloned the object we want to take a picture of.
  • We positioned the object in front of our camera, and changed the object to the render camera layer.
  • We acquired a temporary render texture and assigned it as the camera’s target texture.
  • We told the camera to render.

So at this point we should have our object rendered to the render texture. Now we need to get that object into a texture.

RenderTexture saveActive = RenderTexture.active;
RenderTexture.active = camera.targetTexture;
float width = camera.targetTexture.width;
float height = camera.targetTexture.height;

Texture2D texture = new Texture2D(width, height);
texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0);

texture.Apply();

This code saves the active render texture, makes our render texture active, creates a new Texture2D instance, then copies the pixels from the render texture to the Texture2D.

Finally, we call Texture2D.Apply to actually perforom the pixel copy.

All that’s left now is to clean up after ourselves by restoring the previously active render texture, releasing the temporary render texture we acquired earlier, and destroying the cloned game object.

RenderTexture.active = saveActive;
RenderTexture.ReleaseTemporary(objectImageCamera.targetTexture);
GameObject.DestroyImmediate(gameObject);

And that’s about it. The trickiest part is getting the object positioned and rotated properly in front of the camera so it looks good.

Here is a sample project that implements this. It includes a nice prefab for setting up the camera and everything.

So there you have it. Enjoy.

5 thoughts on “Create a GameObject Image Using Render Textures

  1. Hi,

    nice description how to render to textures in Unity, will be very usefull for me, as I am currently writing a small procedural universe engine, an this will be extremly usefull for me.
    – Render a backwards camera display in the spaceship cockpit
    – Render stars dynamically to a skybox texture (most important for me, hope that works)
    – Black Hole effects

    If you dont mind, I think you had once a tutorial about Horizon Culling. Do you still have it somewhere? I am desperately seeking it, as I am reengineering my code in Unity, and I have a bug in my Horizon Culling checks. You had a great tutorial that would help me a lot to find my mistake.

    Thanks and keep up the good work,
    Joerg

    1. Hey, sorry I didn’t see this before now – thought I had these on auto-approve.

      I do have that tutorial somewhere and I’ll try to find it and repost it soon. I looked through the Internet Archive for a bit, but that post doesn’t seem to be there for some reason.

  2. Great post. It’s insight like this that helps new developers become more efficient and skilled. I’m hope you continue to post great content.

  3. Great post, thanks a lot for sharing!

    To anyone using this, remember to call Object.Destroy() on the created Texture2D once you’re done with it (e.g. every time you create a new snapshot).

    Without this, you’ve got a memory leak that can build up to a few hundred megabytes pretty quickly if you’re creating high resolution snapshots and/or doing so frequently.

Comments are closed.

Comments are closed.