简单记录一下这两天用Texture实现渲染YUV420P的一些要点。

在视频播放的过程中,有的时候解码出来的数据是YUV420P的。表面(surface)通过设置参数是可以渲染YUV420P的,但Texture纹理似乎不支持直接渲染YUV420P。表面(surface)用法似乎比较单一,通常用来显示数据,用Texture的话就可以用上D3D的许多其他功能,做出一些效果。当然,这看个人需求,通常而言显示视频数据用表面就够了。

1.利用像素着色器把YUV420P数据转为RGB32

视频播放过程中帧与帧之间是有固定时间间隔的。若解码解出来的是YUV420P的数据,要用Texture渲染的话,就需要把数据转为RGB32的(应该是要转成RGB32的,没做过详细调查,看到的例子中纹理所用的数据都是RGB32的),如果这个过程交给CPU去做的话,会提高CPU的占用率,用GPU来做这项工作则就释放了CPU的一部分压力。

本文考虑的方式是用三层纹理分别存放YUV420P的Y、U、V分量(这个词不知道用对没有),然后像素着色器把三个分量的数据计算成RGB32的数据然后显示。这是本文的核心内容。

像素着色器的HLSL代码如下:

  1. sampler YTex;
  2. sampler UTex;
  3. sampler VTex;
  4.  
  5. struct PS_INPUT
  6. {
  7. float2 y : TEXCOORD0;
  8. float2 u : TEXCOORD1;
  9. float2 v : TEXCOORD2;
  10. };
  11.  
  12. float4 Main(PS_INPUT input):COLOR0
  13. {
  14. float y = tex2D(YTex,input.y).r;
  15. float u = tex2D(UTex, input.u.xy / 2).r - 0.5f;
  16. float v = tex2D(VTex,input.v.xy / 2).r - 0.5f;
  17.  
  18. float r = y + 1.14f * v;
  19. float g = y - 0.394f * u - 0.581f * v;
  20. float b = y + 2.03f * u;
  21.  
  22. return float4(r,g,b, 1);
  23. }

HLSL代码可以直接写在txt文件中,sampler可视作标识纹理层和采样级的对象,Direct3D将把每一个sampler对象唯一地与某一纹理层关联起来。具体的HLSL语法请自行查资料,我也是粗略知道是怎么回事,就不误人子弟了。在代码中通过调用D3DXCompileShaderFromFile函数可以从文件编译像素着色器。但实际上,我个人不是很喜欢这种把代码放在一个单独文件里面的做法,这种代码应该尽可能的编进exe里面。但是我还只是初步了解D3D,不知道怎么把它编进exe里面,如果有人知道,还望指教。

  1. ID3DXBuffer* shader = 0;
  2. ID3DXBuffer* errorBuffer = 0;
  3.  
  4. hr = D3DXCompileShaderFromFile(
  5. "ps_multitex.txt",
  6. 0,
  7. 0,
  8. "Main", // entry point function name
  9. "ps_2_0",
  10. D3DXSHADER_DEBUG,
  11. &shader,
  12. &errorBuffer,
  13. &MultiTexCT);
  14.  
  15. // output any error messages
  16. if( errorBuffer )
  17. {
  18. ::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
  19. d3d::Release<ID3DXBuffer*>(errorBuffer);
  20. }
  21.  
  22. if(FAILED(hr))
  23. {
  24. ::MessageBox(0, "D3DXCompileShaderFromFile() - FAILED", 0, 0);
  25. return false;
  26. }
  27.  
  28. //
  29. // Create Pixel Shader
  30. //
  31. hr = Device->CreatePixelShader(
  32. (DWORD*)shader->GetBufferPointer(),
  33. &MultiTexPS);
  34.  
  35. if(FAILED(hr))
  36. {
  37. ::MessageBox(0, "CreateVertexShader - FAILED", 0, 0);
  38. return false;
  39. }
  40.  
  41. d3d::Release<ID3DXBuffer*>(shader);

以上代码中,D3DXCompileShaderFromFile函数从文件ps_multitex.txt编译HLSL代码;参数Main是HLSL代码的入口函数,如上一点代码中所见。这个入口函数可以是自定义的其他的,但要注意保持一致;ps_2_0表示像素着色器的版本。CreatePixelShader函数创建像素着色器。

2.sampler与纹理关联

创建纹理层。本文实现YUV420P渲染的方法采用了三层纹理,每层纹理分别存放Y、U、V数据。

  1. Device->CreateTexture ( Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &YTex, NULL ) ;
  2. Device->CreateTexture ( Width / 2, Height / 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &UTex, NULL ) ;
  3. Device->CreateTexture ( Width / 2, Height / 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &VTex, NULL ) ;

