using System; using FairyGUI; using FairyGUI.Utils; using System.Collections; using UnityEngine; using UnityEngine.Rendering.Universal; using Object = UnityEngine.Object; namespace NBF { [Serializable] public class ModelViewRenderImage { private ModelViewerSettings _viewerSettings; public Transform modelRoot { get; private set; } Camera _camera; Image _image; Transform _root; Transform _background; Transform _model; RenderTexture _renderTexture; int _width; int _height; bool _cacheTexture; float _rotating; const int RENDER_LAYER = 0; const int HIDDEN_LAYER = 10; public ModelViewRenderImage(GGraph holder) { _width = (int)holder.width; _height = (int)holder.height; _cacheTexture = true; _image = new Image(); holder.SetNativeObject(_image); _root = new GameObject("RenderImage").transform; _root.transform.position = Vector3.zero; // _root.SetParent(_camera.transform, false); SetLayer(_root.gameObject, HIDDEN_LAYER); Object.DontDestroyOnLoad(_root.gameObject); Object prefab = Resources.Load("RenderTexture/RenderImageCamera"); GameObject go = (GameObject)Object.Instantiate(prefab, _root, false); _camera = go.GetComponent(); _camera.transform.position = Vector3.zero; _camera.cullingMask = 1 << RENDER_LAYER; _camera.enabled = false; Object.DontDestroyOnLoad(_camera.gameObject); modelRoot = new GameObject("model_root").transform; modelRoot.SetParent(_root, false); _background = new GameObject("background").transform; _background.SetParent(_root, false); _image.onAddedToStage.Add(OnAddedToStage); _image.onRemovedFromStage.Add(OnRemoveFromStage); _viewerSettings = new ModelViewerSettings(); if (_image.stage != null) OnAddedToStage(); else _camera.gameObject.SetActive(false); } public void LoadModel(string model) { this.UnloadModel(); Object prefab = Resources.Load(model); GameObject go = ((GameObject)Object.Instantiate(prefab)); var joint = go.GetComponent(); if (joint != null) { Object.Destroy(joint); } _model = go.transform; _model.SetParent(this.modelRoot, false); _model.localPosition = Vector3.zero; _model.localScale = Vector3.one; _model.localEulerAngles = Vector3.zero; ModelViewerUtils.InitSetting(go, _viewerSettings); Review(); } public void UnloadModel() { if (_model != null) { _model.gameObject.SetActive(false); Object.Destroy(_model.gameObject); _model = null; } _rotating = 0; } public void Review() { _model.position = _viewerSettings.objectPosition; _model.localScale = _viewerSettings.objectScale; _model.eulerAngles = _viewerSettings.objectRotation; _camera.clearFlags = CameraClearFlags.Nothing; _camera.backgroundColor = new Color(0, 0, 0, 0); // 完全透明 _camera.transform.position = _viewerSettings.cameraPosition; _camera.transform.LookAt(_viewerSettings.cameraTarget); _camera.orthographic = _viewerSettings.cameraOrtho; _camera.orthographicSize = _viewerSettings.cameraSize; _camera.orthographicSize /= _viewerSettings.camerasScaleFactor; _camera.fieldOfView = _viewerSettings.cameraFov; float fov = 2 * Mathf.Rad2Deg * Mathf.Atan2( Mathf.Tan(_camera.fieldOfView * Mathf.Deg2Rad / 2), _viewerSettings.camerasScaleFactor ); if (fov < 0) fov += 180; _camera.fieldOfView = fov; _camera.nearClipPlane = 0.001f; _camera.farClipPlane = 10000; _camera.depthTextureMode = DepthTextureMode.Depth; _camera.clearFlags = CameraClearFlags.Color; _camera.GetUniversalAdditionalCameraData().renderPostProcessing = false; //URP only // var urpCamData = _camera.GetUniversalAdditionalCameraData(); // urpCamData.renderPostProcessing = false; // urpCamData.requiresColorOption = CameraOverrideOption.On; // urpCamData.requiresDepthOption = CameraOverrideOption.Off; // urpCamData.requiresColorTexture = false; // urpCamData.SetRenderer(1); // 关键步骤:指定“透明输出”的Renderer } public void Dispose() { UnloadModel(); Object.Destroy(_camera.gameObject); DestroyTexture(); _image.Dispose(); _image = null; } void CreateTexture() { if (_renderTexture != null) return; _renderTexture = new RenderTexture(1024, 1024, 24, RenderTextureFormat.ARGB32) { antiAliasing = 1, filterMode = FilterMode.Bilinear, anisoLevel = 0, useMipMap = false }; // _renderTexture.Create(); _image.texture = new NTexture(_renderTexture); _image.shader = "Unlit/Transparent"; //Shader.Find("Unlit/Transparent"); // _material = new Material(Shader.Find("Unlit/Transparent")); // _image.blendMode = BlendMode.Off; // _image.blendMode = BlendMode.Normal; } void DestroyTexture() { if (_renderTexture != null) { Object.Destroy(_renderTexture); _renderTexture = null; _image.texture = null; } } void OnAddedToStage() { if (_renderTexture == null) CreateTexture(); Timers.inst.AddUpdate(Render); _camera.gameObject.SetActive(true); Render(); } void OnRemoveFromStage() { if (!_cacheTexture) DestroyTexture(); Timers.inst.Remove(Render); _camera.gameObject.SetActive(false); } void Render(object param = null) { if (_rotating != 0 && modelRoot != null) { Vector3 localRotation = modelRoot.localRotation.eulerAngles; localRotation.y += _rotating; modelRoot.localRotation = Quaternion.Euler(localRotation); } SetLayer(_root.gameObject, RENDER_LAYER); _camera.targetTexture = _renderTexture; RenderTexture old = RenderTexture.active; RenderTexture.active = _renderTexture; GL.Clear(true, true, Color.clear); _camera.Render(); RenderTexture.active = old; SetLayer(_root.gameObject, HIDDEN_LAYER); UpdateDebugSave(); } void SetLayer(GameObject go, int layer) { Transform[] transforms = go.GetComponentsInChildren(true); foreach (Transform t in transforms) { t.gameObject.layer = layer; } } #region MyRegion /// /// The rendertexture is not transparent. So if you want to the UI elements can be seen in the back of the models/particles in rendertexture, /// you can set a maximunm two images for background. /// Be careful if your image is 9 grid scaling, you must make sure the place holder is inside the middle box(dont cover from border to middle). /// /// public void SetBackground(GObject image) { SetBackground(image, null); } /// /// The rendertexture is not transparent. So if you want to the UI elements can be seen in the back of the models/particles in rendertexture, /// you can set a maximunm two images for background. /// /// /// public void SetBackground(GObject image1, GObject image2) { Image source1 = (Image)image1.displayObject; Image source2 = image2 != null ? (Image)image2.displayObject : null; Vector3 pos = _background.position; pos.z = _camera.farClipPlane; _background.position = pos; Vector2[] uv = new Vector2[4]; Vector2[] uv2 = null; Rect rect = _image.TransformRect(new Rect(0, 0, this._width, this._height), source1); Rect uvRect = GetImageUVRect(source1, rect, uv); if (source2 != null) { rect = _image.TransformRect(new Rect(0, 0, this._width, this._height), source2); uv2 = new Vector2[4]; GetImageUVRect(source2, rect, uv2); } Vector3[] vertices = new Vector3[4]; for (int i = 0; i < 4; i++) { Vector2 v = uv[i]; vertices[i] = new Vector3((v.x - uvRect.x) / uvRect.width * 2 - 1, (v.y - uvRect.y) / uvRect.height * 2 - 1, 0); } Mesh mesh = new Mesh(); mesh.vertices = vertices; mesh.uv = uv; if (uv2 != null) mesh.uv2 = uv2; mesh.colors32 = new Color32[] { Color.white, Color.white, Color.white, Color.white }; mesh.triangles = new int[] { 0, 1, 2, 2, 3, 0 }; MeshFilter meshFilter = this._background.gameObject.GetComponent(); if (meshFilter == null) meshFilter = this._background.gameObject.AddComponent(); meshFilter.mesh = mesh; MeshRenderer meshRenderer = this._background.gameObject.GetComponent(); if (meshRenderer == null) meshRenderer = this._background.gameObject.AddComponent(); #if (UNITY_5 || UNITY_5_3_OR_NEWER) meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; #else meshRenderer.castShadows = false; #endif meshRenderer.receiveShadows = false; Shader shader = Shader.Find("Game/FullScreen"); Material mat = new Material(shader); mat.mainTexture = source1.texture.nativeTexture; if (source2 != null) mat.SetTexture("_Tex2", source2.texture.nativeTexture); meshRenderer.material = mat; } Rect GetImageUVRect(Image image, Rect localRect, Vector2[] uv) { Rect imageRect = new Rect(0, 0, image.size.x, image.size.y); Rect bound = ToolSet.Intersection(ref imageRect, ref localRect); Rect uvRect = image.texture.uvRect; if (image.scale9Grid != null) { Rect gridRect = (Rect)image.scale9Grid; float sourceW = image.texture.width; float sourceH = image.texture.height; uvRect = Rect.MinMaxRect(Mathf.Lerp(uvRect.xMin, uvRect.xMax, gridRect.xMin / sourceW), Mathf.Lerp(uvRect.yMin, uvRect.yMax, (sourceH - gridRect.yMax) / sourceH), Mathf.Lerp(uvRect.xMin, uvRect.xMax, gridRect.xMax / sourceW), Mathf.Lerp(uvRect.yMin, uvRect.yMax, (sourceH - gridRect.yMin) / sourceH)); float vw = imageRect.width - (sourceW - gridRect.width); float vh = imageRect.height - (sourceH - gridRect.height); uvRect = Rect.MinMaxRect(Mathf.Lerp(uvRect.xMin, uvRect.xMax, (bound.x - gridRect.x) / vw), Mathf.Lerp(uvRect.yMin, uvRect.yMax, (imageRect.height - bound.yMax - (sourceH - gridRect.yMax)) / vh), Mathf.Lerp(uvRect.xMin, uvRect.xMax, (bound.xMax - gridRect.x) / vw), Mathf.Lerp(uvRect.yMin, uvRect.yMax, (imageRect.height - bound.yMin - gridRect.y) / vh)); } else { uvRect = Rect.MinMaxRect(Mathf.Lerp(uvRect.xMin, uvRect.xMax, bound.xMin / imageRect.width), Mathf.Lerp(uvRect.yMin, uvRect.yMax, (imageRect.height - bound.yMax) / imageRect.height), Mathf.Lerp(uvRect.xMin, uvRect.xMax, bound.xMax / imageRect.width), Mathf.Lerp(uvRect.yMin, uvRect.yMax, (imageRect.height - bound.yMin) / imageRect.height)); } uv[0] = uvRect.position; uv[1] = new Vector2(uvRect.xMin, uvRect.yMax); uv[2] = new Vector2(uvRect.xMax, uvRect.yMax); uv[3] = new Vector2(uvRect.xMax, uvRect.yMin); if (image.texture.rotated) ToolSet.RotateUV(uv, ref image.texture.uvRect); return uvRect; } #endregion #region MyRegion void UpdateDebugSave() { if (Input.GetKeyDown(KeyCode.F9)) { SaveRenderTextureToPNG(_renderTexture, "RenderTextureDebug.png"); } } void SaveRenderTextureToPNG(RenderTexture rt, string fileName) { if (rt == null) { Debug.LogWarning("RenderTexture 为 null,无法保存。"); return; } RenderTexture current = RenderTexture.active; RenderTexture.active = rt; Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false); tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); byte[] bytes = tex.EncodeToPNG(); string path = System.IO.Path.Combine(Application.dataPath, "../" + fileName); System.IO.File.WriteAllBytes(path, bytes); Debug.Log($"✅ RenderTexture 已保存到: {path}"); UnityEngine.Object.Destroy(tex); RenderTexture.active = current; } #endregion } }