最近好久没来写些字了。
工作中发现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数据,就可以正常渲染了
就酱。
网友评论