unity3d渲染出图

2023-08-18 13:05 综合百科 0阅读 投稿:小七
unity3d渲染出图图1


Hello . 大家好

今天给大家带来的是U3D渲染到CubeMap的切图保存工具

我是麦田


1需求


因为自研引擎写GLSL暂时不支持直接采样HDR,只能够支持原生的6图模式的图片合成,如果通过手动切太浪费时间了,目前本插件还没有完善到完全自动化的程度,但是实现了基本的功能。

2实现需求


Cube Map 通常在渲染引擎中充当天空盒、反射贴图等重要角色。Cube Map的制作往往通过采集的HDR图或者在3D渲染软件中渲染生成。一张完整的Cube Map 包含前后左右上下六张图。但是在一些特定平台并不能直接使用exr等其他HDR图,例如three.js H5 平台(使用纯原生threejs引擎的是支持的),所以就需要生成通用的6张PNG(不一定是PNG)的图。


如下图,这一个是Unity烘焙面板用来烘焙环境贴图的参数面板,本着实时渲染在手机端能省就省的原则,Unity这里采取的也是一个预先烘焙好环境贴图的方式(预积分),为了让贴图分辨率高一点,我这里设置成了1024。


unity3d渲染出图图2


Unity烘焙后的probe如下图,是被排列成一个6*1的矩阵形式的。Unity烘焙后的probe如下图,是被排列成一个6*1的矩阵形式的。


unity3d渲染出图图3


如何在Unity3D引擎中快速方便的生成六张图呢?下面,我们就开始编写。


我们先搭建一个用于渲染的场景:


unity3d渲染出图图4


通过引擎烘焙出一张 EXR 图(也可以直接使用Reflection Probe 来生成),插件还没有做到一件自动,这一步其实可以通过脚本调用的。


先添加一个 Reflection Probe 并调整好位置与角度。


unity3d渲染出图图5


然后在Unity中渲染反射贴图,渲染效果如下。


unity3d渲染出图图6


在Inspector面板将贴图标记为可读,否则会无法访问。


unity3d渲染出图图7


选择插件并切分:


插件主要功能是读取Cube Map 并生成六张图。


选择菜单栏后选择一张CubeMap(标记可读取后的),弹出路径选择面板后保存


unity3d渲染出图图8unity3d渲染出图图9unity3d渲染出图图10



建立对应的六张图片数组并将六张图片命名,方便调用:(代码都写了注释,纯API调用,没有难点,这里不再赘述)


var idx = (int)CubemapFace.PositiveX;var count = (int)CubemapFace.NegativeZ + 1;//使用一个 list 来存储cubemap的图片 List<PickupCubemapTexInfo> texs = new List<PickupCubemapTexInfo>(); Dictionary<CubemapFace, string> nameMap = new Dictionary<CubemapFace, string>();//设置存储的名称 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($"cd:{cd}"); foldername = Path.Combine(foldername, Selection.activeObject.name);if (!Directory.Exists(foldername)) { Directory.CreateDirectory(foldername); } foldername = foldername.Replace("\\", "/"); Debug.Log($"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($"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);


之后使用插件将六张图片输出。


unity3d渲染出图图11


这六张图片可以作为反射贴图,也可以作为天空盒。下图是天空盒以及金属反射效果,使用Unity自带的CubeMapShader即可显示。


unity3d渲染出图图12


最后贴出插件代码,在Unity本地新建一个Editor文件后放入,即可使用。


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($"Selecting one cubemap."); return; } var idx = (int)CubemapFace.PositiveX; var count = (int)CubemapFace.NegativeZ + 1; //使用一个 list 来存储cubemap的图片 List<PickupCubemapTexInfo> texs = new List<PickupCubemapTexInfo>(); Dictionary<CubemapFace, string> nameMap = new Dictionary<CubemapFace, string>(); //设置存储的名称 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($"cd:{cd}"); foldername = Path.Combine(foldername, Selection.activeObject.name); if (!Directory.Exists(foldername)) { Directory.CreateDirectory(foldername); } foldername = foldername.Replace("\\", "/"); Debug.Log($"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($"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 -

声明:若水百科所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流。若您的权利被侵害,请联系youzivr@vip.qq.com