Mirrors in Unity Free version – almost works

UPDATE: see end of post for another idea…

One of the ways they make you buy Unity Pro is gimping the non-Pro version by taking away Stencil Buffer and Render-to-Texture (needed for … many (most?) of the truly interesting effect you can think of). Except … it’s only needed by old-school OpenGL programmers, because everything RtT does can be mimicked in Shaders. Stencils … maybe also?

Anyway, I’m working on a hobby project and don’t need or want the Pro version for now (I’d like to support Unity, but these days the jump in price is way too expensive, at $X000 (they price-gouge you if you’re purchasing from within EU) for a simple hobby project). If/when we’re using it at work, and I have lots of cash, sure – but for now: can’t find that much money.

For the most part, I’m happy to go without RtT for now – generally speaking, hobby projects don’t “need” those kind of effects (and if they do: maybe it’s not a hobby any more?). There’s just one exception: reflections.

Reflections

Reflections are one of 3D’s reasons to exist: they are simple, and powerful, and easy to use, and they entirely rely upon Render to Tex .. – oh, crap.

But I’ve been using this awesome free online book about writing GLSL shaders in Unity and one of the interesting sections is about demonstrating that a GLSL shader can simulate a combination of RtT and Stencils. The GLSL code is very simple, if you know GLSL already it’s just a bit odd getting your head around the “special” way that Unity requires you to write your code (and note: some of the variable names are “magic”: hard-coded into Unity with bonus features).

They use the standard starting point of cloning each of your objects to make a “mirrored” object that you can render in the “mirror universe”. Then they do a neat trick with simulating a stencil buffer, and simulating RtT through some shader-render-order fun and games. Unfortunately, it doesn’t work: In Unity 3.x, the objects “inside the mirror” still appear outside it, if you walk around behind it: this is the point of what the mirror was supposed to prevent (and it so nearly works – Unity renders the “behind” objects with a strange alpha-effect that makes me wonder if it’s a built-in anti-Free-version hack, or if there’s something odd about the default cameras)

The book’s sample code does this:

  1. Javascript that makes any “reflected” object track its peer object (so long as they both have meshRenderers)
  2. Shader for the mirror itself to do cool and clever stuff
  3. Shader (very simplistic) for the “reflected” objects to render them “inside the mirror but not outside” (which doesn’t quite work, as noted above)

I had to add a few things to use this in practice:

  1. Convert the script to C#, so I get auto-completion when referencing it
  2. Write some recursive methods to add the script + its parameters to the cloned Player object when player comes near mirror
  3. Write some recursive methods to DESTROY EVERY COMPONENT on the “cloned” player, except for: MeshFilter, MeshRenderer (otherwise your mouselook script, your physics RigidBody etc all exist on the clone, and weird and insane freaky sh*t goes down)
  4. Modify the simple “reflected objects” shader to support standard texture map
  5. Add a post-attach method that when you mirror a dynamic object (e.g. the player) it grabs the texture from the original and assigns it to the bonus paramter on the mirrored-object shader (the book sample just gave mirrored objects a single colour)

Does it work?

Well, almost. Very nearly.

All of which is great, except for two major bugs:

  1. the bizarre semi-transparent colour of objects if you walk behind the mirror (e.g. if another room in your building lies behind the mirror) – they shouldn’t be rendering at all, and I’ve double-checked the book’s approach, it all looks like it should work fine to me (or: not at all. Not “partly work”) :(
  2. the render-order for items in the mirror is FUBAR. They occlude each other back-to-front based on some weird algorithm. The back-to-front occlusion is part of the magic, IIRC, but … it seems to go wrong in the final render :(

For now, since this is a hobby project, I’ve removed everything from the mirror-universe except the player. So these are mirrors that don’t show the room you’re in – they only show you! Looks a bit sucky, but is good enough for now.

If I find a way to fix the bugs, it would be great. Nowhere near as neat (or as performant!) a solution as getting Unity Pro (which I’d certainly encourage you to buy instead of using this poor-man’s approach), but plenty good enough for me to continue my hobbying…

UPDATE: a new technique for Render to Texture

I kicked myself when I saw this, which is a pretty obvious way of simulating RtT. I’m already doing various tricks with multiple, overlapping cameras – so why didn’t I think of this?

Although I fully understand all the techniques above (and can easily write them from scratch), I *don’t* understand how the occlusion is being simulated/dissimulated via the shaders — because if I did, I’d have tried this:

  1. Create a slave-cam in mirror-world (I’ve already written the script to create a slave-player in mirror-world – doing the same with camera is trivial)
    • …in Update(), slave-cam re-sets its absolute position to be the opposite side of the mirror to player-cam, taking into account player-cam’s current/new position
  2. Draw slave-cam AND player-cam, both full-screen, but depth-sorted (set the camera.depth) so slave-cam is beneath (lower depth number)
    • …yes, Unity has “depth” upside-down, the variable should be called “height”
  3. On slave-cam, “dis-render” everything on slave-cam’s side of the mirror (disable occlusion entirely)
  4. On slave-cam, “dis-render” the mirror itself (so you can see through it!)
  5. On player-cam, cut-out (using the link above) the area where mirror appears on-screen, so that slave-cam’s view shows through

As a thought-experiment, that Just Works. Which probably means it’s fundamentally wrong somehow ;). Sadly, I’m out of time for working on this now (remember: hobby project), and I don’t want to spend more time faffing with mirrors, but to anyone else interested, I’d say this technique is definitely worth a try.

UPDATE 2: a new way

I’ve had partial success with a modified version of the above technique. See that post for details (it took fewer steps than I expected, in some ways)

3 thoughts on “Mirrors in Unity Free version – almost works

  1. Pingback: Creating real-time Mirrors in Unity Free (attempt 2) | T=Machine

  2. Kit

    Hey adam, the ‘a new way’ link appears to be dead, and I’m interested in what you came up with :)

Leave a Reply

Your email address will not be published. Required fields are marked *