这个功能并不是很实用,不过美术同学有这样的需求,那么就花了一点时间研究了下。

我们没有使用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序列帧相关推荐

  1. Unity 3D模型动画导出为帧序列

    问题:将用于Unity的3D模型改成2D图片. 思路:把3D动画播放一遍,逐帧对模型截图. 代码说明: 1.要引用的命名空间 using System.IO; using UnityEngine; u ...

  2. 将Project的内容导出成单独的XPO文件

    AX跟VSS整合的版本管理可以通过创建知识库将当前层的代码全部签入到VSS中,但是如果不是一个团队开发solution,而是针对客户的需求随时做得一些小改动,一般都希望以Project的形式组织代码和 ...

  3. 将数据库查询结果导出成Excel表格

    使用Java代码,从数据库中获取结果集,将结果集导出成Excel表格形式. 从数据库中查询学生表所有数据,将其导出成Excel表格,点击查看学生表表结构 . package com.test.test ...

  4. Oracle中数据导出成CVS,EXCEL

    我们可以把oracle数据库中的某个表导出成CVS,可用excel方式打开该文件. 步骤一 选中需要导出的数据,右击->导出结果->csv文件 步骤二 查看导出的文件 步骤三 你可以在cs ...

  5. 解决eclipse中java项目导出成jar包后读写UTF-8文件中文乱码问题

    最近遇到了一个小麻烦,就是在eclipse环境中读写UTF-8格式的txt文件时很正常,但是当导出成jar包后,通过点击来读写文件时出现了部分中文乱码问题. 解决办法: 开始时用的是FileReade ...

  6. jsonhandle主界面没有显示格式_怎么将图纸导出成图片格式保存

    怎么将图纸导出成图片格式保存呢?接下来将与大家分享一下有关CAd图纸转换成图片进行保存的方法. 方法/步骤 为了实现CAd转图片操作,首先我们需要在电脑上安装一个CAD编辑软件,如图所示: 待CAD编 ...

  7. python抓取数据库数据封装成json_用Python将mysql数据导出成json的方法

    1.相关说明 此脚本可以将Mysql的数据导出成Json格式,导出的内容可以进行select查询确定. 数据传入参数有:dbConfigName, selectSql, jsonPath, fileN ...

  8. mongodb 导出 带条件_将 MongoDB 导出成 csv

    **[摘要]** 将 Mongodb 数据结构转换成结构化的数据需求,我们可利用集算器 SPL 语言来进行辅助实现.若想了解更多,请前往乾学院:将 MongoDB 导出成 csv! 来源:https: ...

  9. 【Selenium】导出成py脚本的基础使用

    0. Selenium简介 Selenium是一个用于Web应用程序测试的工具. Selenium测试直接运行在浏览器中,就像真正的用户在操作一样. 主流浏览器都支持.例如firefox,在插件市场里 ...

最新文章

  1. 计算机大师高德纳权威著作《计算机程序设计艺术》影印版精装版已经入库,即将上市!
  2. 导出勾选密码永不过期的AD账户信息
  3. [.Net线程处理系列]专题二:线程池中的工作者线程
  4. C语言水洼数算法,C++ 数据结构之水洼的数量算法
  5. 【NLP-ChatBot】能干活的聊天机器人-对话系统概述
  6. 【白皮书分享】2021内容营销白皮书.pdf(附下载链接)
  7. BZOJ.3165.[HEOI2013]Segment(李超线段树)
  8. FBI 连续第三次发布关于国家黑客利用 Kwampirs 发动全球供应链攻击的警告
  9. JAVA反射构建对象
  10. 智能服务器软件,无纸化智能会议系统服务器软件
  11. win10下安装ubuntu双系统
  12. 小刘同学的第五十五篇博文
  13. 2020-12-28
  14. 福禄克光纤测试:多模光纤 VS 单模光纤
  15. 【学习笔记】H5性能测试
  16. LoRaWAN入网方式以及加密进阶版
  17. ODI(Oracle Data Integrator)基本使用教程(1)
  18. 【新书推荐】崛起的超级智能:互联网大脑如何影响科技未来
  19. 一位中科院自动化研究所博士的毕业论文致谢:求学22载,计算机终成一生的事业与希望...
  20. AD936x_增益控制AGC详解

热门文章

  1. firefox 1.0中添加自定义搜索引擎——北大天网搜索引擎
  2. java 位与运算_java中位运算和移位运算详解
  3. [附源码]Java计算机毕业设计SSM爱宝贝影楼管理系统
  4. 小哨兵还原卡密码如何清楚?
  5. java配置多环境_[原创]多版本Java环境变量的配置
  6. ROS调用笔记本摄像头和外界摄像头问题汇总(本人亲身经历)非常好用
  7. 1005打印任务取消不了 hp_怎么办?HP1005打印机显示正在打印却打 – 手机爱问
  8. 易排通用规划平台,以Excel作为数据源的调用方法与数据文件说明
  9. 亚马逊卖家怎样让茶叶乘风破浪,远销海外?-跨境创业找众光
  10. 你还在用挂历记录大事件?试试这款动态日历表!