DirectX: рендеринг в экранный буфер без использования цели рендеринга

Я пишу движок 2D-игры с открытым исходным кодом и хочу поддерживать как можно больше устройств и платформ. Хотя сейчас у меня только Windows Mobile. Я выполняю рендеринг с помощью DirectX Mobile с DirectDraw в качестве резервного пути. Тем не менее, я столкнулся с небольшой проблемой.

Кажется, что хотя эталонный драйвер поддерживает createRenderTarget, многие многие многие физические устройства этого не делают. Мне нужен какой-то способ рендеринга на экран без использования цели рендеринга, потому что я рендерю спрайты с помощью текстурированных четырехугольников, но мне также нужно иметь возможность рисовать отдельные пиксели.

Вот как я это делаю прямо сейчас:

// save old values

if (Error::Failed(m_D3DDevice->GetRenderTarget(&m_D3DOldTarget)))
{
    ERROR_EXPLAIN("Could not retrieve backbuffer.");
    return false;
}

// clear render surface

if (Error::Failed(m_D3DDevice->SetRenderTarget(m_D3DRenderSurface, NULL)))
{
    ERROR_EXPLAIN("Could not set render target to render texture.");
    return false;
}

if  (Error::Failed (m_D3DDevice->Clear( 
            0,
            NULL,                        // target rectangle
            D3DMCLEAR_TARGET,
            D3DMCOLOR_XRGB(0, 0, 0),     // clear color
            1.0f, 
            0
        )
    )
)
{
    ERROR_EXPLAIN("Failed to clear render texture.");
    return false;
}

D3DMLOCKED_RECT render_rect;
if (Error::Failed(m_D3DRenderSurface->LockRect(&render_rect, NULL, NULL)))
{
    ERROR_EXPLAIN("Failed to lock render surface pixels.");
}
else
{
    m_D3DBackSurf->SetBuffer((Pixel*)render_rect.pBits);
    m_D3DRenderSurface->UnlockRect();
}

// begin scene

if (Error::Failed(m_D3DDevice->BeginScene()))
{
    ERROR_EXPLAIN("Failed to start rendering.");
    return false;
}

// =====================
// example rendering
// =====================

// some other stuff, but the most important part of rendering a sprite:
device->SetTexture(0, m_Texture));
device->SetStreamSource(0, m_VertexBuffer, sizeof(Vertex));
device->DrawPrimitive(D3DMPT_TRIANGLELIST, 0, 2);

// plotting a pixel
Surface* target = (Surface*)Device::GetRenderMethod()->GetRenderTarget();
buffer = target->GetBuffer();
buffer[somepixel] = MAKECOLOR(255, 0, 0);

// end scene

if (Error::Failed(device->EndScene()))
{
    ERROR_EXPLAIN("Failed to end scene.");
    return false;
}

// clear screen

if (Error::Failed(device->SetRenderTarget(m_D3DOldTarget, NULL)))
{
    ERROR_EXPLAIN("Couldn't set render target to backbuffer.");
    return false;
}

if (Error::Failed(device->GetBackBuffer (   
        0,
        D3DMBACKBUFFER_TYPE_MONO, 
        &m_D3DBack 
        ) 
    ) 
)
{
    ERROR_EXPLAIN("Couldn't retrieve backbuffer.");
    return false;
}

RECT dest = { 
    0, 
    0, 
    Device::GetWidth(), 
    Device::GetHeight()
};

if (Error::Failed( device->StretchRect (        
            m_D3DRenderSurface, 
            NULL,
            m_D3DBack, 
            &dest, 
            D3DMTEXF_NONE 
        )
    )
)
{
    ERROR_EXPLAIN("Failed to stretch render texture to backbuffer.");
    return false;
}

if (Error::Failed(device->Present(NULL, NULL, NULL, NULL)))
{
    ERROR_EXPLAIN("Failed to present device.");
    return false;
}

Я ищу способ сделать то же самое (рендерить спрайты с использованием аппаратного ускорения и отображать пиксели в буфере) без использования цели рендеринга.

Заранее спасибо.


person knight666    schedule 22.03.2010    source источник


Ответы (2)


Вы не указали много о том, как вы хотите отображать пиксели, поэтому я начну с предположения, что это действительно порядок сложности "buffer[somepixel] = MAKECOCOLOR(255, 0, 0 );".

