美文网首页
使用Unity直接渲染YUV420格式

使用Unity直接渲染YUV420格式

作者: 我心若氺 | 来源:发表于2018-08-15 18:49 被阅读0次

    最近好久没来写些字了。

    工作中发现YUV420格式转为RGB格式,最后也还是要渲染的,
    所以考虑直接渲染,节约了CPU工作量,GPU做这种粗活非常擅长的。

    话不多说,开始编码。
    先说一下开发环境,使用的是,Windows10 + Unity5.3.6
    其实用什么版本的Unity没关系,(最新的Unity2018.2最好,这个版本非常强大)
    我写的是普通顶点/片段shader.

    1、了解YUV420格式

    这个可以看我的《YUV格式初探》嘿嘿。
    主要分为I420 , YV12, NV12, NV21.
    这4种都是12bits表达8个像素,比起rgb888格式,24bits表达8像素,节约了一半空间
    具体布局如下:
    I420: YYYYYYYY UU VV =>YUV420P

    YV12: YYYYYYYY VV UU =>YUV420P

    NV12: YYYYYYYY UVUV =>YUV420SP

    NV21: YYYYYYYY VUVU =>YUV420SP

    2、创建Shader

    首先创建一个空白shader,再创建一个材质球Material,把shader赋给它,
    然后把这个材质球Material挂到需要渲染的UI组件上去,比如Image/RawImage。

    3、创建贴图Texture

    根据上述的格式,可以看出:
    YUV420P里Y,U,V分量数据明显的分为3块,I420,YV12两种的区别只是U和V分量数据顺序不同罢了,并不影响我们使用。那我们可以创建 3 个贴图Texture,分别存放Y,U,V分量数据,然后再片段着色器里做 3 次采样Sampler,然后重新计算为RGB颜色。
    3 张贴图都使用8bits的格式,例如Alpha8。要注意U,V贴图的尺寸只有Y的1/4哦。
    YUV420SP里Y,U,V分量数据明显的分为2块,Y分量单独一块,UV分量混杂在一起作为一块。NV12,NV21两种的区别只是U和V分量数据顺序不同罢了。那我们就要创建 2 个贴图Texture,分别存放Y分量和UV分量数据,然后再片段着色器里做2次采样Sampler,然后重新计算为RGB颜色。
    Y分量贴图要使用8bits格式,例如Alpha8,UV分量贴图要使用16bits格式,例如我就用的RGBA4444,RGBA各占4bits,然后把RG通道重组为U, BA通道重组为V。要注意UV贴图的尺寸只有Y的1/4哦。

    float u = (uv4.r * 15 * 16 + uv4.g * 15) / 255 - 0.5; 
    float v = (uv4.b * 15 * 16 + uv4.a * 15) / 255 - 0.5;
    

    当然你也可以用其他格式比如RGB888,但你只用R,G通道等等。
    Do whatever you want.

    4、Show me the code

    话不多说,直接上代码了。
    我就以NV12格式举例:
    创建贴图

    m_YImageTex = new Texture2D(ImageXSize, ImageYSize, TextureFormat.Alpha8, false);
    m_UVImageTex = new Texture2D(ImageXSize / 2, ImageYSize / 2, TextureFormat.RGBA4444, false);
    

    给shader设置数据

    YUVTex.material.SetTexture("_MainTex" , YData);
    YUVTex.material.SetTexture("_UVTex", UVData); 
    

    shader本体

    Shader "Custom/UI/YUVRender"
    {
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            _UVTex ("Texture", 2D) = "white" {}
        }
        SubShader
        {
            // No culling or depth
            Cull Off ZWrite Off ZTest Always
    
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                };
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.uv = v.uv;
                    return o;
                }
                
                sampler2D _MainTex;
                sampler2D _UVTex;
    
                fixed4 frag (v2f i) : SV_Target
                {
                    fixed4 col = tex2D(_MainTex, i.uv);
                    fixed4 uv4 = tex2D(_UVTex, i.uv);
                    float y = 1.1643 * (col.a - 0.0625);
                    float u = (uv4.r * 15 * 16 + uv4.g * 15) / 255 - 0.5; 
                    float v = (uv4.b * 15 * 16 + uv4.a * 15) / 255 - 0.5;
    
                    //float r = y + 1.596 * v;
                    //float g = y - 0.391  * u - 0.813 * v;
                    //float b = y + 2.018  * u;
                    float r = y + 1.403 * v;
                    float g = y - 0.344  * u - 0.714 * v;
                    float b = y + 1.770  * u;
                    col.rgba = float4(r, g, b, 1.f);
                    return col;
                }
                ENDCG
            }
        }
    }
    
    

    然后就是每帧更新YData和UVData数据,就可以正常渲染了


    就酱。

    相关文章

      网友评论

          本文标题:使用Unity直接渲染YUV420格式

          本文链接:https://www.haomeiwen.com/subject/ynwwbftx.html