private static bool CaptureViews( Transform root, BillboardImposter imposter, Snapshots[] snapshots, Transform lightingRoot, Shader albedoBake, Shader normalBake, ComputeShader processCompute) { Vector3 originalScale = root.localScale; root.localScale = Vector3.one; var prevRt = RenderTexture.active; var baseAtlas = RenderTexture.GetTemporary(_atlasResolution, _atlasResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); baseAtlas.enableRandomWrite = true; baseAtlas.Create(); var packAtlas = RenderTexture.GetTemporary(_atlasResolution, _atlasResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); packAtlas.enableRandomWrite = true; packAtlas.Create(); var tempAtlas = RenderTexture.GetTemporary(baseAtlas.descriptor); tempAtlas.Create(); var frameReso = _atlasResolution / imposter.Frames; var frame = RenderTexture.GetTemporary(frameReso, frameReso, 32, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); frame.enableRandomWrite = true; frame.Create(); var packFrame = RenderTexture.GetTemporary(frameReso, frameReso, 32, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); packFrame.Create(); var tempFrame = RenderTexture.GetTemporary(frame.descriptor); tempFrame.Create(); var frameResUpscale = frameReso * 4; var superSizedFrame = RenderTexture.GetTemporary(frameResUpscale, frameResUpscale, 32, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); superSizedFrame.enableRandomWrite = true; superSizedFrame.Create(); var superSizedFrameTemp = RenderTexture.GetTemporary(superSizedFrame.descriptor); var superSizedAlphaMask = RenderTexture.GetTemporary(superSizedFrame.descriptor); superSizedAlphaMask.Create(); imposter.BaseTexture = new Texture2D(baseAtlas.width, baseAtlas.height, TextureFormat.ARGB32, true, true); imposter.PackTexture = new Texture2D(baseAtlas.width, baseAtlas.height, TextureFormat.ARGB32, true, true); ComputeBuffer minDistancesBuffer = new ComputeBuffer(frame.width * frame.height, sizeof(float)); ComputeBuffer maxDistanceBuffer = new ComputeBuffer(1, sizeof(float)); const int layer = 30; var clearColor = Color.clear; var camera = new GameObject().AddComponent <Camera>(); camera.gameObject.hideFlags = HideFlags.DontSave; camera.cullingMask = 1 << layer; camera.clearFlags = CameraClearFlags.SolidColor; camera.backgroundColor = clearColor; camera.orthographic = true; camera.nearClipPlane = 0f; camera.farClipPlane = imposter.Radius * 2f; camera.orthographicSize = imposter.Radius; camera.allowMSAA = false; camera.enabled = false; var frameCount = imposter.Frames * imposter.Frames; var originalLayers = new Dictionary <GameObject, int>(); StoreLayers(root, layer, ref originalLayers); var originalLights = new Dictionary <Light, bool>(); var customLit = lightingRoot != null; if (customLit) { var lights = FindObjectsOfType <Light>(); for (var i = 0; i < lights.Length; i++) { if (!lights[i].transform.IsChildOf(lightingRoot)) { if (originalLights.ContainsKey(lights[i])) { continue; } originalLights.Add(lights[i], lights[i].enabled); lights[i].enabled = false; } else { lights[i].enabled = true; if (!originalLights.ContainsKey(lights[i])) { originalLights.Add(lights[i], false); } } } } var tempMinMaxRT = RenderTexture.GetTemporary(frame.width, frame.height, 0, RenderTextureFormat.ARGB32); tempMinMaxRT.Create(); Graphics.SetRenderTarget(tempMinMaxRT); GL.Clear(true, true, Color.clear); camera.clearFlags = CameraClearFlags.Nothing; camera.backgroundColor = clearColor; camera.targetTexture = tempMinMaxRT; var min = Vector2.one * frame.width; var max = Vector2.zero; for (var i = 0; i < frameCount; i++) { if (i > snapshots.Length - 1) { Debug.LogError("[Imposter] snapshot data length less than frame count! this shouldn't happen!"); continue; } //position camera with the current snapshot info var snap = snapshots[i]; camera.transform.position = snap.Position; camera.transform.rotation = Quaternion.LookRotation(snap.Ray, Vector3.up); //render alpha only Shader.SetGlobalFloat("_ImposterRenderAlpha", 1f); camera.RenderWithShader(albedoBake, ""); camera.ResetReplacementShader(); //render without clearing (accumulating filled pixels) camera.Render(); //supply the root position taken into camera space //this is for the min max, in the case root is further from opaque pixels var viewPos = camera.WorldToViewportPoint(root.position); var texPos = new Vector2(viewPos.x, viewPos.y) * frame.width; texPos.x = Mathf.Clamp(texPos.x, 0f, frame.width); texPos.y = Mathf.Clamp(texPos.y, 0f, frame.width); min.x = Mathf.Min(min.x, texPos.x); min.y = Mathf.Min(min.y, texPos.y); max.x = Mathf.Max(max.x, texPos.x); max.y = Mathf.Max(max.y, texPos.y); } camera.clearFlags = CameraClearFlags.SolidColor; camera.backgroundColor = clearColor; camera.targetTexture = null; //now read render texture var tempMinMaxTex = new Texture2D(tempMinMaxRT.width, tempMinMaxRT.height, TextureFormat.ARGB32, false); RenderTexture.active = tempMinMaxRT; tempMinMaxTex.ReadPixels(new Rect(0f, 0f, tempMinMaxRT.width, tempMinMaxRT.height), 0, 0); tempMinMaxTex.Apply(); var tempTexC = tempMinMaxTex.GetPixels32(); //loop pixels get min max for (var c = 0; c < tempTexC.Length; c++) { if (tempTexC[c].r != 0x00) { var texPos = Get2DIndex(c, tempMinMaxRT.width); min.x = Mathf.Min(min.x, texPos.x); min.y = Mathf.Min(min.y, texPos.y); max.x = Mathf.Max(max.x, texPos.x); max.y = Mathf.Max(max.y, texPos.y); } } DestroyImmediate(tempMinMaxTex, true); RenderTexture.ReleaseTemporary(tempMinMaxRT); //rescale radius var len = new Vector2(max.x - min.x, max.y - min.y); var maxR = Mathf.Max(len.x, len.y); var ratio = maxR / frame.width; imposter.Radius = imposter.Radius * ratio; camera.farClipPlane = imposter.Radius * 2f; camera.orthographicSize = imposter.Radius; Vector3 scaleFactor = new Vector3(root.localScale.x / originalScale.x, root.localScale.y / originalScale.y, root.localScale.z / originalScale.z); imposter.Offset = Vector3.Scale(imposter.Offset, scaleFactor); snapshots = UpdateSnapshots(imposter.Frames, imposter.Radius, root.position + imposter.Offset, imposter.IsHalf); for (var frameIndex = 0; frameIndex < frameCount; frameIndex++) { if (frameIndex > snapshots.Length - 1) { Debug.LogError("[Imposter] snapshot data length less than frame count! this shouldn't happen!"); continue; } var snap = snapshots[frameIndex]; camera.transform.position = snap.Position; camera.transform.rotation = Quaternion.LookRotation(snap.Ray, Vector3.up); clearColor = Color.clear; //target and clear base frame Graphics.SetRenderTarget(superSizedFrame); GL.Clear(true, true, clearColor); Graphics.SetRenderTarget(superSizedFrameTemp); GL.Clear(true, true, clearColor); camera.targetTexture = superSizedFrameTemp; camera.backgroundColor = clearColor; if (!customLit) { Shader.SetGlobalFloat("_ImposterRenderAlpha", 0f); camera.RenderWithShader(albedoBake, ""); camera.ResetReplacementShader(); } else { camera.Render(); } camera.targetTexture = superSizedAlphaMask; camera.backgroundColor = clearColor; camera.Render(); Graphics.Blit(superSizedAlphaMask, superSizedFrame, _processingMat, 3); Graphics.Blit(superSizedFrame, superSizedAlphaMask); _processingMat.SetTexture("_MainTex", superSizedFrameTemp); _processingMat.SetTexture("_MainTex2", superSizedAlphaMask); _processingMat.SetFloat("_Step", 1f); Graphics.Blit(superSizedFrameTemp, superSizedFrame, _processingMat, 1); Graphics.SetRenderTarget(frame); GL.Clear(true, true, clearColor); Graphics.Blit(superSizedFrame, frame); Graphics.SetRenderTarget(superSizedFrameTemp); GL.Clear(true, true, clearColor); Graphics.SetRenderTarget(superSizedFrame); GL.Clear(true, true, clearColor); clearColor = new Color(0.0f, 0.0f, 0.0f, 0.5f); camera.targetTexture = superSizedFrame; camera.backgroundColor = clearColor; camera.RenderWithShader(normalBake, ""); camera.ResetReplacementShader(); Graphics.SetRenderTarget(packFrame); GL.Clear(true, true, clearColor); Graphics.Blit(superSizedFrame, packFrame); Graphics.SetRenderTarget(tempFrame); GL.Clear(true, true, Color.clear); int threadsX, threadsY, threadsZ; CalcWorkSize(packFrame.width * packFrame.height, out threadsX, out threadsY, out threadsZ); processCompute.SetTexture(0, "Source", packFrame); processCompute.SetTexture(0, "SourceMask", frame); processCompute.SetTexture(0, "Result", tempFrame); processCompute.SetBool("AllChannels", true); processCompute.SetBool("NormalsDepth", true); processCompute.Dispatch(0, threadsX, threadsY, threadsZ); Graphics.Blit(tempFrame, packFrame); Graphics.SetRenderTarget(tempFrame); GL.Clear(true, true, Color.clear); CalcWorkSize(frame.width * frame.height, out threadsX, out threadsY, out threadsZ); processCompute.SetTexture(0, "Source", frame); processCompute.SetTexture(0, "SourceMask", frame); processCompute.SetTexture(0, "Result", tempFrame); processCompute.SetBool("AllChannels", false); processCompute.SetBool("NormalsDepth", false); processCompute.Dispatch(0, threadsX, threadsY, threadsZ); Graphics.Blit(tempFrame, frame); Graphics.SetRenderTarget(tempFrame); GL.Clear(true, true, Color.clear); CalcWorkSize(frame.width * frame.height, out threadsX, out threadsY, out threadsZ); processCompute.SetTexture(1, "Source", frame); processCompute.SetTexture(1, "SourceMask", frame); processCompute.SetBuffer(1, "MinDistances", minDistancesBuffer); processCompute.Dispatch(1, threadsX, threadsY, threadsZ); processCompute.SetInt("MinDistancesLength", minDistancesBuffer.count); processCompute.SetBuffer(2, "MaxOfMinDistances", maxDistanceBuffer); processCompute.SetBuffer(2, "MinDistances", minDistancesBuffer); processCompute.Dispatch(2, 1, 1, 1); CalcWorkSize(frame.width * frame.height, out threadsX, out threadsY, out threadsZ); processCompute.SetTexture(3, "Source", frame); processCompute.SetTexture(3, "SourceMask", frame); processCompute.SetTexture(3, "Result", tempFrame); processCompute.SetBuffer(3, "MinDistances", minDistancesBuffer); processCompute.SetBuffer(3, "MaxOfMinDistances", maxDistanceBuffer); processCompute.Dispatch(3, threadsX, threadsY, threadsZ); Graphics.Blit(tempFrame, frame); int x; int y; XYFromIndex(frameIndex, imposter.Frames, out x, out y); x *= frame.width; y *= frame.height; Graphics.CopyTexture(frame, 0, 0, 0, 0, frame.width, frame.height, baseAtlas, 0, 0, x, y); Graphics.CopyTexture(packFrame, 0, 0, 0, 0, packFrame.width, packFrame.height, packAtlas, 0, 0, x, y); } Graphics.SetRenderTarget(packAtlas); imposter.PackTexture.ReadPixels(new Rect(0f, 0f, packAtlas.width, packAtlas.height), 0, 0); Graphics.SetRenderTarget(baseAtlas); imposter.BaseTexture.ReadPixels(new Rect(0f, 0f, baseAtlas.width, baseAtlas.height), 0, 0); RenderTexture.active = prevRt; baseAtlas.Release(); frame.Release(); packAtlas.Release(); packFrame.Release(); RenderTexture.ReleaseTemporary(baseAtlas); RenderTexture.ReleaseTemporary(packAtlas); RenderTexture.ReleaseTemporary(tempAtlas); RenderTexture.ReleaseTemporary(frame); RenderTexture.ReleaseTemporary(packFrame); RenderTexture.ReleaseTemporary(tempFrame); RenderTexture.ReleaseTemporary(superSizedFrame); RenderTexture.ReleaseTemporary(superSizedAlphaMask); RenderTexture.ReleaseTemporary(superSizedFrameTemp); minDistancesBuffer.Dispose(); maxDistanceBuffer.Dispose(); DestroyImmediate(camera.gameObject, true); //restore layers RestoreLayers(originalLayers); //restore lights var enumerator2 = originalLights.Keys.GetEnumerator(); while (enumerator2.MoveNext()) { var light = enumerator2.Current; if (light != null) { light.enabled = originalLights[light]; } } enumerator2.Dispose(); originalLights.Clear(); var savePath = ""; var file = ""; var filePrefab = ""; if (imposter.AssetReference != null) { savePath = AssetDatabase.GetAssetPath(imposter.AssetReference); var lastSlash = savePath.LastIndexOf("/", StringComparison.Ordinal); var folder = savePath.Substring(0, lastSlash); file = savePath.Substring(lastSlash + 1, savePath.LastIndexOf(".", StringComparison.Ordinal) - lastSlash - 1); filePrefab = file; savePath = folder + "/" + file + "_Imposter" + ".asset"; } else //no prefab, ask where to save { file = root.name; savePath = EditorUtility.SaveFilePanelInProject("Save Billboard Imposter", file + "_Imposter", "asset", "Select save location"); } imposter.PrefabSuffix = _suffix; imposter.name = file; AssetDatabase.CreateAsset(imposter, savePath); imposter.Save(savePath, file, _createUnityBillboard); //spawn var spawned = imposter.Spawn(root.position, true, filePrefab); spawned.transform.position = root.position + new Vector3(2f, 0f, 2f); spawned.transform.rotation = root.rotation; spawned.transform.localScale = originalScale; root.localScale = originalScale; return(true); }