Мое предложение заключалось бы в том, что вместо обратного чтения (что может быть очень дорогим или довольно дешевым в зависимости от архитектуры памяти) изменение и запись/загрузка обратного буфера; Вместо этого нанесите свои пиксели на текстуру и позвольте графическому процессору обрабатывать композицию спрайтов, ускоренных графическим процессором, и пикселей, нанесенных процессором.

В базовом случае это будет примерно так:

  1. Создайте 4-канальную текстуру размером с ваш задний буфер. Если ваша целевая система поддерживает это, укажите флаги использования LOCKABLE и DYNAMIC. (Если это не так или если драйвер предпочитает неблокируемые текстуры, вам придется пройти дополнительную системную текстуру для шага 2 ниже. И вместо этого загрузить ее с помощью UpdateTexture / CopyRects)

  2. Каждый кадр, заблокируйте текстуру, нарисуйте свои пиксели, разблокируйте текстуру. Обязательно запишите информацию о покрытии в альфа-канал пикселя, чтобы у графического процессора были некоторые данные, которые могут управлять композицией. Например. если вы хотите построить сплошной красный цвет, напишите (255, 0, 0, 255) для этого пикселя. Убедитесь, что вы удалили пиксели, которые на самом деле не хотите отображать.

  3. Настройте графический процессор для компоновки ваших графических данных поверх ваших спрайтов. путем рендеринга полноэкранного четырехугольника с текстурой, нанесенной на график (и с использованием точечной выборки). Для простейшего случая непрозрачной композиции включите AlphaTestEnable, установите AlphaRef на 128 и AlphaFunc на GREATER и отключите ZEnable. Это отобразит нанесенные пиксели поверх спрайтов, оставив остальные пиксели спрайтов нетронутыми.

Если вы используете буфер глубины и хотите получить дополнительную производительность, вы можете визуализировать нанесенную текстуру перед вашими спрайтами с включенными как ZEnable, так и ZWriteEnabled. (Тогда убедитесь, что вы визуализируете полноэкранный четырехугольник в ближней плоскости). Это заполнит маску буфера глубины, где находятся ваши непрозрачные пиксели, которые перекроют эти пиксели, когда вы позже будете визуализировать спрайты.

Очевидно, вы также не ограничены простой перезаписью сплошных пикселей. Вы можете использовать альфа-смешивание с комбинацией творческих режимов наложения и значений альфа-канала для создания других видов композиций и наложений.

person tbone    schedule 25.03.2010
comment
Это отличный ответ! Тем не менее, ваш метод все еще проблематичен. В идеальном мире я бы визуализировал спрайт, линию над этим спрайтом, а затем еще один спрайт. Линия будет проведена между двумя спрайтами в соответствии с алгоритмом художника. Единственный способ получить такой результат (насколько я могу судить) — отрендерить каждую линию в свою собственную текстуру. Один из методов, который работает, - это рендеринг непосредственно в резервный буфер, однако мое целевое устройство ЛИБО этого не поддерживает. :\ - person knight666; 27.03.2010
comment
Что мешает вам отображать ваши линии с помощью DX, например. как диффузно окрашенные четырехугольники? Или, если вам нужно, чтобы у них была подробная информация о пикселях, возможно, вы могли бы создавать текстуры при запуске приложения и рендерить их только позже? Если ваши спрайты непрозрачны (или замаскированы), самый простой способ обеспечить правильную сортировку — просто использовать буфер глубины. Если они смешанные/полупрозрачные, то да, вам нужно отсортировать их задом наперед и чередовать их с вашими непрозрачными линиями (или сделать так, чтобы линии записывали глубину впереди, а спрайты проверяли глубину) - person tbone; 28.03.2010

Если вам нужна широкая совместимость с портативными устройствами, у которых нет аппаратного 3D-ускорения (каждый настольный компьютер в течение многих лет поддерживает цели рендеринга), вам может быть проще использовать GDI вместо DirectX.

Я не совсем понимаю, что вы пытаетесь сделать в опубликованном вами коде, но добавлю, что цели рендеринга не поддерживают LockRect (ни текстуры, созданные с флагом цели рендеринга). Однако DirectX для Windows Mobile немного отличается от обычного DirectX, поэтому, возможно, я неправильно это истолковываю. Если вы действительно хотите использовать DirectX в Windows Mobile, поищите CreateOffscreenPlainSurface или что-то вроде того, что является эквивалентом управляемого DirectX. Этот тип поверхности будет блокируемым и будет поддерживать использование StretchRect непосредственно в заднем буфере.

person Alan    schedule 23.03.2010