- Home /
Checking for transparency in texture when clicking on GameObject
Hello community,
My game world is composed of many billboarded quads* displaying textures that contain transparency. All of these quads have colliders, and I'm using NGUI to handle my UI notifications (clicks, drags, etc...).
I've modified NGUI so that when UI happens, my own raycasting method gets called. This provides a list of GameObjects that are candidates to handle the UI. I now need to go through each GameObject and determine if the click point corresponds to a transparent pixel on the texture (if so, go on to the next candidate).
I have all of the math in place to know which texture pixel I need to test, but am having a lot of difficulty actually getting the pixel value. I'm targeting mobile, therefore memory is at a premium. Allowing texture reading/writing disallows compression, which exceeds memory constraints if all my textures are left uncompressed.
Some notes:
I do have a working solution: for each texture I want to test, I blit it to a RenderedTexture. I them copy the RenderedTexture to an uncompressed Texture2D and read the pixel value. This works, but bumps my render time from ~0.2 ms to ~15ms on desktop (I'm sure it's far worse on mobile). Specifically, the more objects there are to test, the more times I have to repeat the above steps in a single frame.
I tried using Graphics.DrawMeshNow, but was never able to catch the output (my RenderedTexture was always empty). I'm guessing DrawMeshNow is async, and I was trying to read the RenderedTexture immediately.
Related to the note above, I would really like a synchronous solution. I essentially trick NGUI into thinking it hit a different GameObject than it did. I'm afraid that if I wait a frame to modify NGUI's target, something screwy could have already happened.
One thought I had was to render all of my candidate GameObjects to a single RenderTexture using proper depth sorting. I could use a custom shader to give the opaque parts of each texture a unique color. Then it would simply be a matter of testing what color a pixel held at a particular location. This has the advantage of at least stabilizing the performance hit as the list of candidates grows. As with attempting to use Graphics.DrawMeshNow(), I still need to do this in a blocking/synchronous manner.
If you've gotten this far, thanks for getting through the wall of text! Also, thanks for any insight you can provide.
*The GameObjects are actually a little more complex than billboarded quads, but that shouldn't impact anything I've discussed. Also, using Sprites is out, because I need functionality that sprites don't provide.
EDIT: I now have a more targeted version of this question here: http://answers.unity3d.com/questions/793392/does-graphicsdrawmeshnow-block-the-thread-until-re.html
If anyone has thoughts related specifically to Graphics.DrawMeshNow, the other question is probably the more appropriate place to answer/comment.
No brilliant ideas, but some approaches to consider...most assume you can live with some imprecision:
You don't have to have use the original textures for your hit testing. You could use 1/4 scale (1/16) size or 1/8 scale (1/64) size duplicates of the images that are read/write enabled.
In using duplicates, you don't need all the data...one byte or even one bit per pixel will do.
You don't need a texture to do the calculation. You can store the transparency data is a binary TextAsset.
There are compression algorithms that allow you to index into the data.
You don't need a sprite to use a polygon collider.
While it's not my area of expertise, it should be possible to build meshes from images with transparency in a modeling program.
Consider asking this on Unity Forums. If you do, include a link to the question here.
Hi Robertbu, thanks for your insight!
You are correct that some imprecision is certainly acceptable. I have considered creating a click mask, as you suggest. I agree that I can get away with 1 bit/pixel, or even less. The only downside is having to maintain this click mask for each texture (there's over a thousand). Therefore, I think this is my fallback approach if I can't solve the performance issue with the current approach.
Thanks for the suggestion of a polygon collider. For a lot of the 2D features, the docs always mention sprites, so I'm never sure how tight the coupling is. I think I like the click mask approach more than generating meshes, but it's good to know that there are options.
Thanks again for your help.
Answer by Pangamini · Sep 18, 2014 at 08:03 AM
So you know which texture and which pixel to test, but can't read the texture? What if you tried to render only a single quad with this texture, mapping it so the hovered pixel is stretched all over it, render this quad into 1x1 renderTarget and read that?
Hi Panga$$anonymous$$i, I like the idea of rendering a single pixel stretched. I guess the question still remains, is it possible to render a mesh immediately? When I use Graphics.Draw$$anonymous$$eshNow(), my RenderedTexture isn't updated. I'm not sure if I'm supposed to be looking for a callback (i.e., OnSomething) or not.
Thanks for your suggestion.