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;}}

这里对几个关键的知识点来做说明:

不付出,却一定不会有收获,不要奢望出现奇迹。

Unity将粒子光效导出成png序列帧

相关文章:

你感兴趣的文章:

标签云: