Как обновлять Texture2D в пиксельных шейдерах каждый кадр (в D3D10)?

Используя D3D10, я рисую 2D-прямоугольник и хочу заполнить его текстурой (растровым изображением), которая должна меняться несколько раз в секунду (например, отображение видео).

Я использую шейдерный эффект с переменной Texture2D и пытаюсь обновить переменную ID3D10EffectShaderResourceVariable и перерисовать сетку.

Мое фактическое использование будет заключаться в копировании растровых изображений из памяти и использовании UpdateSubresource. Но это не сработало, поэтому я свел его к тестовому переключению между двумя образами DDS. В результате он рисует первое изображение, как и ожидалось, но продолжает его рисовать вместо переключения между двумя изображениями.

Я новичок в D3D. Можете ли вы объяснить, может ли этот метод работать вообще, или предложить правильный способ сделать это.

Эффект шейдера:

Texture2D txDiffuse;
SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};
struct VS_INPUT
{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD;
};
struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD0;
};
PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = input.Pos;
    output.Tex = input.Tex;    
    return output;
}
float4 PS( PS_INPUT input) : SV_Target
{
    return txDiffuse.Sample( samLinear, input.Tex );
}
technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

Код (пропущено много частей):

ID3D10ShaderResourceView*           g_pTextureRV = NULL;
ID3D10EffectShaderResourceVariable* g_pDiffuseVariable = NULL;

D3DX10CreateEffectFromResource(gInstance, MAKEINTRESOURCE(IDR_RCDATA1), NULL, NULL, NULL, "fx_4_0", dwShaderFlags, 0, device, NULL, NULL, &g_pEffect, NULL, NULL);

g_pTechnique = g_pEffect->GetTechniqueByName( "Render" );
g_pDiffuseVariable = g_pEffect->GetVariableByName( "txDiffuse" )->AsShaderResource();

// this part is called on Frame render:

device->CreateRenderTargetView( backBuffer, NULL, &rtView);
device->ClearRenderTargetView( rtView, ClearColor );

if(g_pTextureRV != NULL) {
    g_pTextureRV->Release();
    g_pTextureRV = NULL;
}

D3DX10CreateShaderResourceViewFromFile(device, pCurrentDDSFilePath, NULL, NULL, &g_pTextureRV, NULL );
g_pDiffuseVariable->SetResource( g_pTextureRV );

D3D10_TECHNIQUE_DESC techDesc;
g_pTechnique->GetDesc( &techDesc );
for( UINT p = 0; p < techDesc.Passes; ++p )
{
    g_pTechnique->GetPassByIndex( p )->Apply( 0 );
    direct2dDrawingContext->dev->Draw( 6, 0 );
}

// ... present the current back buffer 

person Gil    schedule 15.01.2013    source источник


Ответы (1)


Далее следует одно решение, не обязательно лучшее, но не использующее пользовательские шейдеры (я написал его на C#/управляемом DirectX, но его должно быть легко перекодировать).

Bitmap bmp; //the bitmap that you will use to update the texture
Texture tex; //the texture that DirectX will render

void Render()
{
  //render some stuff

  bmp = GetNextTextureFrame(); //whatever you do to update your bitmap
  Surface s = tex.GetSurfaceLevel(0);
  Graphics g = s.GetGraphics();
  //IntPtr hdc = g.GetHdc();
  //BitBlt(hdc, 0, 0, bmp.Width, bmp.Height, bmpHdc, 0, 0, 0xcc0020);
  g.DrawImageUnscaled(bmp, 0, 0);
  g.ReleaseHdc(hdc);
  s.ReleaseGraphics();
  device.SetTexture(0, tex);
  //now render your primitives

  //render some more stuff
  //present
}

Закомментированные строки — это то, как я это сделал на самом деле, используя hBitmap и DC с BitBlt, потому что это быстрее, чем GDI+. Многие люди, вероятно, скажут вам, что описанное выше — плохой способ сделать это из-за всей необходимой блокировки памяти, и они, вероятно, правы. Но я смог добиться 30 кадров в секунду с несколькими текстурами 1920x1080, поэтому независимо от того, правильно ли это, это работает.

person HypnoToad    schedule 29.03.2013