Unity截图调用OpenGL绘制

这次研究的是一个屏幕截取并保存的功能。先上图。

1.Ctrl + Alt + X 开始截图。

2.Enter键执行截屏并弹出保存框。

3.截取后保存的图片。

大致的思路是这样:

1.GL类执行界面选框的显示。

2.用Texture2D类设置新的像素块颜色。

3.System.Windows.forms.dll弹出保存对话框,保存图片文件并打开。

难点在于GL框的绘制逻辑以及新的图片生成时屏幕坐标的计算。

代码如下:

ScreenShot类

using UnityEngine;
using System.Collections;
using System.Windows.Forms;
using System.IO;
using System.Text;
 
//截屏工具
public class ScreenShot : MonoBehaviour
{
	//选框材质
	public Material mat;
	//初始位置
	Vector3 startPos = Vector3.zero;
	//结束位置
	Vector3 endPos = Vector3.zero;
	//是否绘制
	bool isDraw = false;
	//是否清除
	bool isClear = true;
	
	void Update ()
	{
		//左键按下开始绘制
		if(Input.GetMouseButtonDown(0))
		{
			isDraw = true;
			isClear = true;
			startPos = Input.mousePosition;
		}
		//左键松开GL框悬停
		if(Input.GetMouseButtonUp(0))
		{
			//鼠标原位置点击不做GL绘制
			if(endPos == startPos)
			{
				isDraw = false;
			}
			isClear = false;
		}
		//右键按下重新绘制
		if(Input.GetMouseButtonDown(1))
		{
			isDraw = false;
			enabled = false;
		}
		//Enter键按下执行截屏
		if(Input.GetKeyDown(KeyCode.Return) && isDraw && !isClear)
		{
			isDraw = false;
			StartCoroutine(DoScreenShot(new Vector2(startPos.x,UnityEngine.Screen.height - startPos.y),new Vector2(endPos.x,UnityEngine.Screen.height - endPos.y)));
			enabled = false;
		}
	}
	
	void OnGUI ()
	{
		//文本提示
		if(!isDraw)
		{
			GUI.Label(new Rect(Input.mousePosition.x,UnityEngine.Screen.height - Input.mousePosition.y,100,100),"    请框选截图范围");
		}
		if(isDraw && !isClear && endPos != startPos)
		{
			GUI.Label(new Rect(Input.mousePosition.x,UnityEngine.Screen.height - Input.mousePosition.y,100,100),"    按下Enter键截屏");
		}
	}
	
	void OnPostRender ()
	{
		//绘制GL线框
		if(!isDraw)return;
		if(isClear)
		endPos = Input.mousePosition;
		
		GL.PushMatrix();
		if(!mat)return;
		mat.SetPass(0);
		GL.LoadPixelMatrix();
		//绘制GL填充面
		GL.Begin(GL.QUADS);
		GL.Color(new  Color(Color.yellow.r,Color.yellow.g,Color.yellow.b,0.1f));
		GL.Vertex3(startPos.x,startPos.y,0);
		GL.Vertex3(endPos.x,startPos.y,0);
		GL.Vertex3(endPos.x,endPos.y,0);
		GL.Vertex3(startPos.x,endPos.y,0);
		GL.End();
		//绘制GL边框线
		GL.Begin(GL.LINES);
		GL.Color(Color.red);
		GL.Vertex3(startPos.x,startPos.y,0);
		GL.Vertex3(endPos.x,startPos.y,0);
		GL.Vertex3(endPos.x,startPos.y,0);
		GL.Vertex3(endPos.x,endPos.y,0);
		GL.Vertex3(endPos.x,endPos.y,0);
		GL.Vertex3(startPos.x,endPos.y,0);
		GL.Vertex3(startPos.x,endPos.y,0);
		GL.Vertex3(startPos.x,startPos.y,0);
		GL.End();
		GL.PopMatrix();
	}
	