sampler与纹理的关联

  1. //
  2. // Get Handles
  3. //
  4.  
  5. YTexHandle = MultiTexCT->GetConstantByName(0, "YTex");
  6. UTexHandle = MultiTexCT->GetConstantByName(0, "UTex");
  7. VTexHandle = MultiTexCT->GetConstantByName(0, "VTex");
  8.  
  9. //
  10. // Set constant descriptions:
  11. //
  12.  
  13. UINT count;
  14.  
  15. MultiTexCT->GetConstantDesc(YTexHandle, &YTexDesc, &count);
  16. MultiTexCT->GetConstantDesc(UTexHandle, &UTexDesc, &count);
  17. MultiTexCT->GetConstantDesc(VTexHandle, &VTexDesc, &count);
  18.  
  19. MultiTexCT->SetDefaults(Device);

设置纹理/sampler的状态,这一部分我是在渲染的时候做的,也可以直接写在HLSL代码中。在后面渲染部分还会见到这些代码,其实是同一段代码,我只是为了表述纹理与sampler关联的一个整体过程,把它预先从渲染部分截了出来,希望不会造成误解。

  1. // Y tex
  2. Device->SetTexture( YTexDesc.RegisterIndex, YTex);
  3. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
  4. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
  5. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
  6. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
  7. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
  8.  
  9. // U tex
  10. Device->SetTexture( UTexDesc.RegisterIndex, UTex);
  11. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
  12. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
  13. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
  14. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
  15. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
  16.  
  17. // string tex
  18. Device->SetTexture( VTexDesc.RegisterIndex, VTex);
  19. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
  20. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
  21. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
  22. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
  23. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);

3.渲染YUV420P

获得YUV420P数据。本文直接读取的YUV420P数据。

打开文件代码:

  1. if((infile=fopen("test_yuv420p_320x180.yuv", "rb"))==NULL){
  2. printf("cannot open this file\n");
  3. return false;
  4. }

读取数据并将数据copy到纹理中:

  1. if (fread(buf, 1, Width*Height*3/2, infile) != Width*Height*3/2){
  2. // Loop
  3. fseek(infile, 0, SEEK_SET);
  4. fread(buf, 1, Width*Height*3/2, infile);
  5. }
  6.  
  7. //
  8. // Render
  9. //
  10.  
  11. Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
  12.  
  13. plane[0] = buf;
  14. plane[1] = plane[0] + Width*Height;
  15. plane[2] = plane[1] + Width*Height/4;
  16.  
  17. D3DLOCKED_RECT d3d_rect;
  18. byte *pSrc = buf;
  19. //Locks a rectangle on a texture resource.
  20. //And then we can manipulate pixel data in it.
  21. LRESULT lRet = YTex->LockRect(0, &d3d_rect, 0, 0);
  22. if (FAILED(lRet)){
  23. return false;
  24. }
  25. // Copy pixel data to texture
  26. byte *pDest = (byte *)d3d_rect.pBits;
  27. int stride = d3d_rect.Pitch;
  28. for(int i = 0;i < Height;i ++){
  29. memcpy(pDest + i * stride,plane[0] + i * Width, Width);
  30. }
  31.  
  32. YTex->UnlockRect(0);
  33.  
  34. D3DLOCKED_RECT d3d_rect1;
  35. lRet = UTex->LockRect(0, &d3d_rect1, 0, 0);
  36. if (FAILED(lRet)){
  37. return false;
  38. }
  39. // Copy pixel data to texture
  40. byte *pDest1 = (byte *)d3d_rect1.pBits;
  41. int stride1 = d3d_rect1.Pitch;
  42. for(int i = 0;i < Height/2;i ++){
  43. memcpy(pDest1 + i * stride1 / 2,plane[1] + i * Width / 2, Width / 2);
  44. }
  45.  
  46. UTex->UnlockRect(0);
  47.  
  48. D3DLOCKED_RECT d3d_rect2;
  49. lRet = VTex->LockRect(0, &d3d_rect2, 0, 0);
  50. if (FAILED(lRet)){
  51. return false;
  52. }
  53. // Copy pixel data to texture
  54. byte *pDest2 = (byte *)d3d_rect2.pBits;
  55. int stride2 = d3d_rect2.Pitch;
  56. for(int i = 0;i < Height/2;i ++){
  57. memcpy(pDest2 + i * stride2 / 2,plane[2] + i * Width / 2, Width / 2);
  58. }
  59.  
  60. VTex->UnlockRect(0);

