D3D三层Texture纹理经像素着色器实现渲染YUV420P
简单记录一下这两天用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代码如下:
sampler YTex;
sampler UTex;
sampler VTex; struct PS_INPUT
{
float2 y : TEXCOORD0;
float2 u : TEXCOORD1;
float2 v : TEXCOORD2;
}; float4 Main(PS_INPUT input):COLOR0
{
float y = tex2D(YTex,input.y).r;
float u = tex2D(UTex, input.u.xy / 2).r - 0.5f;
float v = tex2D(VTex,input.v.xy / 2).r - 0.5f; float r = y + 1.14f * v;
float g = y - 0.394f * u - 0.581f * v;
float b = y + 2.03f * u; return float4(r,g,b, 1);
}
HLSL代码可以直接写在txt文件中,sampler可视作标识纹理层和采样级的对象,Direct3D将把每一个sampler对象唯一地与某一纹理层关联起来。具体的HLSL语法请自行查资料,我也是粗略知道是怎么回事,就不误人子弟了。在代码中通过调用D3DXCompileShaderFromFile函数可以从文件编译像素着色器。但实际上,我个人不是很喜欢这种把代码放在一个单独文件里面的做法,这种代码应该尽可能的编进exe里面。但是我还只是初步了解D3D,不知道怎么把它编进exe里面,如果有人知道,还望指教。
ID3DXBuffer* shader = 0;
ID3DXBuffer* errorBuffer = 0; hr = D3DXCompileShaderFromFile(
"ps_multitex.txt",
0,
0,
"Main", // entry point function name
"ps_2_0",
D3DXSHADER_DEBUG,
&shader,
&errorBuffer,
&MultiTexCT); // output any error messages
if( errorBuffer )
{
::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
d3d::Release<ID3DXBuffer*>(errorBuffer);
} if(FAILED(hr))
{
::MessageBox(0, "D3DXCompileShaderFromFile() - FAILED", 0, 0);
return false;
} //
// Create Pixel Shader
//
hr = Device->CreatePixelShader(
(DWORD*)shader->GetBufferPointer(),
&MultiTexPS); if(FAILED(hr))
{
::MessageBox(0, "CreateVertexShader - FAILED", 0, 0);
return false;
} d3d::Release<ID3DXBuffer*>(shader);
以上代码中,D3DXCompileShaderFromFile函数从文件ps_multitex.txt编译HLSL代码;参数Main是HLSL代码的入口函数,如上一点代码中所见。这个入口函数可以是自定义的其他的,但要注意保持一致;ps_2_0表示像素着色器的版本。CreatePixelShader函数创建像素着色器。
2.sampler与纹理关联
创建纹理层。本文实现YUV420P渲染的方法采用了三层纹理,每层纹理分别存放Y、U、V数据。
Device->CreateTexture ( Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &YTex, NULL ) ;
Device->CreateTexture ( Width / 2, Height / 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &UTex, NULL ) ;
Device->CreateTexture ( Width / 2, Height / 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &VTex, NULL ) ;
sampler与纹理的关联
//
// Get Handles
// YTexHandle = MultiTexCT->GetConstantByName(0, "YTex");
UTexHandle = MultiTexCT->GetConstantByName(0, "UTex");
VTexHandle = MultiTexCT->GetConstantByName(0, "VTex"); //
// Set constant descriptions:
// UINT count; MultiTexCT->GetConstantDesc(YTexHandle, &YTexDesc, &count);
MultiTexCT->GetConstantDesc(UTexHandle, &UTexDesc, &count);
MultiTexCT->GetConstantDesc(VTexHandle, &VTexDesc, &count); MultiTexCT->SetDefaults(Device);
设置纹理/sampler的状态,这一部分我是在渲染的时候做的,也可以直接写在HLSL代码中。在后面渲染部分还会见到这些代码,其实是同一段代码,我只是为了表述纹理与sampler关联的一个整体过程,把它预先从渲染部分截了出来,希望不会造成误解。
// Y tex
Device->SetTexture( YTexDesc.RegisterIndex, YTex);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); // U tex
Device->SetTexture( UTexDesc.RegisterIndex, UTex);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); // string tex
Device->SetTexture( VTexDesc.RegisterIndex, VTex);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
3.渲染YUV420P
获得YUV420P数据。本文直接读取的YUV420P数据。
打开文件代码:
if((infile=fopen("test_yuv420p_320x180.yuv", "rb"))==NULL){
printf("cannot open this file\n");
return false;
}
读取数据并将数据copy到纹理中:
if (fread(buf, 1, Width*Height*3/2, infile) != Width*Height*3/2){
// Loop
fseek(infile, 0, SEEK_SET);
fread(buf, 1, Width*Height*3/2, infile);
} //
// Render
// Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); plane[0] = buf;
plane[1] = plane[0] + Width*Height;
plane[2] = plane[1] + Width*Height/4; D3DLOCKED_RECT d3d_rect;
byte *pSrc = buf;
//Locks a rectangle on a texture resource.
//And then we can manipulate pixel data in it.
LRESULT lRet = YTex->LockRect(0, &d3d_rect, 0, 0);
if (FAILED(lRet)){
return false;
}
// Copy pixel data to texture
byte *pDest = (byte *)d3d_rect.pBits;
int stride = d3d_rect.Pitch;
for(int i = 0;i < Height;i ++){
memcpy(pDest + i * stride,plane[0] + i * Width, Width);
} YTex->UnlockRect(0); D3DLOCKED_RECT d3d_rect1;
lRet = UTex->LockRect(0, &d3d_rect1, 0, 0);
if (FAILED(lRet)){
return false;
}
// Copy pixel data to texture
byte *pDest1 = (byte *)d3d_rect1.pBits;
int stride1 = d3d_rect1.Pitch;
for(int i = 0;i < Height/2;i ++){
memcpy(pDest1 + i * stride1 / 2,plane[1] + i * Width / 2, Width / 2);
} UTex->UnlockRect(0); D3DLOCKED_RECT d3d_rect2;
lRet = VTex->LockRect(0, &d3d_rect2, 0, 0);
if (FAILED(lRet)){
return false;
}
// Copy pixel data to texture
byte *pDest2 = (byte *)d3d_rect2.pBits;
int stride2 = d3d_rect2.Pitch;
for(int i = 0;i < Height/2;i ++){
memcpy(pDest2 + i * stride2 / 2,plane[2] + i * Width / 2, Width / 2);
} VTex->UnlockRect(0);
渲染:
Device->BeginScene(); Device->SetPixelShader(MultiTexPS);
Device->SetFVF(MultiTexVertex::FVF);
Device->SetStreamSource(0, QuadVB, 0, sizeof(MultiTexVertex)); // Y tex
Device->SetTexture( YTexDesc.RegisterIndex, YTex);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); // U tex
Device->SetTexture( UTexDesc.RegisterIndex, UTex);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); // string tex
Device->SetTexture( VTexDesc.RegisterIndex, VTex);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2); Device->EndScene();
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代码:
sampler YTex;
sampler UTex;
sampler VTex; struct PS_INPUT
{
float2 y : TEXCOORD0;
float2 u : TEXCOORD1;
float2 v : TEXCOORD2;
}; float4 Main(PS_INPUT input):COLOR0
{
float y = tex2D(YTex,input.y).r;
//这里不需要除以2
float u = tex2D(UTex,input.u.xy).r - 0.5f;
float v = tex2D(VTex,input.v.xy).r - 0.5f; float r = y + 1.14f * v;
float g = y - 0.394f * u - 0.581f * v;
float b = y + 2.03f * u; return float4(r,g,b, 1);
}
更新2.copy数据到texture
从内存copy数据到显卡的时候,U、V的数据copy上出了问题。新的读取数据并将数据copy到纹理中:
if (fread(buf, 1, Width*Height*3/2, infile) != Width*Height*3/2){
// Loop
fseek(infile, 0, SEEK_SET);
fread(buf, 1, Width*Height*3/2, infile);
} //
// Render
// Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); plane[0] = buf;
plane[1] = plane[0] + Width*Height;
plane[2] = plane[1] + Width*Height/4; D3DLOCKED_RECT d3d_rect;
byte *pSrc = buf;
//Locks a rectangle on a texture resource.
//And then we can manipulate pixel data in it.
LRESULT lRet = YTex->LockRect(0, &d3d_rect, 0, 0);
if (FAILED(lRet)){
return false;
}
// Copy pixel data to texture
byte *pDest = (byte *)d3d_rect.pBits;
int stride = d3d_rect.Pitch;
for(int i = 0;i < Height;i ++){
memcpy(pDest + i * stride,plane[0] + i * Width, Width);
} YTex->UnlockRect(0); D3DLOCKED_RECT d3d_rect1;
lRet = UTex->LockRect(0, &d3d_rect1, 0, 0);
if (FAILED(lRet)){
return false;
}
// Copy pixel data to texture
byte *pDest1 = (byte *)d3d_rect1.pBits;
int stride1 = d3d_rect1.Pitch;
for(int i = 0;i < Height/2;i ++){
//修改1:这个地方不需要除以2
memcpy(pDest1 + i * stride1,plane[1] + i * Width / 2, Width / 2);
} UTex->UnlockRect(0); D3DLOCKED_RECT d3d_rect2;
lRet = VTex->LockRect(0, &d3d_rect2, 0, 0);
if (FAILED(lRet)){
return false;
}
// Copy pixel data to texture
byte *pDest2 = (byte *)d3d_rect2.pBits;
int stride2 = d3d_rect2.Pitch;
for(int i = 0;i < Height/2;i ++){
//修改1:这个地方也不需要除以2
memcpy(pDest2 + i * stride2,plane[2] + i * Width / 2, Width / 2);
} VTex->UnlockRect(0);
完整工程代码:http://download.csdn.net/download/qq_33892166/9710622
D3D三层Texture纹理经像素着色器实现渲染YUV420P的更多相关文章
- HLSL像素着色器
原文:HLSL像素着色器 昨日不可追, 今日尤可为.勤奋,炽诚,不忘初心 手机淘宝二维码 扫描 或者打开连接:程序设计开发 ,掌声鼓励,欢迎光临. 像素着色器替代了固定渲染管线的 ...
- DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区
前言 由于在Direct3D 11中取消了固定管线,要想绘制图形必须要了解可编程渲染管线的流程,一个能绘制出图形的渲染管线最少需要有这两个可编程着色器:顶点着色器和像素着色器. 本章会直接跳过渲染管线 ...
- 片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但
片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但片元着色器是一个更合适的名字, 因为此时的片元并不是一个真正意义上的像素.
- WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码
原文:WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码 HLSL,High Level Shader Language,高级着色器语言,是 Di ...
- ThreeJS 物理材质shader源码分析(像素着色器)
再此之前推荐一款GLTF物理材质在线编辑器https://tinygltf.xyz/ 像素着色器(meshphysical_frag.glsl) #define PHYSICAL uniform ve ...
- 3.QOpenGLWidget-通过着色器来渲染渐变三角形
在上章2.通过QOpenGLWidget绘制三角形,我们学习绘制三角形还是单色的,本章将为三角形每个顶点着色. 1.着色器描述 着色器的开头总是要声明版本,接着是输入和输出变量.uniform和m ...
- Unity3d之Shader编程:子着色器、通道与标签的写法 & 纹理混合
一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器 ...
- 【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 & 纹理混合
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html 作者:毛星云 ...
- Shader开发之三大着色器
固定功能管线着色器Fixed Function Shaders 固定功能管线着色器的关键代码一般都在Pass的材质设置Material{}和纹理设置SetTexture{}部分. Shader &qu ...
随机推荐
- web设计页面跳转的方法
一.asp.net c# 打开新页面或页面跳转 1. 最常用的页面跳转(原窗口被替代):Response.Redirect("newpage.aspx"); 2. 利用url地址打 ...
- 设计模式(七)适配器模式(Adapter Pattern)
一.引言 在实际的开发过程中,由于应用环境的变化(例如使用语言的变化),我们需要的实现在新的环境中没有现存对象可以满足,但是其他环境却存在这样现存的对象.那么如果将“将现存的对象”在新的环境中进行调用 ...
- IOS 2D游戏开发框架 SpriteKit-->续(创建用户角色精灵--原创)
一.主要实现 今天spritekit实现创建玩家角色精灵(SKSpriteNode *), 增加角色精灵的手势操作,这里增加的手势计算方法与objective-c中是不一样的,因为objectiv ...
- Hibernate关联映射 映射文件的配置
一:多对一单向关联 首先我们必须创建两个实体类 例如:Dept类 public class Dept { private Integer deptNo; private String dName; p ...
- Access提示“操作必须使用一个可更新的查询”的解决办法
问题:软件工程师开发了一个asp.net+access网站,本地调试增.删.改和查都没有异常.部署到服务器windows2008 R2的IIS上运行后,查询没有异常.可是在修改操作提交时,产生异常:提 ...
- jQ图片列表光标移动动画
本效果使用jQuery和CSS实现了图片列表,当鼠标移入时图片向左微动,移出则复原. 效果展示: http://hovertree.com/texiao/jquery/88/ 效果图如下: 其中的jQ ...
- React Native windows搭建记录
因为是window电脑上运行的,所以测试的是安卓 1: 安装jdk:jdk-8u45-windows-x64.exe 2: 配置JAVA的环境变量 在安卓的配置基础上添加一个变量ANDROID_HOM ...
- android JNI 调用NDK方法
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- ViewPager之Fragment页面切换
一.概述 ViewPager是android-support-v4中提供的类,它是一个容器类,常用于页面之间的切换. 继上篇文章<ViewPager之引导页>之后,本文主要介绍ViewPa ...
- 下载本 WebEnh博客 安卓APP
暂时还在学习开发安卓和苹果APP应用,写得一般,以后会更新的,谢谢大家关注.对了这个是用HTML5+写的哦.不太难,但是要搞懂还是要多花点时间了,有时间就会更新的 ... ...