赞
踩
最近有一个需求是需要我在Unity中将获取到的图像数据来展示在Unity的界面之中。功能其实很简单,熟悉Unity-Shader的小伙伴可能很快就可以做出来。然而我很少和图像的渲染打交道,基本上是0基础了,在做这个需求之前连Shader是什么都不知道。本文记录了自己做这个需求所学习到的Unity-Shader的基础知识,以及渲染数据的方法和代码。文末会给出测试程序和代码,通过Unity可以直接运行。
本文适合和我一样的0基础的小伙伴,需要实现在Unity中将图像数据(包括NA21,NV12,RGBA三种数据格式)绘制到Unity界面中。通过本片文章,你会学到以下内容:
Shader就是一种专门用来渲染图像的技术,通过Shader可以自定义显卡渲染方法,来展示出高大上的炫酷特效。通过Shader可以操作GPU去绘制模型中的每一个像素点的颜色。
目前有三种Shader语言,他们分别是:
而Unity-Shader,其实Unity对Shader的一层封装。如果在Unity中使用Shader,那你就不用深究使用以上哪三个语言来实现Shader技术,你只用专注于Unity平台中的Shader脚本的规则。
在Unity平台中,Unity-Shader也有三种不同的书写方法,他们分别是:Surface Shaders 表面着色器(功能最强大);Vertex/Fragment Shaders 顶点/片断着色器(最主流);Fixed Function Shaders 固定管线着色器(已经废弃);
在三种方法中,Fixed Function Shaders已经被淘汰,完全没有学习的必要了。Surface Shaders 是功能最强大的,Vertex/Fragment Shaders 顶点/片断着色器是最主流的。基本上目前的Unity-Shader的教程都是以Vertex/Fragment Shader来讲解的。当然本文的工程也是使用Vertex/Fragment Shader来实现的。
既然是Unity-Shader,那么它的格式既遵循Unity封装的标准,也需要遵循传统的Shader的格式(本例中使用的是Cg语言)。以本例的NV21图像的渲染Shader为例,Unity-Shader的基本格式包含两个部分:第一部分是Shader的名称。第二部分存放了图像像素数组,因为这个Shader是渲染NV21数据的,因此这里的图像属性有两个:分别表示Y通道像素值的数组和UV通道像素值的数组。最有一个部分SubShader就是告诉GPU对于这些数据的渲染的方法。(这里部分是使用Cg语言去实现的)
第一部分和第二部分很简单了,这里就不做讲解了。最重要的部分就是第三部分,SubShader的编程,下面对这一部分的内容进行讲解。
举起来说,在Unity-Shader中,Cg程序片段被放在Pass中,Pass又放在SubShader中。在CG程序片段之前,通常需要先使用 #pragma声明编译指令:
下图是本例NV12-Shader中SubShader的实现。我在重要的部分加上了序号,这样方便讲解。
第一个部分是Cg的一般规范,定义顶点着色器和片段着色器的名称(后面会讲解这两个东西是干啥的)。我们只用遵循它即可,一般不用修改。
第二个部分是结构体的一些定义,是我自己写的为了方便数据格式的组织而已。
最重点的部分就是第三个部分和第四个部分,我们要对程序进行修改,比如对数据格式为RGBA的图像进行渲染,或者图像进行镜像,就是在这里进行修改的。这两个部分也就是要实现两个函数:顶点着色器的实现和片段着色器的实现。这两个函数也分别对应着两个功能:顶点着色器用来定位位置,就是确定我现在要将像素渲染在界面上的哪一个位置。片段着色器用来确定这个点的像素具体是什么颜色的。了解了这些我们可以知道将NV21数据渲染到界面上的一个整体的流程:
上一节我们讲解了在Unity-Shader中是如何对图像进行渲染的。在Unity-Shader进行数据渲染之前,我们在Unity的C#语言中当然还需要将我们的数据传给Unity-Shader了。通过上节的讲解我们现在应该知道,Unity中的NV12数据传入到Unity-Shader中的_YTex和_UVTex了。这里简单讲一下Unity的数据是如何传入的。代码很简单:首先我们要创建一个_dataY和_dataUV,他们分别对应着_YTex和_UVTex。这里需要注意的是new时候的数据的定义。因为对于YUV数据类型来说,Y的数据量是UV数据量的两倍。所以这里创建_UVTex时,宽和长都除以了2.另外因为UV数据类型包含两个值,8位的U值和8位的V值,所以这里对于_YTex来说第三个参数是TextureFormat.R8,而对于_UVTex来说第三个参数是TextureFormat.RG16。
初始化数组以后,这里通过读文件的方法将数据读到数组中,完成了_dataY和_dataUV的赋值。
最后,将数据传入到 Texture2D _texY和 Texture2D _texUV中,使用SetTexture就将数据传到Shader脚本的_YTex和_UVTexture两个属性了
界面很简单,只包含一个,Scene界面上有一个RawImage,点击Start,就会将NV21数据读到Shader中,Shader接着就会对RawImage进行渲染,将图像输出到界面上。
控件的绑定关系是:首先创建一个RawImage,接着为这个RawImage创建一个Material(图中为DemoMaterial),在Material中选择我们写好的Shader脚本即可。
在编译器中运行时,在Material中看到传入到Shader脚本中的两个属性(T Texture和UV Texture)。分别表示颜色信息和亮度信息,在图像渲染不正确的时候可以先检查这两个图像通道是否输入正确。
在本文的Demo中,给出了NV21图像的渲染方法和Demo。对于其它的一些扩展,这里只提供思路,如果大家需要,可以留言评论一下,后面我再将其它的功能也整合到demo中。更多更炫酷的功能还是要大家自己开发啦。
NV12的图像和NV21的图像非常接近,所以如果需要渲染NV12的图像格式是非常简单的,只需要在Shader脚本中将u,v变量交换即可。
如果需要渲染BGRA的图像,首先我们数据的输入就不需要两个通道了(之前是Y通道和UV通道),因此我们需要,在properties中我只需要一个_BGRATex即可,然后在顶点着色器中直接取出BGRA四个分量值进行返回即可。
关于BGRA在C#中对应的Texture2D也需要注意,因为它的数据格式变了,相应的Texture2D的初始化部分也和NV21部分有差异。(TextureFormat变成了RGBA32)
关于BGRA的Shader脚本我也放在Demo工程中,但是对应的C#部分是缺失的,有需要的同学可以找我要或者自行补充。
镜像:其实就是一个图像左右翻转的过程。Shaer脚本的顶点着色器部分中: fixed2 uv = fixed2(i.uv.x, 1 - i.uv.y);这一句话就是控制扫描的方向。如果需要镜像,我们只需要将图像扫描的方式从由左到右变成由右到左。因此控制一下这一句,将代码改成fixed2 uv = fixed2(1- i.uv.x, 1 - i.uv.y);即可。
Demo工程包含一套整体的NV21图像的渲染的工程,包含一张测试图像。另外工程中给出了BRGA图像格式渲染的Shader脚本供参考,对应的C#部分代码需要大家补齐。最后给大家提供了一个YUV格式的图像查看器,大家可以进行辅助测试。
获取Demo及YUV格式图像查看工具,关注公众号后发送:Unity-Shader即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。