我们知道在屏幕后处理里面通过 Graphics.Blit 函数可以通过材质处理屏幕图片, 当我们想要处理一般图片的时候, 直接调用GL函数就行了, 按照习惯自己封装一个 Blit 方法 :
public static void Blit(Texture source, Material material, RenderTexture destination, int materialPass = 0) { if(material.SetPass(materialPass)) { material.mainTexture = source; Graphics.SetRenderTarget(destination); GL.PushMatrix(); GL.LoadOrtho(); GL.Begin(GL.QUADS); { Vector3 coords = new Vector3(0, 0, 0); GL.TexCoord(coords); GL.Vertex(coords); coords = new Vector3(1, 0, 0); GL.TexCoord(coords); GL.Vertex(coords); coords = new Vector3(1, 1, 0); GL.TexCoord(coords); GL.Vertex(coords); coords = new Vector3(0, 1, 0); GL.TexCoord(coords); GL.Vertex(coords); } GL.End(); GL.PopMatrix(); } }
因为 Graphics.SetRenderTarget 方法传入的是 RenderTexture, 渲染出来的 RenderTexture 不能直接当成 Texture2D 或 Cubemap 或 Texture3D 等来使用, 一般需要进行二次转换. 就拿 Texture2D 来作为例子, 转换方法貌似有那么几种, 下来看看 :
0. 各个变量
public Material material; public Texture2D input; public Texture2D outPutTex2D; public RenderTexture renderTexture;
1. 使用指针的方式, 一般来说如果 RenderTexture 的内存跟 Texture2D 一样的话, 用 Texture2D 直接指向 RenderTexture 的相关地址应该就可以了, 因为官方没有文档直接就测试代码 :
private void Start() { if(renderTexture == false) { renderTexture = RenderTexture.GetTemporary(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); } renderTexture.hideFlags = HideFlags.DontSave; outPutTex2D = Texture2D.CreateExternalTexture(Screen.width, Screen.height, TextureFormat.ARGB32, false, true, renderTexture.GetNativeTexturePtr()); // ArgumentException: nativeTex can not be null }
在渲染 RenderTexture 之前获取它的 GetNativeTexturePtr 是不行的, 报错. 改一下, 在渲染完之后再获取的话 :
private void Start() { if(renderTexture == false) { renderTexture = RenderTexture.GetTemporary(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); } renderTexture.hideFlags = HideFlags.DontSave; Blit(input, material, renderTexture); var nativeTexturePtr = renderTexture.GetNativeTexturePtr(); if(nativeTexturePtr != System.IntPtr.Zero) { outPutTex2D = Texture2D.CreateExternalTexture(Screen.width, Screen.height, TextureFormat.ARGB32, false, true, nativeTexturePtr); } }
直接就崩了, 虽然断点看到它的 nativeTexturePtr 确实能获取到, 不过想到 RenderTexture 在创建的时候没有指定是哪种内存, 直接用指针来创建 Texture2D 应该就是会崩的吧.
因为官方文档啥也没写, 估计这条路走不通...
2. 使用 Texture2D.ReadPixels 方法, 这个是最常见的方法 :
private void Start() { int W = (int)Screen.width; int H = (int)Screen.height; if(renderTexture == false) { renderTexture = RenderTexture.GetTemporary(W, H, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); } renderTexture.hideFlags = HideFlags.DontSave; outPutTex2D = new Texture2D(W, H, TextureFormat.ARGB32, false, true); Blit(input, material, renderTexture); var current = RenderTexture.active; RenderTexture.active = renderTexture; outPutTex2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); outPutTex2D.Apply(); RenderTexture.active = current; }
这个最常用, 结果也是正确的没什么好说的, 只是效率堪忧
3. 调用 Graphics.CopyTexture 复制图片 :
private void Start() { int W = (int)Screen.width; int H = (int)Screen.height; if(renderTexture == false) { renderTexture = RenderTexture.GetTemporary(W, H, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); } renderTexture.hideFlags = HideFlags.DontSave; outPutTex2D = new Texture2D(W, H, TextureFormat.ARGB32, false, true); Blit(input, material, renderTexture); if((SystemInfo.copyTextureSupport & UnityEngine.Rendering.CopyTextureSupport.RTToTexture) != 0) { Graphics.CopyTexture(renderTexture, outPutTex2D); } }
这个应该就是上面的使用指针地址进行复制的封装, 效率杠杠的. 而且也不需要对应类型.