Mason, would you mind doing a rough rundown on how you managed to achieve this?
Sure thing!
I was originally planning on selling this on the Unity Asset Store, but I realize now that I don't have nearly enough time to support it, so I'm just going to release what I have. Feel free to reverse engineer it, but I warn you in advance that the programming is a bit messy (I'm by no means a professional).
Below is a link to the latest packaged version of "SpriteWrite."
SpriteWrite V0.4
---------------------------------------------------------------------
I'll give you an overview of how it works below:
The main idea is that a camera on the object generates a render texture "selfie" of itself, then displays that render texture onto a quad. The player's camera culling mask is set to see the quad, but not the 3D object.
The 3D model that we want to capture as a sprite (let's call this object the
subject) has a camera attached to it that is about a meter away, facing the subject, and pivots around the subject's center. Think of it like a selfie stick. The allowed angles arrayed around the subject are limited by script to give it that old school "snap" into discrete view angles.
This "subject camera" is set to only be able to see objects that are in a specific layer which I named "3D Models". The camera is set to orthographic projection and clear flags to "solid color" with the color being black with zero alpha ( rgba = (0,0,0,0) ) so that everything that a camera
doesn't see gets drawn as fully transparent color. A script that is placed on the subject game object will read the render bounds and set the size and proportions of the camera to capture as much of the subject in the camera's view while trimming out as many empty pixels as possible. This also includes setting the near and far clipping planes correctly.
We also have a quad that is a child of the parent subject that will rotate about the Y axis to face the player at all times (billboard) and will have its texture replaced with the render texture "selfie" that the subject generated on that frame. This quad will be set to a layer named "2D Model". Again, the
main camera can see objects on the "2D Model" layer, but not objects on the "3D Model" layer.
All this is also done only on specific frames depending on the chosen FPS (frames per second) parameter in the main script. So if the user selects 8 frames per second, the subject updates its render texture every 0.125 seconds. However, if the player moves to view the subject from another discrete view angle, then it will update the render texture, regardless of the previously mentioned schedule.
Whenever it's time to update the render texture, the subject game object is moved to the "3D Models" layer. Any frame that it's not updating the render texture, the subject is moved and kept on a layer named "Invisible" which no camera can see based on their culling mask settings. Because of this scheme, you only have the CPU/GPU load of the mesh renderer for the 1 frame where the render texture is being updated. The downfall is that you will have an extra draw call for every sprite since they each have their own unique texture.
---------------------------------------------------------------------
Caveats:
As I mentioned above, each sprite will generate a draw call since they don't share the same texture. I've considered making a sprite atlas of sorts using a large render texture created from all the selfies and having the individual sprites reference an index of that texture instead. Then many sprites can share the same texture and they would combine to one draw call. But that gets complicated
fast and I don't know that it would help performance much in many cases.
Another issue is that if all the sprites have the same "FPS" value, then all the subject objects will try to update their render textures at the same time. If you have lots of sprite objects, then you could get a big spike in CPU/GPU load on the frame that all the sprite objects are updating.
A bigger issue is what happens when two subjects are close enough to each other that when they update their render textures, one of their "selfie cameras" sees the other object. This will cause artifacts where one (or sometimes both) of the resulting sprites has parts of the other subject visible in it.
There could be multiple solutions to this problem, but I chose to create a manager script that manages which game object gets to update it's render texture each frame. Each subject sends a request to this manager when it's time to update its render texture and the manager adds that game object to a
Queue. At the end of each frame, the manager takes the game object on the top of the list and sends it a message to allow that subject to update its render texture. This effectively solves the two prior problems above since it spreads out the update over multiple frames, but it introduces another problem: no sprite object updates at the same time, which can look a bit odd sometimes. I plan on fixing this with a more sophisticated update management scheme, but what I have works for my intended purposes for now.
Anyhow, that's the rundown. Feel free to contact me if you have any other questions!