Hello . 大家好
今天給大家帶來的是U3D渲染到的切圖保存工具
我是麥田
1需求
因為自研引擎寫GLSL暫時不支持直接采樣HDR,只能夠支持原生的6圖模式的圖片合成,如果通過手動切太浪費時間了,目前本插件還沒有完善到完全自動化的程度,但是實現了基本的功能。
2實現需求
Cube Map 通常在渲染引擎中充當天空盒、反射貼圖等重要角色。Cube Map的制作往往通過采集的HDR圖或者在3D渲染軟件中渲染生成。一張完整的Cube Map 包含前后左右上下六張圖。但是在一些特定平臺并不能直接使用exr等其他HDR圖,例如three.js H5 平臺(使用純原生引擎的是支持的),所以就需要生成通用的6張PNG(不一定是PNG)的圖。
如下圖,這一個是Unity烘焙面板用來烘焙環境貼圖的參數面板,本著實時渲染在手機端能省就省的原則,Unity這里采取的也是一個預先烘焙好環境貼圖的方式(預積分),為了讓貼圖分辨率高一點,我這里設置成了1024。
Unity烘焙后的probe如下圖,是被排列成一個6*1的矩陣形式的。Unity烘焙后的probe如下圖,是被排列成一個6*1的矩陣形式的。
如何在引擎中快速方便的生成六張圖呢?下面,我們就開始編寫。
我們先搭建一個用于渲染的場景:
通過引擎烘焙出一張 EXR 圖(也可以直接使用 Probe 來生成),插件還沒有做到一件自動,這一步其實可以通過腳本調用的。
先添加一個 Probe 并調整好位置與角度。
然后在Unity中渲染反射貼圖,渲染效果如下。
在面板將貼圖標記為可讀,否則會無法訪問。
選擇插件并切分:
插件主要功能是讀取Cube Map 并生成六張圖。
選擇菜單欄后選擇一張(標記可讀取后的),彈出路徑選擇面板后保存
建立對應的六張圖片數組并將六張圖片命名,方便調用:(代碼都寫了注釋,純API調用,沒有難點網頁切圖用什么軟件,這里不再贅述)
var idx = (int)CubemapFace.PositiveX;
var count = (int)CubemapFace.NegativeZ + 1;
//使用一個 list 來存儲cubemap的圖片
List texs = new List();
Dictionary nameMap = new Dictionary();
//設置存儲的名稱
nameMap[CubemapFace.PositiveX] = "Right";
nameMap[CubemapFace.NegativeX] = "Left";
nameMap[CubemapFace.PositiveY] = "Upwards";
nameMap[CubemapFace.NegativeY] = "Downward";
nameMap[CubemapFace.PositiveZ] = "Forward";
????????nameMap[CubemapFace.NegativeZ]?=?"Backward";
使用字節流對每個圖片進行編碼與存儲,注意填寫圖片大小,防止失真。
if (string.IsNullOrEmpty(foldername) || !Directory.Exists(foldername))
{

// noop
}
else
{
//循環讀出圖片數據
for (; idx < count; idx++)
{
var face = (CubemapFace)idx;
var ps = cubemap.GetPixels(face);
// 注意這里的texture2d 的width, height對應cubemap中的face size,但類中沒定義,所以這里匹配好你的cubemap來使用就好了
var newTex = new Texture2D(cubemap.width, cubemap.height);
newTex.SetPixels(ps);
texs.Add(new PickupCubemapTexInfo { tex = newTex, name = nameMap[face] });
}
foldername = foldername.Replace("\\", "/");
var cd = Directory.GetParent(Path.GetFullPath("Assets")).FullName;
cd = cd.Replace("\\", "/");
Debug.Log(#34;cd:{cd}");
foldername = Path.Combine(foldername, Selection.activeObject.name);
if (!Directory.Exists(foldername))
{
Directory.CreateDirectory(foldername);
}
foldername = foldername.Replace("\\", "/");
Debug.Log(#34;append assetsname folder:{foldername}");
//循環并輸出圖片資源
foreach (var item in texs)
{
var fullname = Path.Combine(foldername, item.name + ".png");
fullname = fullname.Replace("\\", "/");
fullname = fullname.Replace(cd + "/", "");
Debug.Log(#34;fullname:{fullname}");
//注意:圖片可能上下反轉 ,此步是將圖片反轉回來
var tex = VerticalFlipTexture(item.tex);
var bs = tex.EncodeToPNG();

var fs = File.Open(fullname, FileMode.Create, FileAccess.Write);
fs.Write(bs, 0, bs.Length);
fs.Close();
fs.Dispose();
}
AssetDatabase.SaveAssets();//保存資源
AssetDatabase.Refresh();//刷新資源面板
}
}
最后可以加一個進度條,方便查看進度。
//加載一個可視化的進度條
var title = "Select the directory which stored CubeMap Tex.";
var filepath = AssetDatabase.GetAssetPath(Selection.activeObject);
var folder = Directory.GetParent(filepath);
var foldername = EditorUtility.OpenFolderPanel(title, folder.ToString(), string.Empty);
之后使用插件將六張圖片輸出。
這六張圖片可以作為反射貼圖網頁切圖用什么軟件,也可以作為天空盒。下圖是天空盒以及金屬反射效果,使用Unity自帶的即可顯示。
最后貼出插件代碼,在Unity本地新建一個文件后放入,即可使用。
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
public class ReadEXRToPNG : Editor
{
[MenuItem("CustomTool/Cut CubeMap", false, 2)]
public static void EXR2PNG()
{
Cubemap cubemap = null;
Debug.Log(Selection.activeObject.GetType());
//判斷所選資源類型并過濾 Cubemap 資源
if (Selection.activeObject.GetType() == typeof(Cubemap))
{
cubemap = Selection.activeObject as Cubemap;
}

//為空則跳出并報警告
if (cubemap == null)
{
Debug.LogWarning(#34;Selecting one cubemap.");
return;
}
var idx = (int)CubemapFace.PositiveX;
var count = (int)CubemapFace.NegativeZ + 1;
//使用一個 list 來存儲cubemap的圖片
List texs = new List();
Dictionary nameMap = new Dictionary();
//設置存儲的名稱
nameMap[CubemapFace.PositiveX] = "Right";
nameMap[CubemapFace.NegativeX] = "Left";
nameMap[CubemapFace.PositiveY] = "Upwards";
nameMap[CubemapFace.NegativeY] = "Downward";
nameMap[CubemapFace.PositiveZ] = "Forward";
nameMap[CubemapFace.NegativeZ] = "Backward";
try
{
//加載一個可視化的進度條
var title = "Select the directory which stored CubeMap Tex.";
var filepath = AssetDatabase.GetAssetPath(Selection.activeObject);
var folder = Directory.GetParent(filepath);
var foldername = EditorUtility.OpenFolderPanel(title, folder.ToString(), string.Empty);
if (string.IsNullOrEmpty(foldername) || !Directory.Exists(foldername))
{
// noop
}
else
{
//循環讀出圖片數據
for (; idx < count; idx++)
{
var face = (CubemapFace)idx;

var ps = cubemap.GetPixels(face);
// 注意這里的texture2d 的width, height對應cubemap中的face size,但類中沒定義,所以這里匹配好你的cubemap來使用就好了
var newTex = new Texture2D(cubemap.width, cubemap.height);
newTex.SetPixels(ps);
texs.Add(new PickupCubemapTexInfo { tex = newTex, name = nameMap[face] });
}
foldername = foldername.Replace("\\", "/");
var cd = Directory.GetParent(Path.GetFullPath("Assets")).FullName;
cd = cd.Replace("\\", "/");
Debug.Log(#34;cd:{cd}");
foldername = Path.Combine(foldername, Selection.activeObject.name);
if (!Directory.Exists(foldername))
{
Directory.CreateDirectory(foldername);
}
foldername = foldername.Replace("\\", "/");
Debug.Log(#34;append assetsname folder:{foldername}");
//循環并輸出圖片資源
foreach (var item in texs)
{
var fullname = Path.Combine(foldername, item.name + ".png");
fullname = fullname.Replace("\\", "/");
fullname = fullname.Replace(cd + "/", "");
Debug.Log(#34;fullname:{fullname}");
//注意:圖片可能上下反轉 ,此步是將圖片反轉回來
var tex = VerticalFlipTexture(item.tex);
var bs = tex.EncodeToPNG();
var fs = File.Open(fullname, FileMode.Create, FileAccess.Write);
fs.Write(bs, 0, bs.Length);
fs.Close();
fs.Dispose();
}

AssetDatabase.SaveAssets();//保存資源
AssetDatabase.Refresh();//刷新資源面板
}
}
catch (Exception er)
{
Debug.LogError(er);
}
finally
{
foreach (var item in texs)
{
Texture2D.DestroyImmediate(item.tex);
}
texs.Clear();
}
}
//垂直翻轉
public static Texture2D VerticalFlipTexture(Texture2D texture)
{
//得到圖片的寬高
int width = texture.width;
int height = texture.height;
Texture2D flipTexture = new Texture2D(width, height);
for (int i = 0; i < height; i++)
{
flipTexture.SetPixels(0, i, width, 1, texture.GetPixels(0, height - i - 1, width, 1));
}
flipTexture.Apply();
return flipTexture;
}
}
public class PickupCubemapTexInfo
{
public Texture2D tex;
public string name;
}
- End -