【Unity-Shader脚本】0基础学会通过用Unity-Shader脚本渲染图像数据(NV21,NV12,RGBA数据)详细教程--附demo,NV21测试图像,YUV图像查看器。

前言

  最近有一个需求是需要我在Unity中将获取到的图像数据来展示在Unity的界面之中。功能其实很简单,熟悉Unity-Shader的小伙伴可能很快就可以做出来。然而我很少和图像的渲染打交道,基本上是0基础了,在做这个需求之前连Shader是什么都不知道。本文记录了自己做这个需求所学习到的Unity-Shader的基础知识,以及渲染数据的方法和代码。文末会给出测试程序和代码,通过Unity可以直接运行。

  本文适合和我一样的0基础的小伙伴,需要实现在Unity中将图像数据(包括NA21,NV12,RGBA三种数据格式)绘制到Unity界面中。通过本片文章,你会学到以下内容:

  1. 看懂我给出的Unity-Shader的Demo,知道Demo是如何渲染图像数据的。
  2. 能够对Demo进行简单的修改来完成新的功能。(比如你要渲染BGRA的图像,或者你需要将渲染的图像镜像等)
  3. 学习到Unity-Shader最最基础和浅显的知识。

Unity-Shader代码部分讲解

什么是Unity-Shader

  Shader就是一种专门用来渲染图像的技术,通过Shader可以自定义显卡渲染方法,来展示出高大上的炫酷特效。通过Shader可以操作GPU去绘制模型中的每一个像素点的颜色。

  目前有三种Shader语言,他们分别是:

  1. 基于OpenGL的OpenGL Shading Language(GLSL)
  2. 基于DirextX的High Level Shading Language(HLSL)
  3. 基于NVIDIA公司的C for Graphic.(Cg)

  而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,那么它的格式既遵循Unity封装的标准,也需要遵循传统的Shader的格式(本例中使用的是Cg语言)。以本例的NV21图像的渲染Shader为例,Unity-Shader的基本格式包含两个部分:第一部分是Shader的名称。第二部分存放了图像像素数组,因为这个Shader是渲染NV21数据的,因此这里的图像属性有两个:分别表示Y通道像素值的数组和UV通道像素值的数组。最有一个部分SubShader就是告诉GPU对于这些数据的渲染的方法。(这里部分是使用Cg语言去实现的)
.csdnimg.cn/6af611ea597f4e78872ee9b3e8bf7f8d.png)
  第一部分和第二部分很简单了,这里就不做讲解了。最重要的部分就是第三部分,SubShader的编程,下面对这一部分的内容进行讲解。
  举起来说,在Unity-Shader中,Cg程序片段被放在Pass中,Pass又放在SubShader中。在CG程序片段之前,通常需要先使用 #pragma声明编译指令:
在这里插入图片描述
  下图是本例NV12-Shader中SubShader的实现。我在重要的部分加上了序号,这样方便讲解。
  第一个部分是Cg的一般规范,定义顶点着色器和片段着色器的名称(后面会讲解这两个东西是干啥的)。我们只用遵循它即可,一般不用修改。

  第二个部分是结构体的一些定义,是我自己写的为了方便数据格式的组织而已。

  最重点的部分就是第三个部分和第四个部分,我们要对程序进行修改,比如对数据格式为RGBA的图像进行渲染,或者图像进行镜像,就是在这里进行修改的。这两个部分也就是要实现两个函数:顶点着色器的实现和片段着色器的实现。这两个函数也分别对应着两个功能:顶点着色器用来定位位置,就是确定我现在要将像素渲染在界面上的哪一个位置。片段着色器用来确定这个点的像素具体是什么颜色的。了解了这些我们可以知道将NV21数据渲染到界面上的一个整体的流程

  1. 首先获取图像的数据,然后将Y通道的数据和UV通道的数据传到Unity-Shader脚本的Properties中(本例中以_YTex和_UVTex)保存。
  2. 通过顶点着色器依次找到界面上需要渲染的每一个像素的位置。
  3. 通过片段着色器,通过Properties中的属性值得到这些像素的颜色RGBA的值,然后进行渲染。
      第三个部分顶点着色器的实现是通过UnityObjectToClipPos函数实现的,这个函数是将这个是把顶点从模型空间直接转化到裁剪空间,也就是进行了M-V-P三次转化。如果只是进行将简单的图像2D数据渲染到界面上,那么我们也不需要进行特殊的修改。
      第四个部分片段着色器的实现。首先我们在顶点着色器中获得到要渲染像素的位置(v2f的数据类型)。然后要找到渲染点对应的_YTex和UVTexture的坐标,再通过这个坐标和properties的属性计算出像素y值,u值和v值,然后通过yuv转rgb的方法进行一个运算,计算出该像素点的rgb的值,进行一个返回。至于这里的寻找_YTex和_UVTexture的坐标的坐标代码为什么是 fixed2 uv = fixed2(i.uv.x, 1 - i.uv.y);在后面的调试方法的章节中会进行讲解。
    在这里插入图片描述

Unity-C#代码部分讲解

  上一节我们讲解了在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两个属性了
在这里插入图片描述

Unity-界面部分讲解及Demo使用说明

功能演示

  界面很简单,只包含一个,Scene界面上有一个RawImage,点击Start,就会将NV21数据读到Shader中,Shader接着就会对RawImage进行渲染,将图像输出到界面上。
在这里插入图片描述

控件的绑定关系

  控件的绑定关系是:首先创建一个RawImage,接着为这个RawImage创建一个Material(图中为DemoMaterial),在Material中选择我们写好的Shader脚本即可。
在这里插入图片描述

调试技巧

  在编译器中运行时,在Material中看到传入到Shader脚本中的两个属性(T Texture和UV Texture)。分别表示颜色信息和亮度信息,在图像渲染不正确的时候可以先检查这两个图像通道是否输入正确。
在这里插入图片描述

程序扩展

  在本文的Demo中,给出了NV21图像的渲染方法和Demo。对于其它的一些扩展,这里只提供思路,如果大家需要,可以留言评论一下,后面我再将其它的功能也整合到demo中。更多更炫酷的功能还是要大家自己开发啦。

NV12图像数据的渲染

  NV12的图像和NV21的图像非常接近,所以如果需要渲染NV12的图像格式是非常简单的,只需要在Shader脚本中将u,v变量交换即可。
在这里插入图片描述

BRG图像数据的渲染

  如果需要渲染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工程说明

  Demo工程包含一套整体的NV21图像的渲染的工程,包含一张测试图像。另外工程中给出了BRGA图像格式渲染的Shader脚本供参考,对应的C#部分代码需要大家补齐。最后给大家提供了一个YUV格式的图像查看器,大家可以进行辅助测试。
  获取Demo及YUV格式图像查看工具,关注公众号后发送:Unity-Shader即可。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41937380/article/details/128908256