渲染:

  1. Device->BeginScene();
  2.  
  3. Device->SetPixelShader(MultiTexPS);
  4. Device->SetFVF(MultiTexVertex::FVF);
  5. Device->SetStreamSource(0, QuadVB, 0, sizeof(MultiTexVertex));
  6.  
  7. // Y tex
  8. Device->SetTexture( YTexDesc.RegisterIndex, YTex);
  9. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
  10. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
  11. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
  12. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
  13. Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
  14.  
  15. // U tex
  16. Device->SetTexture( UTexDesc.RegisterIndex, UTex);
  17. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
  18. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
  19. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
  20. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
  21. Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
  22.  
  23. // string tex
  24. Device->SetTexture( VTexDesc.RegisterIndex, VTex);
  25. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
  26. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
  27. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
  28. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
  29. Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
  30.  
  31. Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
  32.  
  33. Device->EndScene();
  34. Device->Present(0, 0, 0, 0);

D3D Texture纹理渲染YUV420P的主要就是以上一些内容。完整工程代码:http://download.csdn.net/download/qq_33892166/9702415

本文的HLSL代码写出来的像素着色器画面有些偏黄,知道如何优化的朋友还请指教。

--------------------------------------------------------------2016.12.13 更新---------------------------------------------

本次更新修正遗留的画质问题。

更新1.HLSL代码

HLSL代码中并不需要对U、V做除以2。新HLSL代码:

  1. sampler YTex;
  2. sampler UTex;
  3. sampler VTex;
  4.  
  5. struct PS_INPUT
  6. {
  7. float2 y : TEXCOORD0;
  8. float2 u : TEXCOORD1;
  9. float2 v : TEXCOORD2;
  10. };
  11.  
  12. float4 Main(PS_INPUT input):COLOR0
  13. {
  14. float y = tex2D(YTex,input.y).r;
  15. //这里不需要除以2
  16. float u = tex2D(UTex,input.u.xy).r - 0.5f;
  17. float v = tex2D(VTex,input.v.xy).r - 0.5f;
  18.  
  19. float r = y + 1.14f * v;
  20. float g = y - 0.394f * u - 0.581f * v;
  21. float b = y + 2.03f * u;
  22.  
  23. return float4(r,g,b, 1);
  24. }

更新2.copy数据到texture

从内存copy数据到显卡的时候,U、V的数据copy上出了问题。新的读取数据并将数据copy到纹理中:

  1. if (fread(buf, 1, Width*Height*3/2, infile) != Width*Height*3/2){
  2. // Loop
  3. fseek(infile, 0, SEEK_SET);
  4. fread(buf, 1, Width*Height*3/2, infile);
  5. }
  6.  
  7. //
  8. // Render
  9. //
  10.  
  11. Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
  12.  
  13. plane[0] = buf;
  14. plane[1] = plane[0] + Width*Height;
  15. plane[2] = plane[1] + Width*Height/4;
  16.  
  17. D3DLOCKED_RECT d3d_rect;
  18. byte *pSrc = buf;
  19. //Locks a rectangle on a texture resource.
  20. //And then we can manipulate pixel data in it.
  21. LRESULT lRet = YTex->LockRect(0, &d3d_rect, 0, 0);
  22. if (FAILED(lRet)){
  23. return false;
  24. }
  25. // Copy pixel data to texture
  26. byte *pDest = (byte *)d3d_rect.pBits;
  27. int stride = d3d_rect.Pitch;
  28. for(int i = 0;i < Height;i ++){
  29. memcpy(pDest + i * stride,plane[0] + i * Width, Width);
  30. }
  31.  
  32. YTex->UnlockRect(0);
  33.  
  34. D3DLOCKED_RECT d3d_rect1;
  35. lRet = UTex->LockRect(0, &d3d_rect1, 0, 0);
  36. if (FAILED(lRet)){
  37. return false;
  38. }
  39. // Copy pixel data to texture
  40. byte *pDest1 = (byte *)d3d_rect1.pBits;
  41. int stride1 = d3d_rect1.Pitch;
  42. for(int i = 0;i < Height/2;i ++){
  43. //修改1:这个地方不需要除以2
  44. memcpy(pDest1 + i * stride1,plane[1] + i * Width / 2, Width / 2);
  45. }
  46.  
  47. UTex->UnlockRect(0);
  48.  
  49. D3DLOCKED_RECT d3d_rect2;
  50. lRet = VTex->LockRect(0, &d3d_rect2, 0, 0);
  51. if (FAILED(lRet)){
  52. return false;
  53. }
  54. // Copy pixel data to texture
  55. byte *pDest2 = (byte *)d3d_rect2.pBits;
  56. int stride2 = d3d_rect2.Pitch;
  57. for(int i = 0;i < Height/2;i ++){
  58. //修改1:这个地方也不需要除以2
  59. memcpy(pDest2 + i * stride2,plane[2] + i * Width / 2, Width / 2);
  60. }
  61.  
  62. VTex->UnlockRect(0);

完整工程代码:http://download.csdn.net/download/qq_33892166/9710622