	//截屏
	IEnumerator DoScreenShot (Vector2 startPos,Vector2 endPos)
	{
		Debug.Log(new Rect(startPos.x < endPos.x ? startPos.x : endPos.x,startPos.y < endPos.y ? UnityEngine.Screen.height - endPos.y : UnityEngine.Screen.height - startPos.y,Mathf.Abs(endPos.x - startPos.x - 2),Mathf.Abs(endPos.y - startPos.y - 2)));
		yield return new WaitForSeconds(1);
		Texture2D tex = new Texture2D((int)Mathf.Abs(endPos.x - startPos.x - 2),(int)Mathf.Abs(endPos.y - startPos.y - 2),TextureFormat.RGB24,false);
		tex.ReadPixels (new Rect(startPos.x < endPos.x ? startPos.x : endPos.x,startPos.y < endPos.y ? UnityEngine.Screen.height - endPos.y : UnityEngine.Screen.height - startPos.y,Mathf.Abs(endPos.x - startPos.x - 2),Mathf.Abs(endPos.y - startPos.y - 2)),0,0);
		tex.Apply();
		Destroy(tex);
		SaveScreenShot(tex);
	}
	
	//保存文件
	void SaveScreenShot (Texture2D tex)
	{
		//先将文件保存在工程目录下(其实可以不用这么做)
		byte[] bytes = tex.EncodeToPNG();
		string filename = System.DateTime.Now.Year.ToString("0000") + System.DateTime.Now.Month.ToString("00") + System.DateTime.Now.Day.ToString("00") + System.DateTime.Now.Hour.ToString("00") + System.DateTime.Now.Minute.ToString("00") + System.DateTime.Now.Second.ToString("00") + Random.Range(0,9999).ToString("0000");
		string oldFilePath = UnityEngine.Application.dataPath + "/" + filename + ".png";
		FileStream f =new FileStream(oldFilePath,FileMode.Create);
		f.Write(bytes,0,bytes.Length);
		f.Flush();
		f.Close();
		System.GC.Collect();
		Resources.UnloadUnusedAssets();
		
		//弹出文件保存对话框,调用的是WINDOWS的DLL.
		SaveFileDialog dialog = new SaveFileDialog();
		dialog.Filter = "png|*.png";
		string newFilePath = "";
		if (dialog.ShowDialog() == DialogResult.OK)
		{
			newFilePath = dialog.FileName;
		}
		if(newFilePath != "")
		{
			if(File.Exists(newFilePath))
			{
				File.Delete(newFilePath);
			}
			File.Move(oldFilePath, newFilePath);				
			UnityEngine.Application.OpenURL(newFilePath);
		}
		else 
		{
			File.Delete(oldFilePath);
		}
	}
}

ScreenShotControl类

using UnityEngine;
using System.Collections;
 
//截屏控制
public class ScreenShotControl : MonoBehaviour 
{
	ScreenShot screenshot;
	
	// Use this for initialization
	void Start () 
	{
		screenshot = GetComponent<ScreenShot>();
	}
	
	// Update is called once per frame
	void Update () 
	{
		
	}
	
	void OnGUI ()
	{
		//Ctrl + Alt + X 执行截屏
		if(Event.current.Equals(Event.KeyboardEvent("^&X")))
		{
			screenshot.enabled = true;
		}
	}
}

阐述几个开发中的问题:
1.调用Windows的DLL,需要在Unity里更改一下设置。

File -> Build Settings -> Player Settings ->Other Settings -> Optimization -> Api Compatibility Level 更改为 .NET 2.0。


2.在执行完截屏操作后,虽然能够成功生成图片,但是会报一个错。

ReadPixels was called to read pixels from system frame buffer, while not inside drawing frame.

如果有高手知道具体的解决办法,请指教一下。

3.程序的代码结构写的不是太好,请高手指正。

猜你喜欢

转载自blog.csdn.net/Happy_zailing/article/details/83865516