private static Vector2 RenderToTexture2D(string path, Shape shape, float pixelsPerUnit = 100) { // reset the shape's rotation Quaternion oldRotation = shape.transform.rotation; shape.transform.rotation = Quaternion.identity; // get the bounds of our shape and all its children Bounds bounds = shape.GetShapeBounds(); Vector2 size = shape.GetShapePixelSize(pixelsPerUnit: pixelsPerUnit); int w = (int)size.x; int h = (int)size.y; // get all the shapes in draw order List <Shape> shapes = shape.GetShapesInDrawOrder(); // if the shape is a UI component, we need to set up the canvas in a way // that the camera can point to the image only without the UI // components moving around based on the camera Canvas canvas = shape.GetComponentInParent <Canvas>(); int oldCanvasLayer = -1; RenderMode oldRenderMode = 0; List <GraphicState> modifiedGraphics = null; if (canvas) { oldCanvasLayer = canvas.gameObject.layer; canvas.gameObject.layer = 31; oldRenderMode = canvas.renderMode; canvas.renderMode = RenderMode.WorldSpace; // without a way to selectively show just the shape we want, this is // the only way I can think of to do it. even this won't work if the // user has a UI component not found by this function. modifiedGraphics = DisableUIGraphics(canvas); } // make a new render texture RenderTexture rt = new RenderTexture(w, h, 32, RenderTextureFormat.ARGB32); rt.filterMode = FilterMode.Point; rt.autoGenerateMips = false; rt.Create(); // set up the camera to point exactly at the object's bounds and set its // culling layer to show only layer 31 // note that if the user has anything on layer 31 then it will also // show up in the png, but in that event they can just move it away Camera cam = new GameObject().AddComponent <Camera>(); cam.backgroundColor = new Color(1, 1, 1, 0); cam.clearFlags = CameraClearFlags.SolidColor; cam.transform.position = bounds.center; cam.transform.position -= new Vector3(0, 0, 10); cam.orthographic = true; cam.orthographicSize = bounds.extents.y; cam.aspect = bounds.size.x / bounds.size.y; cam.targetTexture = rt; cam.cullingMask = 1 << 31; // make the render texture active so calls to Texture2D.ReadPixels() will // read from it RenderTexture oldRT = RenderTexture.active; RenderTexture.active = rt; // draw each shape with blending turned off, and layer them on top of each // other with blending between shapes but not between a shape and the // camera's background color, which is what would happen if we just let the // camera render all of them - the camera's background color affects the // color of semi-transparent pixels even if the background color has an // alpha value of 0. this is because the shader's blending is trying to // alias against the background color, but in the png we don't want that. // there's no shader option I'm aware of that would be able to blend between // shapes but not between a shape and the background color, so that's why // we have to do it manually. Texture2D dstTex = null; foreach (Shape s in shapes) { if (canvas) { Graphic g = s.GetComponent <Graphic>(); g.enabled = true; if (s.GetComponent <Mask>() != null) { foreach (GraphicState gs in modifiedGraphics) { if (gs.graphic == g && gs.showMaskGraphic) { s.GetComponent <Mask>().showMaskGraphic = true; } } } } if (dstTex == null) { dstTex = s.DrawToTexture2D(cam, w, h); } else { Texture2D srcTex = s.DrawToTexture2D(cam, w, h); BlendTextures(dstTex, srcTex); DestroyImmediate(srcTex); } if (canvas) { if (s.GetComponent <Mask>() == null) { s.GetComponent <Graphic>().enabled = false; } else { s.GetComponent <Mask>().showMaskGraphic = false; } } } // put the old render texture back RenderTexture.active = oldRT; // grab the sprite's pivot point based on the top object's location // relative to its children Vector2 pivot = shape.GetPivot(); // restore the shape's rotation shape.transform.rotation = oldRotation; // restore the canvas if (canvas) { canvas.gameObject.layer = oldCanvasLayer; canvas.renderMode = oldRenderMode; foreach (GraphicState gs in modifiedGraphics) { gs.graphic.enabled = true; if (gs.hasMask) { gs.graphic.GetComponent <Mask>().showMaskGraphic = gs.showMaskGraphic; } } } // save the png byte[] bytes = dstTex.EncodeToPNG(); System.IO.File.WriteAllBytes(path, bytes); // clean up DestroyImmediate(dstTex); DestroyImmediate(cam.gameObject); DestroyImmediate(rt); return(pivot); }