2025-07-10 23:16:27 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
|
using UnityEngine;
|
2025-05-23 16:24:00 +08:00
|
|
|
|
|
|
|
|
|
namespace ZXL.Helper
|
|
|
|
|
{
|
|
|
|
|
public static class TextureHelper
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 精灵图片转纹理
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="sprite"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Texture2D SpriteToTexture2D(Sprite sprite)
|
|
|
|
|
{
|
|
|
|
|
// 获取 Sprite 的纹理
|
|
|
|
|
Texture2D texture = sprite.texture;
|
|
|
|
|
// 创建一个新的 Texture2D 用于存储 Sprite 的图像数据
|
|
|
|
|
Texture2D texture2D = new Texture2D((int)sprite.rect.width, (int)sprite.rect.height);
|
|
|
|
|
// 读取 Sprite 的像素数据到新的 Texture2D
|
|
|
|
|
Color[] pixels = texture.GetPixels(
|
|
|
|
|
(int)sprite.rect.x,
|
|
|
|
|
(int)sprite.rect.y,
|
|
|
|
|
(int)sprite.rect.width,
|
|
|
|
|
(int)sprite.rect.height
|
|
|
|
|
);
|
|
|
|
|
// 将像素数据设置到新的 Texture2D 中
|
|
|
|
|
texture2D.SetPixels(pixels);
|
|
|
|
|
texture2D.Apply(); // 应用更改
|
|
|
|
|
return texture2D;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 纹理转精灵图片
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="texture"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Sprite SpriteFromTexture2D(Texture2D texture)
|
|
|
|
|
{
|
|
|
|
|
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height),
|
|
|
|
|
new Vector2(0.5f, 0.5f));
|
|
|
|
|
return sprite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 截取相机画面并返回纹理图片
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="camera"></param>
|
|
|
|
|
/// <returns></returns>
|
2025-07-14 19:29:10 +08:00
|
|
|
|
public static Texture2D SaveCameraToTexture(Camera camera, bool generateMipMaps = false,
|
2025-07-11 22:35:50 +08:00
|
|
|
|
TextureFormat textureFormat = TextureFormat.RGBA32)
|
2025-05-23 16:24:00 +08:00
|
|
|
|
{
|
2025-07-11 22:35:50 +08:00
|
|
|
|
if (camera == null)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError("Camera is null!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2025-05-23 16:24:00 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
RenderTexture originalTargetTexture = camera.targetTexture;
|
|
|
|
|
RenderTexture tempRenderTexture = null;
|
|
|
|
|
Texture2D capturedImage = null;
|
2025-05-23 16:24:00 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
2025-07-14 19:29:10 +08:00
|
|
|
|
// 创建支持透明度的临时RenderTexture
|
2025-07-11 22:35:50 +08:00
|
|
|
|
tempRenderTexture = RenderTexture.GetTemporary(
|
2025-07-14 19:29:10 +08:00
|
|
|
|
camera.pixelWidth, camera.pixelHeight, 24,
|
|
|
|
|
RenderTextureFormat.ARGB32, // 使用支持Alpha通道的格式
|
|
|
|
|
RenderTextureReadWrite.Default);
|
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
// 设置相机渲染到临时RenderTexture
|
|
|
|
|
camera.targetTexture = tempRenderTexture;
|
2025-07-14 19:29:10 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
// 强制相机渲染一帧
|
|
|
|
|
camera.Render();
|
2025-05-23 16:24:00 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
// 创建Texture2D
|
|
|
|
|
capturedImage = new Texture2D(
|
2025-07-14 19:29:10 +08:00
|
|
|
|
tempRenderTexture.width,
|
|
|
|
|
tempRenderTexture.height,
|
|
|
|
|
textureFormat,
|
2025-07-11 22:35:50 +08:00
|
|
|
|
generateMipMaps);
|
2025-05-23 16:24:00 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
// 保存当前激活的RenderTexture
|
|
|
|
|
RenderTexture previousActive = RenderTexture.active;
|
2025-07-14 19:29:10 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
// 设置临时RenderTexture为当前激活的,以便读取像素
|
|
|
|
|
RenderTexture.active = tempRenderTexture;
|
2025-07-14 19:29:10 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
// 读取像素并应用
|
|
|
|
|
capturedImage.ReadPixels(new Rect(0, 0, tempRenderTexture.width, tempRenderTexture.height), 0, 0);
|
|
|
|
|
capturedImage.Apply();
|
2025-07-14 19:29:10 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
// 恢复之前的RenderTexture
|
|
|
|
|
RenderTexture.active = previousActive;
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError($"Error capturing camera: {e.Message}");
|
|
|
|
|
if (capturedImage != null)
|
|
|
|
|
{
|
|
|
|
|
UnityEngine.Object.DestroyImmediate(capturedImage);
|
|
|
|
|
capturedImage = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
// 恢复相机的原始设置
|
|
|
|
|
camera.targetTexture = originalTargetTexture;
|
2025-07-14 19:29:10 +08:00
|
|
|
|
|
2025-07-11 22:35:50 +08:00
|
|
|
|
// 释放临时RenderTexture
|
|
|
|
|
if (tempRenderTexture != null)
|
|
|
|
|
{
|
|
|
|
|
RenderTexture.ReleaseTemporary(tempRenderTexture);
|
|
|
|
|
tempRenderTexture = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-23 16:24:00 +08:00
|
|
|
|
|
|
|
|
|
return capturedImage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Texture转Texture2D
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="texture"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Texture2D ConvertTextureToTexture2D(Texture texture)
|
|
|
|
|
{
|
|
|
|
|
Texture2D texture2D = new Texture2D(texture.width, texture.height, TextureFormat.RGBA32, false);
|
|
|
|
|
RenderTexture currentRT = RenderTexture.active;
|
|
|
|
|
|
|
|
|
|
// 创建一个新的 RenderTexture
|
|
|
|
|
RenderTexture renderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 32);
|
|
|
|
|
Graphics.Blit(texture, renderTexture);
|
|
|
|
|
|
|
|
|
|
// 将 RenderTexture 内容复制到 Texture2D
|
|
|
|
|
RenderTexture.active = renderTexture;
|
|
|
|
|
texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
|
|
|
|
|
texture2D.Apply();
|
|
|
|
|
|
|
|
|
|
// 清理
|
|
|
|
|
RenderTexture.active = currentRT;
|
|
|
|
|
RenderTexture.ReleaseTemporary(renderTexture);
|
|
|
|
|
|
|
|
|
|
return texture2D;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 纹理图片转二进制数据
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="texture"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static byte[] ConvertTextureToByteArray(Texture2D texture)
|
|
|
|
|
{
|
|
|
|
|
return texture.EncodeToPNG();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 二进制数据转纹理图片
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bytes"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Texture2D ConvertByteArrayToTexture2D(byte[] bytes)
|
|
|
|
|
{
|
|
|
|
|
Texture2D texture = new Texture2D(2, 2); // 初始化一个临时的 Texture2D
|
2025-07-10 23:16:27 +08:00
|
|
|
|
if (texture.LoadImage(bytes)) // 使用 LoadImage 来加载字节数据
|
2025-05-23 16:24:00 +08:00
|
|
|
|
{
|
|
|
|
|
return texture;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError("加载图片失败!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-10 23:16:27 +08:00
|
|
|
|
|
|
|
|
|
public static async UniTask TransparentCaptureSystem(TransparentCaptureSystemOption option)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
if (option.targetCamera == null)
|
|
|
|
|
{
|
|
|
|
|
throw new NullReferenceException("targetCamera is null");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async UniTask CaptureAndCrop()
|
|
|
|
|
{
|
|
|
|
|
// 确保尺寸有效
|
|
|
|
|
ValidateSizes();
|
|
|
|
|
|
|
|
|
|
// 创建原始截图
|
|
|
|
|
await (CaptureOriginalScreenshot());
|
|
|
|
|
|
|
|
|
|
// 裁剪图片(保留透明通道)
|
|
|
|
|
CropWithTransparency();
|
|
|
|
|
|
|
|
|
|
// 保存结果
|
|
|
|
|
SaveCroppedImage();
|
|
|
|
|
|
|
|
|
|
// 清理资源
|
|
|
|
|
CleanupResources();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async UniTask CaptureOriginalScreenshot()
|
|
|
|
|
{
|
|
|
|
|
// 创建RenderTexture(带alpha通道)
|
|
|
|
|
option._renderTexture = new RenderTexture(option.captureWidth, option.captureHeight, 32, RenderTextureFormat.ARGB32);
|
|
|
|
|
option._renderTexture.Create();
|
|
|
|
|
|
|
|
|
|
// 保存原始相机设置
|
|
|
|
|
RenderTexture originalRT = option.targetCamera.targetTexture;
|
|
|
|
|
CameraClearFlags originalClearFlags = option.targetCamera.clearFlags;
|
|
|
|
|
Color originalBackgroundColor = option.targetCamera.backgroundColor;
|
|
|
|
|
|
|
|
|
|
// 配置相机透明背景
|
|
|
|
|
option.targetCamera.targetTexture = option._renderTexture;
|
|
|
|
|
option.targetCamera.clearFlags = CameraClearFlags.SolidColor;
|
|
|
|
|
option.targetCamera.backgroundColor = option.preserveAlpha ? option.backgroundColor : Color.black;
|
|
|
|
|
|
|
|
|
|
// 渲染相机
|
|
|
|
|
option.targetCamera.Render();
|
|
|
|
|
|
|
|
|
|
// 创建Texture2D(带alpha通道)
|
|
|
|
|
option._originalTexture = new Texture2D(option.captureWidth, option.captureHeight,
|
|
|
|
|
option.preserveAlpha ? TextureFormat.ARGB32 : TextureFormat.RGB24,
|
|
|
|
|
false);
|
|
|
|
|
|
|
|
|
|
// 读取像素(包括alpha)
|
|
|
|
|
RenderTexture.active = option._renderTexture;
|
|
|
|
|
option._originalTexture.ReadPixels(new Rect(0, 0, option.captureWidth, option.captureHeight), 0, 0);
|
|
|
|
|
option._originalTexture.Apply();
|
|
|
|
|
RenderTexture.active = null;
|
|
|
|
|
|
|
|
|
|
// 恢复相机设置
|
|
|
|
|
option.targetCamera.targetTexture = originalRT;
|
|
|
|
|
option.targetCamera.clearFlags = originalClearFlags;
|
|
|
|
|
option.targetCamera.backgroundColor = originalBackgroundColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string fullPath;
|
|
|
|
|
|
|
|
|
|
void CropWithTransparency()
|
|
|
|
|
{
|
|
|
|
|
if (option._originalTexture == null)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError("原始截图未创建!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算裁剪起始位置
|
|
|
|
|
int startX = Mathf.FloorToInt((option._originalTexture.width - option.cropWidth) * option.cropOffset.x);
|
|
|
|
|
int startY = Mathf.FloorToInt((option._originalTexture.height - option.cropHeight) * option.cropOffset.y);
|
|
|
|
|
|
|
|
|
|
// 确保在有效范围内
|
|
|
|
|
startX = Mathf.Clamp(startX, 0, option._originalTexture.width - option.cropWidth);
|
|
|
|
|
startY = Mathf.Clamp(startY, 0, option._originalTexture.height - option.cropHeight);
|
|
|
|
|
|
|
|
|
|
// 创建裁剪后的纹理(带alpha通道)
|
|
|
|
|
option._croppedTexture = new Texture2D(option.cropWidth, option.cropHeight,
|
|
|
|
|
option.preserveAlpha ? TextureFormat.ARGB32 : TextureFormat.RGB24,
|
|
|
|
|
false);
|
|
|
|
|
|
|
|
|
|
// 获取原始纹理的像素数据(包括alpha)
|
|
|
|
|
Color[] pixels = option._originalTexture.GetPixels(startX, startY, option.cropWidth, option.cropHeight);
|
|
|
|
|
|
|
|
|
|
// 设置到裁剪纹理
|
|
|
|
|
option._croppedTexture.SetPixels(pixels);
|
|
|
|
|
option._croppedTexture.Apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SaveCroppedImage()
|
|
|
|
|
{
|
|
|
|
|
if (option._croppedTexture == null)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError("裁剪后的纹理未创建!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建保存目录
|
|
|
|
|
string folderPath = option.saveFolder;
|
|
|
|
|
if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);
|
|
|
|
|
|
|
|
|
|
// 生成文件名
|
|
|
|
|
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
|
|
|
|
string extension = option.saveAsPNG ? ".png" : ".jpg";
|
|
|
|
|
string fileName;
|
|
|
|
|
if (string.IsNullOrEmpty(option.onlyFileName))
|
|
|
|
|
fileName = $"{option.fileNamePrefix}_{option.cropWidth}x{option.cropHeight}_{timestamp}{extension}";
|
|
|
|
|
else
|
|
|
|
|
fileName = $"{option.onlyFileName}{extension}";
|
|
|
|
|
|
|
|
|
|
fullPath = Path.Combine(folderPath, fileName);
|
|
|
|
|
|
|
|
|
|
// 编码保存
|
|
|
|
|
byte[] imageBytes;
|
|
|
|
|
|
|
|
|
|
if (option.saveAsPNG)
|
|
|
|
|
{
|
|
|
|
|
imageBytes = option._croppedTexture.EncodeToPNG();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// JPG不支持透明通道,强制去除alpha
|
|
|
|
|
Texture2D jpgTexture =
|
|
|
|
|
new Texture2D(option._croppedTexture.width, option._croppedTexture.height, TextureFormat.RGB24, false);
|
|
|
|
|
jpgTexture.SetPixels(option._croppedTexture.GetPixels());
|
|
|
|
|
jpgTexture.Apply();
|
|
|
|
|
imageBytes = jpgTexture.EncodeToJPG(90);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
File.WriteAllBytes(fullPath, imageBytes);
|
|
|
|
|
|
|
|
|
|
// 验证透明度
|
|
|
|
|
if (option.saveAsPNG && option.preserveAlpha)
|
|
|
|
|
{
|
|
|
|
|
// 检查左上角像素是否透明
|
|
|
|
|
Color topLeft = option._croppedTexture.GetPixel(0, option._croppedTexture.height - 1);
|
|
|
|
|
Debug.Log($"保存的图片左上角透明度: {topLeft.a}");
|
|
|
|
|
|
|
|
|
|
// 检查中心像素是否透明
|
|
|
|
|
Color center = option._croppedTexture.GetPixel(option._croppedTexture.width / 2, option._croppedTexture.height / 2);
|
|
|
|
|
Debug.Log($"保存的图片中心透明度: {center.a}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Debug.Log($"截图已保存至: {fullPath}");
|
|
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
UnityEditor.EditorUtility.RevealInFinder(fullPath);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ValidateSizes()
|
|
|
|
|
{
|
|
|
|
|
// 确保原始尺寸有效
|
|
|
|
|
option.captureWidth = Mathf.Clamp(option.captureWidth, 32, SystemInfo.maxTextureSize);
|
|
|
|
|
option. captureHeight = Mathf.Clamp(option.captureHeight, 32, SystemInfo.maxTextureSize);
|
|
|
|
|
|
|
|
|
|
// 确保裁剪尺寸有效
|
|
|
|
|
option.cropWidth = Mathf.Clamp(option.cropWidth, 32,option. captureWidth);
|
|
|
|
|
option.cropHeight = Mathf.Clamp(option.cropHeight, 32, option.captureHeight);
|
|
|
|
|
|
|
|
|
|
// 确保偏移量在0-1范围
|
|
|
|
|
option.cropOffset.x = Mathf.Clamp01(option.cropOffset.x);
|
|
|
|
|
option.cropOffset.y = Mathf.Clamp01(option.cropOffset.y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CleanupResources()
|
|
|
|
|
{
|
|
|
|
|
if (option._renderTexture != null)
|
|
|
|
|
{
|
|
|
|
|
Destroy(_renderTexture);
|
|
|
|
|
option._renderTexture = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (option._originalTexture != null)
|
|
|
|
|
{
|
|
|
|
|
Destroy(_originalTexture);
|
|
|
|
|
option._originalTexture = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保留裁剪纹理用于预览或后续使用
|
|
|
|
|
// 如果不再需要,可以取消注释下面一行
|
|
|
|
|
Destroy(_croppedTexture);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UniTask.Run(CaptureAndCrop);
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class TransparentCaptureSystemOption
|
|
|
|
|
{
|
|
|
|
|
// 相机设置
|
|
|
|
|
public Camera targetCamera;
|
|
|
|
|
public KeyCode captureKey = KeyCode.C;
|
|
|
|
|
|
|
|
|
|
// 原始截图设置
|
|
|
|
|
public int captureWidth = 1080;
|
|
|
|
|
public int captureHeight = 1920;
|
|
|
|
|
|
|
|
|
|
// 裁剪设置
|
|
|
|
|
public int cropWidth = 800;
|
|
|
|
|
public int cropHeight = 300;
|
|
|
|
|
public Vector2 cropOffset = Vector2.zero; // 裁剪偏移量 (0-1)
|
|
|
|
|
|
|
|
|
|
// 透明通道
|
|
|
|
|
public bool preserveAlpha = true;
|
|
|
|
|
public Color backgroundColor = new Color(0, 0, 0, 0); // 透明背景
|
|
|
|
|
|
|
|
|
|
// 输出设置
|
|
|
|
|
public string saveFolder = "Screenshots";
|
|
|
|
|
public string fileNamePrefix = "Screenshot";
|
|
|
|
|
public bool saveAsPNG = true;
|
|
|
|
|
public string onlyFileName = "";
|
|
|
|
|
|
|
|
|
|
public RenderTexture _renderTexture;
|
|
|
|
|
public Texture2D _originalTexture;
|
|
|
|
|
public Texture2D _croppedTexture;
|
2025-05-23 16:24:00 +08:00
|
|
|
|
}
|
2025-07-10 23:16:27 +08:00
|
|
|
|
}
|