Unity将粒子光效导出成png序列帧
这个功能并不是很实用,不过美术同学有这样的需求,那么就花了一点时间研究了下。
我们没有使用Unity的引擎,但是做特效的同学找了一批Unity的粒子特效,希望导出成png序列帧的形式,然后我们的游戏来使用。这个就相当于拿Unity做了特效编辑器的工作。这个并不是很“邪门”,因为用幻影粒子,或者3dmax,差不多也是这个思路,只不过那些软件提供了正规的导出功能,而Unity则没有。
先上代码
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;public class ParticleExporter : MonoBehaviour
{// Default folder name where you want the animations to be outputpublic string folder = "PNG_Animations";// Framerate at which you want to play the animationpublic int frameRate = 25; // export frame rate 导出帧率,设置Time.captureFramerate会忽略真实时间,直接使用此帧率public float frameCount = 100; // export frame count 导出帧的数目,100帧则相当于导出5秒钟的光效时间。由于导出每一帧的时间很长,所以导出时间会远远长于直观的光效播放时间public int screenWidth = 960; // not use 暂时没用,希望可以直接设置屏幕的大小(即光效画布的大小)public int screenHeight = 640;public Vector3 cameraPosition = Vector3.zero;public Vector3 cameraRotation = Vector3.zero;private string realFolder = ""; // real folder where the output files will beprivate float originaltimescaleTime; // track the original time scale so we can freeze the animation between framesprivate float currentTime = 0;private bool over = false;private int currentIndex = 0;private Camera exportCamera; // camera for export 导出光效的摄像机,使用RenderTexturepublic void Start(){// set frame rateTime.captureFramerate = frameRate;// Create a folder that doesn't exist yet. Append number if necessary.realFolder = Path.Combine(folder, name);// Create the folderif (!Directory.Exists(realFolder)) {Directory.CreateDirectory(realFolder);}originaltimescaleTime = Time.timeScale;GameObject goCamera = Camera.main.gameObject;if (cameraPosition != Vector3.zero) {goCamera.transform.position = cameraPosition;}if (cameraRotation != Vector3.zero) {goCamera.transform.rotation = Quaternion.Euler(cameraRotation);}GameObject go = Instantiate(goCamera) as GameObject;exportCamera = go.GetComponent<Camera>();currentTime = 0;}void Update(){currentTime += Time.deltaTime;if (!over && currentIndex >= frameCount) {over = true;Cleanup();Debug.Log("Finish");return;}// 每帧截屏StartCoroutine(CaptureFrame());}void Cleanup(){DestroyImmediate(exportCamera);DestroyImmediate(gameObject);}IEnumerator CaptureFrame(){// Stop timeTime.timeScale = 0;// Yield to next frame and then start the rendering// this is important, otherwise will have erroryield return new WaitForEndOfFrame();string filename = String.Format("{0}/{1:D04}.png", realFolder, ++currentIndex);Debug.Log(filename);int width = Screen.width;int height = Screen.height;//Initialize and render texturesRenderTexture blackCamRenderTexture = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32);RenderTexture whiteCamRenderTexture = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32);exportCamera.targetTexture = blackCamRenderTexture;exportCamera.backgroundColor = Color.black;exportCamera.Render();RenderTexture.active = blackCamRenderTexture;Texture2D texb = GetTex2D();//Now do it for Alpha CameraexportCamera.targetTexture = whiteCamRenderTexture;exportCamera.backgroundColor = Color.white;exportCamera.Render();RenderTexture.active = whiteCamRenderTexture;Texture2D texw = GetTex2D();// If we have both textures then create final output textureif (texw && texb) {Texture2D outputtex = new Texture2D(width, height, TextureFormat.ARGB32, false);// we need to check alpha ourselves,because particle use additive shader// Create Alpha from the difference between black and white camera rendersfor (int y = 0; y < outputtex.height; ++y) { // each rowfor (int x = 0; x < outputtex.width; ++x) { // each columnfloat alpha;alpha = texw.GetPixel(x, y).r - texb.GetPixel(x, y).r;alpha = 1.0f - alpha;Color color;if (alpha == 0) {color = Color.clear;} else {color = texb.GetPixel(x, y);}color.a = alpha;outputtex.SetPixel(x, y, color);}}// Encode the resulting output texture to a byte array then write to the filebyte[] pngShot = outputtex.EncodeToPNG();File.WriteAllBytes(filename, pngShot);// cleanup, otherwise will memory leakpngShot = null;RenderTexture.active = null;DestroyImmediate(outputtex);outputtex = null;DestroyImmediate(blackCamRenderTexture);blackCamRenderTexture = null;DestroyImmediate(whiteCamRenderTexture);whiteCamRenderTexture = null;DestroyImmediate(texb);texb = null;DestroyImmediate(texw);texb = null;System.GC.Collect();// Reset the time scale, then move on to the next frame.Time.timeScale = originaltimescaleTime;}}// Get the texture from the screen, render all or only half of the cameraprivate Texture2D GetTex2D(){// Create a texture the size of the screen, RGB24 formatint width = Screen.width;int height = Screen.height;Texture2D tex = new Texture2D(width, height, TextureFormat.ARGB32, false);// Read screen contents into the texturetex.ReadPixels(new Rect(0, 0, width, height), 0, 0);tex.Apply();return tex;}
}
这里对几个关键的知识点来做说明:
1、整体思路是这样的,Unity中调整好摄像机,正常播放特效,然后每帧截屏,保存成我们需要的png序列帧。这个不仅仅是特效可以这么用,其实模型也可以。比如我们需要同屏显示几百上千人,或者是无关紧要的怪物、场景物件等等,就可以使用这个导出成2d的序列帧,可以大大提高效率,使一些不可能的情况变为可能。
2、关于时间和帧率的控制。由于截屏所需要的时间远远大于帧间隔,所以光效如果是播放1秒,则导出时间可能超过一分钟。Time.captureFrameRate可以设置帧率,设置后则忽略真实时间,光效、模型会按照帧率的时间来播放。这个接口恰好就是用在视频录制上的。
3、光效画布控制。这个暂时没有找到好的方法,由于是全屏幕截屏,所以Game窗口的大小就是光效画布的大小。
4、通过调整摄像机的位置、旋转,控制光效的显示信息。
5、截屏函数就是GetTex2D()。这里面最主要的是ReadPixels函数。需要注意,CaptureFrame函数必须要以协程的方式运行,因为里面有一句yield return new WaitForEndOfFrame();如果没有这一句,会报一个错误,大概意思就是ReadPixels不在DrawFrame里面运行。
6、截屏时间消耗很大,所以需要在截屏开始使用Time.timeScale=0暂停时间运行,截屏后再恢复
7、注意截屏操作完成后清理各种资源,并进行GC。否则内存很有可能就不够用了,截100帧图片,内存很有可能就两三G了。
8、截屏的时候使用了两个RenderTexture,分别绘制白底和黑底的图片,然后根据这两张图片计算出alpha。如果不是光效其实可以不这么麻烦,直接把Camera的backgroundColor中的alpha设置为0就可以了。但是光效使用了特殊的shader,比如Additive,这里涉及到alpha blend。绘制光效时如果也这样设置的话,导出的图片没有任何东西。所以必须要有实色背景。
Unity将粒子光效导出成png序列帧相关推荐
- Unity 3D模型动画导出为帧序列
问题:将用于Unity的3D模型改成2D图片. 思路:把3D动画播放一遍,逐帧对模型截图. 代码说明: 1.要引用的命名空间 using System.IO; using UnityEngine; u ...
- 将Project的内容导出成单独的XPO文件
AX跟VSS整合的版本管理可以通过创建知识库将当前层的代码全部签入到VSS中,但是如果不是一个团队开发solution,而是针对客户的需求随时做得一些小改动,一般都希望以Project的形式组织代码和 ...
- 将数据库查询结果导出成Excel表格
使用Java代码,从数据库中获取结果集,将结果集导出成Excel表格形式. 从数据库中查询学生表所有数据,将其导出成Excel表格,点击查看学生表表结构 . package com.test.test ...
- Oracle中数据导出成CVS,EXCEL
我们可以把oracle数据库中的某个表导出成CVS,可用excel方式打开该文件. 步骤一 选中需要导出的数据,右击->导出结果->csv文件 步骤二 查看导出的文件 步骤三 你可以在cs ...
- 解决eclipse中java项目导出成jar包后读写UTF-8文件中文乱码问题
最近遇到了一个小麻烦,就是在eclipse环境中读写UTF-8格式的txt文件时很正常,但是当导出成jar包后,通过点击来读写文件时出现了部分中文乱码问题. 解决办法: 开始时用的是FileReade ...
- jsonhandle主界面没有显示格式_怎么将图纸导出成图片格式保存
怎么将图纸导出成图片格式保存呢?接下来将与大家分享一下有关CAd图纸转换成图片进行保存的方法. 方法/步骤 为了实现CAd转图片操作,首先我们需要在电脑上安装一个CAD编辑软件,如图所示: 待CAD编 ...
- python抓取数据库数据封装成json_用Python将mysql数据导出成json的方法
1.相关说明 此脚本可以将Mysql的数据导出成Json格式,导出的内容可以进行select查询确定. 数据传入参数有:dbConfigName, selectSql, jsonPath, fileN ...
- mongodb 导出 带条件_将 MongoDB 导出成 csv
**[摘要]** 将 Mongodb 数据结构转换成结构化的数据需求,我们可利用集算器 SPL 语言来进行辅助实现.若想了解更多,请前往乾学院:将 MongoDB 导出成 csv! 来源:https: ...
- 【Selenium】导出成py脚本的基础使用
0. Selenium简介 Selenium是一个用于Web应用程序测试的工具. Selenium测试直接运行在浏览器中,就像真正的用户在操作一样. 主流浏览器都支持.例如firefox,在插件市场里 ...
最新文章
- 计算机大师高德纳权威著作《计算机程序设计艺术》影印版精装版已经入库,即将上市!
- 导出勾选密码永不过期的AD账户信息
- [.Net线程处理系列]专题二:线程池中的工作者线程
- C语言水洼数算法,C++ 数据结构之水洼的数量算法
- 【NLP-ChatBot】能干活的聊天机器人-对话系统概述
- 【白皮书分享】2021内容营销白皮书.pdf(附下载链接)
- BZOJ.3165.[HEOI2013]Segment(李超线段树)
- FBI 连续第三次发布关于国家黑客利用 Kwampirs 发动全球供应链攻击的警告
- JAVA反射构建对象
- 智能服务器软件,无纸化智能会议系统服务器软件
- win10下安装ubuntu双系统
- 小刘同学的第五十五篇博文
- 2020-12-28
- 福禄克光纤测试:多模光纤 VS 单模光纤
- 【学习笔记】H5性能测试
- LoRaWAN入网方式以及加密进阶版
- ODI(Oracle Data Integrator)基本使用教程(1)
- 【新书推荐】崛起的超级智能:互联网大脑如何影响科技未来
- 一位中科院自动化研究所博士的毕业论文致谢:求学22载,计算机终成一生的事业与希望...
- AD936x_增益控制AGC详解
热门文章
- firefox 1.0中添加自定义搜索引擎——北大天网搜索引擎
- java 位与运算_java中位运算和移位运算详解
- [附源码]Java计算机毕业设计SSM爱宝贝影楼管理系统
- 小哨兵还原卡密码如何清楚?
- java配置多环境_[原创]多版本Java环境变量的配置
- ROS调用笔记本摄像头和外界摄像头问题汇总(本人亲身经历)非常好用
- 1005打印任务取消不了 hp_怎么办?HP1005打印机显示正在打印却打 – 手机爱问
- 易排通用规划平台,以Excel作为数据源的调用方法与数据文件说明
- 亚马逊卖家怎样让茶叶乘风破浪,远销海外?-跨境创业找众光
- 你还在用挂历记录大事件?试试这款动态日历表!