D3D三层Texture纹理经像素着色器实现渲染YUV420P的更多相关文章

  1. HLSL像素着色器

    原文:HLSL像素着色器 昨日不可追, 今日尤可为.勤奋,炽诚,不忘初心 手机淘宝二维码 扫描       或者打开连接:程序设计开发 ,掌声鼓励,欢迎光临.     像素着色器替代了固定渲染管线的  ...

  2. DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区

    前言 由于在Direct3D 11中取消了固定管线,要想绘制图形必须要了解可编程渲染管线的流程,一个能绘制出图形的渲染管线最少需要有这两个可编程着色器:顶点着色器和像素着色器. 本章会直接跳过渲染管线 ...

  3. 片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但

    片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但片元着色器是一个更合适的名字, 因为此时的片元并不是一个真正意义上的像素.

  4. WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码

    原文:WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码 HLSL,High Level Shader Language,高级着色器语言,是 Di ...

  5. ThreeJS 物理材质shader源码分析(像素着色器)

    再此之前推荐一款GLTF物理材质在线编辑器https://tinygltf.xyz/ 像素着色器(meshphysical_frag.glsl) #define PHYSICAL uniform ve ...

  6. 3.QOpenGLWidget-通过着色器来渲染渐变三角形

    在上章2.通过QOpenGLWidget绘制三角形,我们学习绘制三角形还是单色的,本章将为三角形每个顶点着色.   1.着色器描述 着色器的开头总是要声明版本,接着是输入和输出变量.uniform和m ...

  7. Unity3d之Shader编程:子着色器、通道与标签的写法 & 纹理混合

    一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器 ...

  8. 【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 &amp; 纹理混合

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html 作者:毛星云 ...

  9. Shader开发之三大着色器

    固定功能管线着色器Fixed Function Shaders 固定功能管线着色器的关键代码一般都在Pass的材质设置Material{}和纹理设置SetTexture{}部分. Shader &qu ...

随机推荐

  1. Java中的泛型 (上) - 基本概念和原理

    本节我们主要来介绍泛型的基本概念和原理 后续章节我们会介绍各种容器类,容器类可以说是日常程序开发中天天用到的,没有容器类,难以想象能开发什么真正有用的程序.而容器类是基于泛型的,不理解泛型,我们就难以 ...

  2. Java进击C#——应用开发之Linq和EF

    本章简言 上一章笔者对于WinForm开发过程用到的几个知识点做了讲解.笔者们可以以此为开端进行学习.而本章我们来讲一个跟ORM思想有关的知识点.在讲之前让我们想一下关于JAVA的hibernate知 ...

  3. NopCommerce 在Category 显示Vendor List列表

    实现效果如下: 1.在前台Web的Category Menu显示 Vendor; 2.点击Vendor 显示Vendor List列表: 主要配置步骤: 1.运行网站 Admin 后台   Categ ...

  4. C++四种类型转换方式。

    类型转换有c风格的,当然还有c++风格的.c风格的转换的格式很简单(TYPE)EXPRESSION,但是c风格的类型转换有不少的缺点,有的时候用c风格的转换是不合适的,因为它可以在任意类型之间转换,比 ...

  5. IOS 2D游戏开发框架 SpriteKit-->续(创建敌对精灵)

    这次包括之后讲的spritekit 我都会围绕一个案例来说,这个案例就是一个简单的2d飞机大战游戏,今天这里我讲创建敌对精灵,就是敌对飞机,敌对飞机不停的被刷新到屏幕上.....当然这里涉及到的类其实 ...

  6. nw.js自定义最小化图标的click事件

    选择frameless时,最小化和关闭按钮的点击事件需要自己来做,办法是: /* * 下面两个模块一定要引入到js文件中 */ var gui = require('nw.gui'); var win ...

  7. win10 安装visual studio 2015遇到的坑

    最近win7系统不知啥原因无法访问域中的网络文件,打算升级到win10体验一下.结果发现这一路有太多的坑.首先安装win10基本上算顺利,但是当进入系统后,菜单模式对于PC的鼠标来说,用起来感觉不顺手 ...

  8. jQuery为开发插件提拱了两个方法:jQuery.fn.extend(); jQuery.extend();

    jQuery为开发插件提拱了两个方法,分别是: jQuery.fn.extend(); jQuery.extend(); jQuery.fn jQuery.fn = jQuery.prototype ...

  9. CSS3之盒子模型

    display:box 使子元素成行排列如果父级宽度小于子级盒子 不会把超出部分挤出下面 而是直接超出 -box-orient:vertical 使盒子垂直显示  默认水平显示 -box-direct ...

  10. Critical: Update Your Windows Secure Channel (cve-2014-6321,MS14-066)

    前言:风雨欲来山满楼,下半年开始各种凶猛的漏洞层出不穷,天下已经不太平,互联网已经进入一个新的台阶 0x01 cve-2014-6321 11月的补丁月,微软请windows的用户吃了顿大餐,发布了1 ...