• 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
    ");
        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

  • 相关阅读:
    监控组策略应用组策略建模
    win7加域出现桌面文件丢失和映射驱动器丢失解决办法
    通过windows server 2008 AD域服务器之组策略关闭默认windows共享
    Win 2008 R2 AD组策略统一域用户桌面背景
    Win7+Ubuntu11.10(EasyBCD硬盘安装)
    组策略复制失败排错思路实例
    Windows Server 2008 R2搭建域环境中遇到的一个小错误的解决办法
    通过windows server 2008 AD域服务器之组策略关闭默认windows共享
    无需写try/catch,也能正常处理异常
    [原]《Web前端开发修炼之道》读书笔记JavaScript部分
  • 原文地址:https://www.cnblogs.com/betterwgo/p/6131723.html
Copyright © 2020-2023  润新知