Страницы

Поиск по вопросам

Показаны сообщения с ярлыком directx. Показать все сообщения
Показаны сообщения с ярлыком directx. Показать все сообщения

вторник, 25 февраля 2020 г.

Использование DirectX в C#

#c_sharp #directx


Написал программу на C# (квадрат из 8 точек, повороты, масштабирование, сброс). Теперь
нужно переделать её под DirectX, о котором я вообще ничего не знаю... Я даже не знаю
как его подключать, могли бы вы посоветовать очень простую лекцию для выполнения этого
простого задания...    


Ответы

Ответ 1



Подключить DX функции для создания графики можно установив пакет DirectX SDK который можно скачать с оффсайта Microsoft. А переделать под него - означает использовать в качестве функции рисования API DX'а(DirectDraw), самый банальный способ на мой взгляд и простой это написать ту же программу юзая Microsoft Visual C++, там вывод графики осуществляется по средствам DirectDraw. Это довольно обширная тема чтобы описать все этапы тут, ведь нам потребуется написать процедуру вызова окна, инициализации палитры и много чего другого, поэтому поделюсь ссылкой на ресурс который помог мне в этом разобраться. http://www.helloworld.ru/texts/comp/games/directx/ddraw/directx.htm

Ответ 2



раз на С# то может надо переделать на Direct2D, а не DirectX? вот библиотека а если все таки на DirectX, то есть смысл скачать себе XNA Game Studio, там все можно сделать в 10 строчек

Ответ 3



На С# есть замечательная библиотека SharpDX Основной код(Код шейдера ниже) // Copyright (c) 2010-2013 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Diagnostics; using System.Windows.Forms; using SharpDX; using SharpDX.D3DCompiler; using SharpDX.Direct3D; using SharpDX.Direct3D11; using SharpDX.DXGI; using SharpDX.Windows; using Buffer = SharpDX.Direct3D11.Buffer; using Device = SharpDX.Direct3D11.Device; namespace MiniCube { /// /// SharpDX MiniCube Direct3D 11 Sample /// internal static class Program { // [STAThread] private static void Main() { var form = new RenderForm("SharpDX - MiniCube Direct3D11 Sample"); // SwapChain description var desc = new SwapChainDescription() { BufferCount = 1, ModeDescription = new ModeDescription(form.ClientSize.Width, form.ClientSize.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm), IsWindowed = true, OutputHandle = form.Handle, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput }; // Used for debugging dispose object references // Configuration.EnableObjectTracking = true; // Disable throws on shader compilation errors //Configuration.ThrowOnShaderCompileError = false; // Create Device and SwapChain Device device; SwapChain swapChain; Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None, desc, out device, out swapChain); var context = device.ImmediateContext; // Ignore all windows events var factory = swapChain.GetParent(); factory.MakeWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAll); // Compile Vertex and Pixel shaders var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0"); var vertexShader = new VertexShader(device, vertexShaderByteCode); var pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "PS", "ps_4_0"); var pixelShader = new PixelShader(device, pixelShaderByteCode); var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode); // Layout from VertexShader input signature var layout = new InputLayout(device, signature, new[] { new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0) }); // Instantiate Vertex buiffer from vertex data var vertices = Buffer.Create(device, BindFlags.VertexBuffer, new[] { new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4( 1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), }); // Create Constant Buffer var contantBuffer = new Buffer(device, Utilities.SizeOf(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); // Prepare All the stages context.InputAssembler.InputLayout = layout; context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, Utilities.SizeOf() * 2, 0)); context.VertexShader.SetConstantBuffer(0, contantBuffer); context.VertexShader.Set(vertexShader); context.PixelShader.Set(pixelShader); // Prepare matrices var view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY); Matrix proj = Matrix.Identity; // Use clock var clock = new Stopwatch(); clock.Start(); // Declare texture for rendering bool userResized = true; Texture2D backBuffer = null; RenderTargetView renderView = null; Texture2D depthBuffer = null; DepthStencilView depthView = null; // Setup handler on resize form form.UserResized += (sender, args) => userResized = true; // Setup full screen mode change F5 (Full) F4 (Window) form.KeyUp += (sender, args) => { if (args.KeyCode == Keys.F5) swapChain.SetFullscreenState(true, null); else if (args.KeyCode == Keys.F4) swapChain.SetFullscreenState(false, null); else if (args.KeyCode == Keys.Escape) form.Close(); }; // Main loop RenderLoop.Run(form, () => { // If Form resized if (userResized) { // Dispose all previous allocated resources Utilities.Dispose(ref backBuffer); Utilities.Dispose(ref renderView); Utilities.Dispose(ref depthBuffer); Utilities.Dispose(ref depthView); // Resize the backbuffer swapChain.ResizeBuffers(desc.BufferCount, form.ClientSize.Width, form.ClientSize.Height, Format.Unknown, SwapChainFlags.None); // Get the backbuffer from the swapchain backBuffer = Texture2D.FromSwapChain(swapChain, 0); // Renderview on the backbuffer renderView = new RenderTargetView(device, backBuffer); // Create the depth buffer depthBuffer = new Texture2D(device, new Texture2DDescription() { Format = Format.D32_Float_S8X24_UInt, ArraySize = 1, MipLevels = 1, Width = form.ClientSize.Width, Height = form.ClientSize.Height, SampleDescription = new SampleDescription(1, 0), Usage = ResourceUsage.Default, BindFlags = BindFlags.DepthStencil, CpuAccessFlags = CpuAccessFlags.None, OptionFlags = ResourceOptionFlags.None }); // Create the depth buffer view depthView = new DepthStencilView(device, depthBuffer); // Setup targets and viewport for rendering context.Rasterizer.SetViewport(new Viewport(0, 0, form.ClientSize.Width, form.ClientSize.Height, 0.0f, 1.0f)); context.OutputMerger.SetTargets(depthView, renderView); // Setup new projection matrix with correct aspect ratio proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, form.ClientSize.Width / (float)form.ClientSize.Height, 0.1f, 100.0f); // We are done resizing userResized = false; } var time = clock.ElapsedMilliseconds / 1000.0f; var viewProj = Matrix.Multiply(view, proj); // Clear views context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0); context.ClearRenderTargetView(renderView, Color.Black); // Update WorldViewProj Matrix var worldViewProj = Matrix.RotationX(time) * Matrix.RotationY(time * 2) * Matrix.RotationZ(time * .7f) * viewProj; worldViewProj.Transpose(); context.UpdateSubresource(ref worldViewProj, contantBuffer); // Draw the cube context.Draw(36, 0); // Present! swapChain.Present(0, PresentFlags.None); }); // Release all resources signature.Dispose(); vertexShaderByteCode.Dispose(); vertexShader.Dispose(); pixelShaderByteCode.Dispose(); pixelShader.Dispose(); vertices.Dispose(); layout.Dispose(); contantBuffer.Dispose(); depthBuffer.Dispose(); depthView.Dispose(); renderView.Dispose(); backBuffer.Dispose(); context.ClearState(); context.Flush(); device.Dispose(); context.Dispose(); swapChain.Dispose(); factory.Dispose(); } } } Код шейдера // Copyright (c) 2010-2013 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. struct VS_IN { float4 pos : POSITION; float4 col : COLOR; }; struct PS_IN { float4 pos : SV_POSITION; float4 col : COLOR; }; float4x4 worldViewProj; PS_IN VS( VS_IN input ) { PS_IN output = (PS_IN)0; output.pos = mul(input.pos, worldViewProj); output.col = input.col; return output; } float4 PS( PS_IN input ) : SV_Target { return input.col; } SharpDX MiniCube Direct3D 11 Sample (лицензия MIT)

среда, 22 января 2020 г.

Деформация 3D модели с помощью кривых Безье

#cpp #3d #directx #mesh


Пишу программу, которая должна деформировать модель по опорным точкам. 

Получилось написать следующий код, который создает полотно процедурно по 16 опорным
точкам. Вот как это выглядит:



//вычисляем полином Бершнейна третьей степени на основании четырех точек
XMFLOAT3 Bernstein(float u, XMFLOAT3 p1, XMFLOAT3 p2, XMFLOAT3 p3, XMFLOAT3 p4) {
    XMFLOAT3 a, b, c, d;

    a = pointTimes(pow(u, 3), p1);
    b = pointTimes(3 * pow(u, 2) * (1 - u), p2);
    c = pointTimes(3 * u * pow((1 - u), 2), p3);
    d = pointTimes(pow((1 - u), 3), p4);

    return pointAdd(pointAdd(a, b), pointAdd(c, d));
}

//тоже самое что и верхняя функция, только принимаем указатель на массив точек
XMFLOAT3 Bernstein(float u, XMFLOAT3 *p) {
    XMFLOAT3 a, b, c, d;

    a = pointTimes(pow(u, 3), p[0]);
    b = pointTimes(3 * pow(u, 2) * (1 - u), p[1]);
    c = pointTimes(3 * u * pow(1 - u, 2), p[2]);
    d = pointTimes(pow(1 - u, 3), p[3]);

    return pointAdd(pointAdd(a, b), pointAdd(c, d));
}

void CreateSurfaceByControlPoints(XMFLOAT3 points[], D3DTextureVertex **ppVertices) {
    const int step = 16;    //количество делений у полотна
                            //чем больше делений, тем глаже модель становится (создается
больше вершин)

    const int max = (step * (step + 1) * 2);    //общее количество вершин у модели

    XMFLOAT3 temp[4];
    XMFLOAT3 *last = new XMFLOAT3[step + 1];

    //в массиве points, размером 16 точек,
    //у меня верхняя линия с индексами от 0 до 3, следющая от 4 до 7 и т.д.

    for(int i = 0; i < 4; i++) {
        temp[i] = points[(i + 1) * 4 - 1];
    }

    for(int i = 0; i < step + 1; i++) {
        last[i] = Bernstein((float)i / (float)step, temp);
    }

    *ppVertices = new D3DTextureVertex[max];

    int c = 0; //счетчик текущей вершины

    for(int i = 1; i < step + 1; i++) {
        float py = (float)i / (float)step;
        float pyold = ((float)i - 1.0f) / (float)step;

        for(int j = 0; j < 4; j++) {
            temp[j] = Bernstein(py, points[j * 4 + 0], points[j * 4 + 1], points[j
* 4 + 2], points[j * 4 + 3]);
        }

        for(int j = 0; j <= step; j++) {
            float px = static_cast(j) / (float)step;

            (*ppVertices)[c++] = D3DTextureVertex(last[j].x, last[j].y, last[j].z,
pyold, px);

            last[j] = Bernstein(px, temp);

            (*ppVertices)[c++] = D3DTextureVertex(last[j].x, last[j].y, last[j].z,
py, px);
        }
    }

    delete[] last;
}


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

Модель уже имеет свою геометрию и UV координаты.
Выглядит следующим образом:


Главный вопрос - есть ли какая-нибудь формула, чтобы рассчитать позицию для каждого
вертекса, основываясь на 8 кривых Безье (4 горизонтальных и 4 вертикальных), учитывая
их первоначальную позицию? Как, например, в профессиональных программах Maya или Blender
есть такие инструменты, как произвольно расставить опорные точки и делать с моделью
любые деформации.
    


Ответы

Ответ 1



Спасибо за помощь! Получилось решить проблему следующим образом: //1378 и 861 - значения у меня рассчитаны, чтобы вся модель умещалась на экране //у вас они могут быть другие или вообще по-другому считаться, главное, чтобы pz и px были в пределах [0.0f, 1.0f] //в переменной points хранятся опорные точки //в ppOutVertices хранится указатель на вершины, которые будем задавать //в ppBaseVertices хранится массив вершин по-умолчанию, на основе которых формируем новый массив //nVertices - количество вершин в моделе void UpdateSurface(XMFLOAT3 points[], D3DTextureVertex **ppOutVertices, D3DTextureVertex **ppBaseVertices, UINT nVertices) { XMFLOAT3 tmp[4]; *ppOutVertices = new D3DTextureVertex[nVertices]; for(int i = 0; i < nVertices; i++) { float pz = (float)(*ppBaseVertices)[i].Pos.x / (1378.0f / 100.0f); for(int j = 0; j < 4; j++) { tmp[j] = Bernstein(pz, points[j * 4 + 0], points[j * 4 + 1], points[j * 4 + 2], points[j * 4 + 3]); } float px = (float)(*ppBaseVertices)[i].Pos.z / (861.0f / 100.0f); XMFLOAT3 bern = XMFLOAT3(Bernstein(px, tmp)); (*ppOutVertices)[i].Pos = XMFLOAT3(bern.x, 0.0f, bern.z); } }

суббота, 11 января 2020 г.

Перерисовка окна при изменении его размера

#cpp #winapi #directx #resize


Только начал изучать DirectX и WinAPI. Написал простейшую программу: создаёт квадрат
на всё окно и "раскрашивает" его шейдером. Но при уменьшении размера окна изображение
сжимается, а при увеличении - растягивается. Как сделать, чтобы этого не происходило,
т.е. при уменьшении, например, изображение не сжималось, а просто показывалась его
меньшая область.
Пробывал сделать это так:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // ...

    switch (message)
    {
        // ...

        case WM_SIZE:
            D3D11_VIEWPORT vp;
            vp.Width = (FLOAT)LOWORD(lParam);
            vp.Height = (FLOAT)HIWORD(lParam);
            vp.MinDepth = 0.0f;
            vp.MaxDepth = 1.0f;
            vp.TopLeftX = 0;
            vp.TopLeftY = 0;
            if (g_pImmediateContext)
                g_pImmediateContext->RSSetViewports(1, &vp);
        break;

        // ...
    }

    return 0;
}


Думал, сработает, но получил странное поведение: при уменьшении размера окна изображение
ещё сильнее уменьшается, а при увеличении остаётся неизменным. Как реализовать описанное
ранее поведение правильно?
    


Ответы

Ответ 1



Вне зависимости от фактических размеров окна его DirectX-ная система координат всегда будет описывать прямоугольник (0, 0)-(1, 1) (DX10) или (-1, -1)-(1, 1) (DX9). Для этого DirectX будет специально масштабировать (а в DX9 ещё и перемещать) координаты на кадровом буфере. Соответственно, ваша задача заключается в применении матрицы трансформации, которая отменяла бы подобный эффект, «сжимая» обе координаты прямо пропорционально габаритам окна. Причём эта матрица должна применяться после применения всех остальных матриц, если они есть. Для DirectX 11 (упомянутого в вопросе) матрица будет иметь следующий вид: где: α — масштабный коэффициент, указывающий, сколько пикселей должно приходиться на единичный отрезок обзорной области. w, h — ширина и высота клиентской области окна; получать при каждом WM_SIZE с помощью LOWORD(lParam) и HIWORD(lParam) соответственно.

Как отрисовать объект с использованием буфера глубины, но без записи в него?

#3d #directx


Требуется отрендерить объект, причем он должен учитывать буфер глубины (т.е. не перекрывать
ближние объекты и перекрывать дальние), но при этом не менять значения в буфере глубины.
Интересует для DirectX (10 или 11).

Т.е. нужно то самое, что в OpenGL делает:

glDepthMask(GL_FALSE);

    


Ответы

Ответ 1



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

понедельник, 6 января 2020 г.

Необходимо ли освобождать интерфейс COM который был получен с помощью QueryInterface

#cpp #com #directx


Делаю обертку над некоторыми из объектов DirectX, дошло дело до копирования из другого
объекта DirectX т.е. преобразование из DirectX объекта в объект моего типа. У каждого
из таких объектов есть метод QueryInterface, далее, я запрашиваю интересующий меня
интерфейс у данного объекта и провожу некоторые действия с ним. По моему мнению другой
объект из которого был создан объект у которого я запросил интерфейс, кол-во ссылок
должно увеличиться, и для предотвращения утечек памяти, я должен вызвать Release для
объекта который я запросил.

Правильно ли так делать, и вообще нужно ли?

HRESULT result = ptr->QueryInterface(__uuidof(ID2D1HwndRenderTarget), reinterpret_cast(&m_render_target_));
if(result != S_OK)
{
    throw std::exception("Failed to QueryInterface from ID2D1SolidColorBrush");
}

result = m_render_target_->CreateSolidColorBrush(ptr->GetColor(), &m_solid_color_brush_ptr_);

if (result != S_OK)
{
    throw std::exception("Failed to CreateSolidColorBrush from ID2D1HwndRenderTarget");
}

m_solid_color_brush_ptr_->SetOpacity(ptr->GetOpacity());

D2D1_MATRIX_3X2_F matrix;
memset(&matrix, 0, sizeof matrix);
ptr->GetTransform(&matrix);
m_solid_color_brush_ptr_->SetTransform(matrix);


Для m_render_target_ локального поля, я должен вызвать Release в деструкторе, а в
обычном конструкторе добавить ссылку вызвав AddRef, т.к. в обычный конструктор передается
указатель на этот объект?
    


Ответы

Ответ 1



Да, вызов QueryInterface увеличивает внутренний счетчик ссылок объекта, поэтому у успешно полученного указателя следует вызвать Release. Однако делать это все руками не следует, благо это все уже реализовано в умных указателях, например WRL::ComPtr: ::Microsoft::WRL::ComPtr<::ID3D11Device> p_d3d_device{}; // создаем D3D устройство... ::Microsoft::WRL::ComPtr<::IDXGIDevice> p_dxgi_device{}; if(!SUCCEEDED(p_d3d_device.As(&p_dxgi_device))) // тут вызовется `QueryInterface` { /* обработка ошибки */ } // при выходе из области видимости у каждого указателя будет вызван `Release`

Ответ 2



Правило очень простое, AddRef и Release должны быть строго симметричны. И да, корректный QueryInterface в случае успеха, должен вызывать AddRef для объекта. Поэтому после завершения работы с вашими интерфейсами, вы должны вызвать Release и все. Офтоп. Второе исключение которое вы бросаете выглядит опасно, не забыли ли вы что m_render_target уже получен и нуждается в заботе.

четверг, 2 января 2020 г.

Подключение DirectX к проекту WPF

#wpf #directx #c_sharp


Пишу небольшую игрушку на WPF, и для одновременного воспроизведения многих звуков
использую DirectSound. До этого пытался использовать AudioVideoPlayback, но была точно
такая же проблема. Я добавил в проект ссылку на DirectSound.dll из папки DirectX. 
Прописал 
    using Microsoft.DirectX.DirectSound;

Вот эти строки отвечают за инициализацию:
    Microsoft.DirectX.DirectSound.Buffer sp;
    Microsoft.DirectX.DirectSound.Buffer deathSp;
    Device m_SoundDevice;

В конструкторе окна выделяю память, подгружаю все:
    m_SoundDevice = new Device();
    m_SoundDevice.SetCooperativeLevel((new system.Windows.Interop.WindowInteropHelper(this)).Handle,
CooperativeLevel.Priority);

    sp = new Microsoft.DirectX.DirectSound.Buffer("Shot2.wav", m_SoundDevice);
    deathSp = new Microsoft.DirectX.DirectSound.Buffer("Death1.wav", m_SoundDevice);

Проблема состоит в том, что если в свойствах проекта стоит .NET 4.0 или выше, то
при отладке студия иногда просто зависает, и закрывается только завершением процесса
студии, иногда сразу же выдает ошибку, как при версиях 3.Х. Скомпилированный при этом
экзешник выдает ошибку при запуске. Если понизить версию .NET до 3.5 или 3.0, то при
отладке программа запускается, но вылетает при инициализации, которую я указал выше.
Пишет следующее:

Нет доступных исходных файлов. В стеке
вызова содержится только внешний код.
XamlParseException не обработано
"Вызов конструктора для типа
"Game.MainWindow", удовлетворяющего
указанным ограничениям привязки,
привел к выдаче исключения.": номер
строки "3" и позиция в строке "9".

Я не пойму, в чем причина. Где эта третья строка - тоже. В xaml в 3ей строке ничего
интересного, как и в самом коде на 3ей строке.    


Ответы

Ответ 1



Уфф, я разобрался в проблеме. Причина была в том, что задавать CooperativeLevel для SoundDevice нельзя во время инициализации нашего MainWindow. Инициализацию всего DirectSound нужно вешать на событие OnLoad нашего главного окна, либо в любой момент после завершения его инициализации.

четверг, 26 декабря 2019 г.

Как работает программа показа живых обоев на рабочем столе (winVista/7/8/8.1)?

#directx #cpp


Существует программа DreamScenes, которая позволяет устанавливать видео обои на рабочий
стол (обычное видео в формате dream/wmv/mpg). Также в VLC media player есть функция
показа видео в режиме обоев (исходники данной программы доступны, но разобраться в
них не получилось: Исходники). Как выглядит код, реализующий такую фичу?
Пока я смогла реализовать такое "чудо" таким образом (примерный код, рабочий):
#include 
#include 
#include 

using namespace std;

void main()
{
    WIN32_FIND_DATA File;
    HANDLE F;
for(int i=0; i < 10; i++)  {
    F=FindFirstFile("C:\\leto\\*.jpg", &File);
    if (F!=INVALID_HANDLE_VALUE)
    {        do
                {
                ofstream bat("C:\\logo.bat", ios_base::out);
                bat << "REG ADD \"HKCU\\Control Panel\\Desktop\" /v Wallpaper /d
\"C:\\leto\\" << File.cFileName <<"\" /f" << endl <<
                "rundll32.exe user32.dll,UpdatePerUserSystemParameters" << endl;
                bat.close();
                system("C:\\logo.vbs");
                Sleep(200);
                }
             while (FindNextFile(F,&File)!=0);
                FindClose(F);
    }
}
    system("pause");
}

Описание: В папке находятся картинки (кадры анимации). Находим первую картинку, в
батнике прописываем код, который меняет обои рабочего стола на мою картинку, запускаем
vbs-скрипт, который позволяет выполнять батник в фоновом режиме, затем делаем паузу
между выполнением следующей команды в программе С++ (0.2 сек). Таким образом наблюдается
анимация на рабочем столе (но на первом проходе она виснет, почти не выполняется, потом
работает, в этом и проблема). Как реализовать нормально, без зависа и кучи картинок,
как в упомянутых в начале темы программах?    


Ответы

Ответ 1



Отрисовку можно сделать посредством DirectDraw, вот тут есть пример. Но, решение несовместимо с Aero, придется выключать.

среда, 25 декабря 2019 г.

GLSL, HLSL Шейдер из нескольких файлов

#opengl #directx #glsl #шейдеры


Например есть 3 шейдера. параллакс, бамп, и солид. В каждом шейдере должны быть общие
вычисления типа вычисления источников света. 

Реально ли в GLSL и HLSL вынести эти функции в отдельный файл и потом их как бы подключать
как .h файл к коду программы?
Каковы конструкции, ключевые слова в шейдерных языках и что нужно сделать в коде
программы для того чтобы их использовать?
    


Ответы

Ответ 1



Насколько мне видится, самым универсальным решением для HLSL и GLSL может быть написание своего простенького препроцессора, который будет вместо строке #include("bump.glsl") вставлять код этого файла. Также можно пытаться обойтись дефайнами и таким образом отключать или включать какие-то функции в шейдере. P.S. В вопросе наверное стоило указать версию GLSL и HLSL, которую вы планируете использовать.

вторник, 24 декабря 2019 г.

Воспроизведение большого видео с деформацией - DirectX

#mediaplayer #directx #gpu #directshow


Приветствую всех!

Ситуация следующая: есть видео разрешением 1920x2400, закодированное hevc (h265),
спрятанное в .mp4, подготовленное с частотой 30 фпс; аудиопотока нет, только видео

Есть задача - воспроизводить это видео на двух мониторах 1920x1080 (так называемая
вертикальная стерео-пара)
После поиска методов для реализации данного алгоритма, было принято решение использовать
DirectShow для получения кадров и DirectX для вывода текстуры. Получилась следующая
цепочка графов:

FileSourceFilter -> Lav Splitter -> Lav Video Decoder -> SampleGrabber -> NullRenderer

В callback-функции SampleGrabber'а беру и создаю Texture2D, из которой создаю ShaderResourceView.
Ну, и соответственно, есть два плоских меша, на которых накладывается текстура. Отрисовка
происходит вот так:

const float bg_clear_color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

pD3DDC->ClearRenderTargetView(pD3DRenderTarget, bg_clear_color);
pD3DDC->ClearDepthStencilView(pD3DDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

objects[0]->Render();
objects[1]->Render();

pD3DSwapChain->Present(0, 0);


И картинка идет, но происходят подтормаживания. Бьюсь над этим вопросом уже несколько
месяцев, не знаю в каком направлении рыть, пробовал всякое, и аппаратное ускорение
и снижение битрейта видео до 10-12 мбит/c. Ситуация немного улучшилась, но все равно
картинка дергается.

Прошу экспертов и знающих проконсультировать по данному вопросу, может быть сам алгоритм
неправильно продуман. Есть чувство, что я что-то упускаю. Буду рад любой помощи, спасибо.

EDIT:

В данный момент по совету @Roman совершаю миграцию своего движка на Media Foundation
с DirectShow. Так как мы перешли на MF, начали использовать h264 для надежности, потому
что как я понял, h265 сложнее использовать и по-умолчанию драйвера не установлены,
придется подключать их дополнительно, как самописные фильтры DS (возможно, я ошибаюсь,
но это пока не важно).

Задача немного изменилась и теперь произносится так: есть четыре видео 1920x1080
h264 20 mbps 30 fps. Разрешение было выбрано именно такое, потому что в документации
на MSDN мной было замечено, что максимальное разрешение видео, при котором возможно
использовать аппаратное ускорение, - 1920x1088. Задача требует максимально быстро получать
кадры из каждого видео и писать их в текстуры ID3D11Texture2D, чтобы потом их выводить
в сцене Direct3D 11.

Сейчас на рабочей машине стоит две видеокарты NVIDIA GTX 950, к каждой видеокарте
подключено по два монитора, и еще один монитор. Все мониторы 1920x1080.

После того как я провел неделю кряду над поиском информации по MF, я был удивлен,
как мало информации можно найти на тему видео текстур с поддержкой HWA, проштрудировал
практически все мануалы по MF на MSDN, но все равно многое осталось мне непонятным.
Сейчас расскажу как у меня сейчас все работает и чего хотелось бы добиться. Выражаю
огромную благодарность товарищу @Roman, который многое подсказал и направил меня в
нужном направлении.

Direct3D устройство создается у меня следующим кодом:

D3D_FEATURE_LEVEL fl[] = {D3D_FEATURE_LEVEL_11_1};
CH(D3D11CreateDevice(0, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
fl, ARRAYSIZE(fl), D3D11_SDK_VERSION, &pD3DDevice, 0, &pD3DDC));


При перечислении доступных устройств у меня в списке отображается четыре, Intel(R)
HD Graphics, два NDIVIA GTX 950 и Microsoft Basic Render Driver.
Хотелось бы использовать два видео на первом NVIDIA устройстве, а остальные два на
другом, но вот в чем проблема. Если я создаю устройство с указанием одного из них,
мне приходится передавать параметр D3D_DRIVER_TYPE_UNKNOWN, что снижает фпс и одно
видео на NV выдает ~25 фпс. Если я инициализирую устройство как в коде выше - по-умолчанию
выбирается Intel, но зато фпс выдает ~50 (я подозреваю, что почему-то не включается
аппаратное ускорение), что странно.

Моя затея следующая: я хочу максимально задействовать силы NVIDIA карты с HWA, чтобы
все кадры видео сразу рендерелись в текстуру, без лишнего копирования в CPU, а потом
обратно в GPU, как у меня было реализовано.

CComPtr sample;
CH(reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, 0, &flags, 0, &sample.p));

CComPtr buffer;
BYTE* data;
CH(sample->ConvertToContiguousBuffer(&buffer));
CH(buffer->Lock(&data, NULL_PTR, NULL_PTR));

DWORD dwSize;
CH(buffer->GetCurrentLength(&dwSize));
UpdateVideoTexture(data, dwSize);

buffer->Unlock();


Этот код был создан на основе вот этого, но там все также есть затык, получается
мы вытаскиваем текстуру из GPU и потом засовываем ее обратно. Этот код медленный. Поэтому
я начал копать дальше, и вот что получилось, как я инициализирую SourceReader.

//Инициализируем COM библиотеку для нашего приложения и MF библиотеку
CH(CoInitializeEx(NULL_PTR, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
CH(MFStartup(MF_VERSION));

//Говорим библиотеке D3D что мы используем мультипоточные вызовы MF, чтобы избежать
undefined behavior
const CComQIPtr pMultithread = pD3DDevice;
pMultithread->SetMultithreadProtected(TRUE);

//Создание Менеджера Устройств MF
UINT token;
CComPtr manager;
CH(MFCreateDXGIDeviceManager(&token, &manager));

//Привязываем наше D3D Устройство к Менеджеру Устройств
CH(manager->ResetDevice(pD3DDevice, token));

//Создаем свойства SourceReader
CComPtr attributes;
CH(MFCreateAttributes(&attributes, 2));
CH(attributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, manager));  //указываем менеджер
CH(attributes->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, FALSE));
CH(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE));

//Создаем SourceReader и загружаем видео, зажатое h264 кодеком
CH(MFCreateSourceReaderFromURL(L"D:\\Work\\WorkFiles\\1.mp4", attributes, &reader));

//Настраиваем выходной медиатип декодера в SourceReader
CComPtr output_type;
CH(MFCreateMediaType(&output_type));
CH(output_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
CH(output_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12));
CH(reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL_PTR, output_type));

//Создаем Видео Устройство
CComPtr pVideoDevice;
CH(pD3DDevice->QueryInterface(IID_PPV_ARGS(&pVideoDevice)));

//Создаем видеотекстуру
CH(D3DHelper_Create2DTexture(pD3DDevice, &pD3DVideoTexture, CORE_WIDTH, CORE_HEIGHT,
DXGI_FORMAT_NV12, D3D11_BIND_DECODER, 0, D3D11_USAGE_DEFAULT, 0));

D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC dc;
dc.DecodeProfile = D3D11_DECODER_PROFILE_H264_VLD_NOFGT;
dc.Texture2D.ArraySlice = 0;
dc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;

//И описываем D3D View текстуры
CH(pVideoDevice->CreateVideoDecoderOutputView((ID3D11Resource*)pD3DVideoTexture,
&dc, &pVideoDecoderOutputView));
CH(pD3DDC->QueryInterface(IID_PPV_ARGS(&pVideoContext)));

//Описываем енумератор Видео Процессора
D3D11_VIDEO_PROCESSOR_CONTENT_DESC ed;
CLEAR(ed);

ed.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
ed.InputFrameRate.Numerator = 60;
ed.InputFrameRate.Denominator = 1;
ed.InputWidth = CORE_WIDTH;
ed.InputHeight = CORE_HEIGHT;
ed.OutputFrameRate.Numerator = 60;
ed.OutputFrameRate.Denominator = 1;
ed.OutputWidth = CORE_WIDTH;
ed.OutputHeight = CORE_HEIGHT;
ed.Usage = D3D11_VIDEO_USAGE_OPTIMAL_SPEED;

CComPtr pVideoEnum;
CH(pVideoDevice->CreateVideoProcessorEnumerator(&ed, &pVideoEnum));

//Создаем Видео Процессор
CH(pVideoDevice->CreateVideoProcessor(pVideoEnum, 0, &pVideoProcessor));


В последствии я планирую вызывать ReadSample, а что делать дальше не знаю, прошу
помощи, спасибо. Ради поддержки вышестоящего кода пришлось даже пересесть с Win7 на
Win10. Благодарю за любую помощь.

EDIT:

P.S.: Вот код, которым сейчас инициализируется текстура:

D3D11_MAPPED_SUBRESOURCE resource;
CH(pD3DDC->Map(pD3DVideoTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource));

memcpy(resource.pData, data, dwSize);
pD3DDC->Unmap(pD3DVideoTexture, 0);

SAFE_COM_RELEASE(pD3DVideoTextureRV);

CH(pD3DDevice->CreateShaderResourceView(pD3DVideoTexture, 0, &pD3DVideoTextureRV));
pD3DDC->PSSetShaderResources(0, 1, &pD3DVideoTextureRV);

    


Ответы

Ответ 1



В этой схеме есть сразу несколько потенциальных заторов, один или несколько из которых ограничивают общую работу. Они таковы: Неизвестна производительность декодера, справляется ли он нормально с задачей или он и тормозит Подключение Sample Grabber таково, что возможно между ним и декодером происходит дополнительное преобразование данных, либо как дополнительный фильтр, либо в рамках LAV декодера - это может сказываться на производительности Sample Grabber выгребает данные в системной памяти независимо от режима аппаратного ускорения Sample Grabber может оперировать ограниченным числом буферов, что в свою очередь может ограничивать возможность декодера своевременно пополнять очередь; само присутствие Sample Grabber как правило является свидетельством неспособности потреблять данные более эффективным способом Не указан тип текстуры заполнение staging текстур в реальном времени имеет слабую производительность Так, в общем, хорошо бы декодировать аппаратно прямиком в текстуру и дальше уже данными в ресурсе графического адаптера манипулировать рисуя сцену. Такой подход, к сожалению, нереализуем с Sample Grabber. Media Foundation API, к примеру, такие фокусы позволяет.

Ответ 2



С помощью этого примера получилось разобраться с проблемой, выводится минимум по 50 фпс на всех четырех мониторах, спасибо за помощь! EDIT: Подробно расскажу как это получилось. Добавляю подробную информацию, поиск которой занял у меня около недели, но результат того стоит, надеюсь, этот код поможет быстрее понять принцип. Выкладываю кусок кода, отвечающий за получение видеотекстуры и ее запись в шейдер для дальнейшего отображения на моделях в сцене. Код максимально сжатый, с опущенными проверками для наглядности алгоритма. Следующая часть работает с инициализацией MF как в первом посте. Суть алгоритма в следующем: CComPtr pSample; CH(pSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, 0, &flags, 0, &pSample)); CComPtr pBuffer; CH(pSample->GetBufferByIndex(0, &pBuffer)); CComPtr pDXGIBuffer; CH(pBuffer->QueryInterface(IID_PPV_ARGS(&pDXGIBuffer))); CComPtr pTexture2D; CH(pDXGIBuffer->GetResource(IID_PPV_ARGS(&pTexture2D))); D3D11_TEXTURE2D_DESC desc; pTexture2D->GetDesc(&desc); D3D11_VIDEO_PROCESSOR_CONTENT_DESC cd; CL(cd); cd.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST; cd.InputWidth = desc.Width; cd.InputHeight = desc.Height; cd.OutputWidth = desc.Width; cd.OutputHeight = desc.Height; cd.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL; CH(pVideoDevice->CreateVideoProcessorEnumerator(&cd, &pVideoEnum)); CH(pVideoEnum->CheckVideoProcessorFormat(DXGI_FORMAT_B8G8R8A8_UNORM, &uiFlags)); CH(pVideoDevice->CreateVideoProcessor(pVideoEnum, 0, &pVideoProcessor)); pSample.Release(); pBuffer.Release(); CH(MFCreateSample(&pSample)); CH(MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pD3DVideoTexture, 0, FALSE, &pBuffer)); CH(pSample->AddBuffer(pBuffer)); D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc; CL(OutputViewDesc); OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; OutputViewDesc.Texture2D.MipSlice = 0; OutputViewDesc.Texture2DArray.MipSlice = 0; OutputViewDesc.Texture2DArray.FirstArraySlice = 0; CComPtr pOutputView; CH(pVideoDevice->CreateVideoProcessorOutputView(pD3DVideoTexture, pVideoEnum, &OutputViewDesc, &pOutputView)); D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC ivd; CL(ivd); ivd.FourCC = 0; ivd.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; ivd.Texture2D.MipSlice = 0; ivd.Texture2D.ArraySlice = 0; CComPtr pInputView; CH(pVideoDevice->CreateVideoProcessorInputView(pTexture2D, pVideoEnum, &ivd, &pInputView)); D3D11_VIDEO_PROCESSOR_STREAM sd; CL(sd); sd.Enable = TRUE; sd.OutputIndex = 0; sd.InputFrameOrField = 0; sd.PastFrames = 0; sd.FutureFrames = 0; sd.ppPastSurfaces = NULL; sd.ppFutureSurfaces = NULL; sd.pInputSurface = pInputView; sd.ppPastSurfacesRight = NULL; sd.ppFutureSurfacesRight = NULL; CH(pVideoContext->VideoProcessorBlt(pVideoProcessor, pOutputView, 0, 1, &sd)); SAFE_COM_RELEASE(pD3DVideoTextureRV); CH(pD3DDevice->CreateShaderResourceView(pD3DVideoTexture, 0, &pD3DVideoTextureRV)); pD3DDC->PSSetShaderResources(0, 1, &pD3DVideoTextureRV); А теперь объясню что здесь происходит. Как ранее подсказывал @Roman, мы при правильной инициализации, мы получаем с помощью IMFSourceReader указатель на наши сэмплы (IMFSample). Далее мы получаем указатель на IMFDXGIBuffer интерфейс, из которого в свою очередь вытягиваем указатель на нужный интерфейс ID3D11Texture2D. В этой текстуре уже хранится информация кадра видео, но как я понял, ее нельзя сразу использовать для рендеринга, нужно пропустить ее через некоторый видеопроцессинг, что мы и делаем далее. Стоит заметить, что ту текстуру, которую я использую для шейдера я создаю ранее, в другой части кода с BIND-флагами D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, как это описано здесь. Далее по коду мы создаем нужные интерфейсы для того, чтобы указать что конечный результат будет записан в нашу текстуру (она объявлена в коде как pD3DVideoTexture). После всех операций, на основе этой текстуры, я создаю ID3D11ShaderResourceView, который связывает нашу текстуру с шейдером. При правильных действиях, программа выдает от 125 до 175 фпс на NVIDIA GTX 950 (для одного видео 1920x1080 h264 20 mbps). Хочу подчеркнуть, что это не максимум, MF поистине сильная вещь, хорошо оптимизированная под Windows 10. Если не использовать дополнительное копирование в pD3DVideoTexture, а указывать везде текстуру задника окна, которую мы можем получить из интерфейса IDXGISwapChain с помощью метода GetBuffer(), то фпс у меня доходил от 300 до 350, при том, что CPU был загружен на ~20%, а GPU на ~50-60%. Под словом "фпс", здесь я имею в виду максимально возможное количество кадров, которое может вытащить движок в секунду, надеюсь, понятно, что для многих видеоплееров не нужно больше 30-50. Надеюсь, этой информацией я кому-то помогу, и люди быстрее найдут решение! Еще раз спасибо за помощь.

понедельник, 23 декабря 2019 г.

Использование Desktop Duplication API

#cpp #cpp_cli #directx #direct3d


#include "stdafx.h"

#include "stdafx.h"
#include "iostream"

#include "D3D9.h"
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#pragma comment(lib, "D3D11.lib")
#pragma comment(lib, "D3d9.lib")
#pragma comment(lib, "dxgi.lib")

#pragma comment(lib, "gdi32.lib")

using namespace System;

#define EXIT(hr) { if (FAILED(hr)) \
                { Console::WriteLine("Error!"); \
                Console::ReadKey(); return -1; } }

HBITMAP ExtractBitmap(ID3D11Texture2D* d3dtex, ID3D11Device* pDevice) 
{
    HRESULT hr;

    HBITMAP hBitmapTexture = NULL;
    HGDIOBJ hBitmap;

    D3D11_TEXTURE2D_DESC desc;
    ID3D11Texture2D* pNewTexture = NULL;

    D3D11_TEXTURE2D_DESC description;

    d3dtex->GetDesc(&desc);
    d3dtex->GetDesc(&description);

    description.BindFlags = 0;
    description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
    description.Usage = D3D11_USAGE_STAGING;

    if (FAILED(pDevice->CreateTexture2D(&description, NULL, &pNewTexture)))
    {
        Console::WriteLine("CreateTexture2D failed!");
        return NULL;
    }

    ID3D11DeviceContext* ctx = NULL;
    pDevice->GetImmediateContext(&ctx);

    ctx->CopyResource(pNewTexture, d3dtex);

    D3D11_MAPPED_SUBRESOURCE resource;
    UINT subresource = D3D11CalcSubresource(0, 0, 0);
    ctx->Map(pNewTexture, subresource, D3D11_MAP_READ_WRITE, 0, &resource);

    // Copy from texture to bitmap buffer.
    uint8_t* sptr = reinterpret_cast(resource.pData);
    uint8_t* dptr = new uint8_t[desc.Width*desc.Height * 4];

    for (size_t h = 0; h < desc.Height; ++h)
    {
        size_t msize = std::min(desc.Width * 4, resource.RowPitch);
        memcpy_s(dptr, desc.Width * 4, sptr, msize);
        sptr += resource.RowPitch;
        dptr += desc.Width * 4;
    }

    dptr -= desc.Width*desc.Height * 4;

    // Swap BGR to RGB bitmap.
    uint32_t *dPtr = reinterpret_cast(dptr);
    for (size_t count = 0; count < desc.Width*desc.Height * 4; count += 4)
    {
        uint32_t t = *dPtr;
        uint32_t t1 = (t & 0x00ff0000) >> 16;
        uint32_t t2 = (t & 0x000000ff) << 16;
        uint32_t t3 = (t & 0x0000ff00);
        uint32_t ta = (t & 0xFF000000);
        *(dPtr++) = t1 | t2 | t3 | ta;
    }

    hBitmapTexture = CreateCompatibleBitmap(GetDC(NULL), desc.Width, desc.Height);
    SetBitmapBits(hBitmapTexture, desc.Width*desc.Height * 4, dptr);

    return (HBITMAP)CopyImage(hBitmapTexture, IMAGE_BITMAP, desc.Width, desc.Height,
LR_CREATEDIBSECTION);
}


int main(array ^args)
{
    HRESULT hr;

    // Supported feature levels.
    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_1
    };

    D3D_FEATURE_LEVEL d3dFeatLvl;
    ID3D11Device* pDevice = nullptr;
    ID3D11DeviceContext* pImmediateContext = nullptr;

    // Get device object.
    hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE,
        NULL, 0, featureLevels,
        ARRAYSIZE(featureLevels),
        D3D11_SDK_VERSION,
        &pDevice,
        &d3dFeatLvl,
        &pImmediateContext);
    EXIT(hr);

    // Get DXGI device.
    IDXGIDevice* DxgiDevice = nullptr;
    hr = pDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&DxgiDevice));
    EXIT(hr);

    // Get DXGI adapter.
    IDXGIAdapter* DxgiAdapter = nullptr;
    hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast(&DxgiAdapter));
    DxgiDevice->Release();
    DxgiDevice = nullptr;
    EXIT(hr);

    // Get DXGI output.
    IDXGIOutput* DxgiOutput = nullptr;
    hr = DxgiAdapter->EnumOutputs(0, &DxgiOutput);
    DxgiAdapter->Release();
    DxgiAdapter = nullptr;
    EXIT(hr);

    DXGI_OUTPUT_DESC OutputDesc;
    DxgiOutput->GetDesc(&OutputDesc);

    // Query interface for Output1.
    IDXGIOutput1* DxgiOutput1 = nullptr;
    hr = DxgiOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast(&DxgiOutput1));
    DxgiOutput->Release();
    DxgiOutput = nullptr;
    EXIT(hr);

    // Create desktop duplication.
    IDXGIOutputDuplication* DeskDupl = nullptr;
    hr = DxgiOutput1->DuplicateOutput(pDevice, &DeskDupl);
    DxgiOutput1->Release();
    DxgiOutput1 = nullptr;
    EXIT(hr);

    DXGI_OUTDUPL_DESC OutputDuplDesc;
    DeskDupl->GetDesc(&OutputDuplDesc);

    ///////////////////////////////////////////////////////////////////////////

    ID3D11Texture2D* AcquiredDesktopImage = nullptr;

    IDXGIResource* DesktopResource = nullptr;
    DXGI_OUTDUPL_FRAME_INFO FrameInfo;

    // Get new frame.
    hr = DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
    EXIT(hr);

    // If still holding old frame, destroy it.
    if (AcquiredDesktopImage)
    {
        AcquiredDesktopImage->Release();
        AcquiredDesktopImage = nullptr;
    }

    // Query interface for IDXGIResource.
    hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast(&AcquiredDesktopImage));
    DesktopResource->Release();
    DesktopResource = nullptr;
    EXIT(hr);

    HBITMAP hBmp = (HBITMAP)ExtractBitmap(AcquiredDesktopImage, pDevice);

    ///////////////////////////////////////////////////////////////////////////

    System::Drawing::Bitmap::FromHbitmap(IntPtr(hBmp))->Save("Screenshot.bmp");

    Console::WriteLine("END!");
    Console::ReadKey();
    return 0;
}


Привожу пример своего исходника по работе с Desktop Duplication API (на C++/CLI).
К сожалению, не могу добиться от него результата - картинка получается пустая.
Каким образом можно поправить этот код, чтобы он работал; +добавить цикл, чтобы можно
было получать скрины не вызывая функцию целиком.
    


Ответы

Ответ 1



Собственно, вот. #include "D3D9.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma comment(lib, "D3D11.lib") #pragma comment(lib, "D3d9.lib") #pragma comment(lib, "dxgi.lib") #pragma comment(lib, "gdi32.lib") using namespace System; #define EXIT(hr) { if (FAILED(hr)) \ { Console::WriteLine("Error!"); \ Console::ReadKey(); return -1; } } HBITMAP ExtractBitmap(ID3D11Texture2D* d3dtex, ID3D11Device* pDevice) { HRESULT hr; HBITMAP hBitmapTexture = NULL; HGDIOBJ hBitmap; D3D11_TEXTURE2D_DESC desc; ID3D11Texture2D* pNewTexture = NULL; D3D11_TEXTURE2D_DESC description; d3dtex->GetDesc(&desc); d3dtex->GetDesc(&description); description.BindFlags = 0; description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; description.Usage = D3D11_USAGE_STAGING; description.MiscFlags = 0; if (FAILED(pDevice->CreateTexture2D(&description, NULL, &pNewTexture))) { return NULL; } ID3D11DeviceContext* ctx = NULL; pDevice->GetImmediateContext(&ctx); ctx->CopyResource(pNewTexture, d3dtex); D3D11_MAPPED_SUBRESOURCE resource; UINT subresource = D3D11CalcSubresource(0, 0, 0); ctx->Map(pNewTexture, subresource, D3D11_MAP_READ_WRITE, 0, &resource); // Copy from texture to bitmap buffer. uint8_t* sptr = reinterpret_cast(resource.pData); uint8_t* dptr = new uint8_t[desc.Width*desc.Height * 4]; for (size_t h = 0; h < desc.Height; ++h) { size_t msize = std::min(desc.Width * 4, resource.RowPitch); memcpy_s(dptr, desc.Width * 4, sptr, msize); sptr += resource.RowPitch; dptr += desc.Width * 4; } dptr -= desc.Width*desc.Height * 4; // SWAP BGR to RGB bitmap uint32_t *dPtr = reinterpret_cast(dptr); for (size_t count = 0; count < desc.Width*desc.Height * 4; count += 4) { uint32_t t = *(dPtr); uint32_t t1 = (t & 0x00ff0000) >> 16; uint32_t t2 = (t & 0x000000ff) << 16; uint32_t t3 = (t & 0x0000ff00); uint32_t ta = (t & 0xFF000000); *(dPtr++) = t1 | t2 | t3 | ta; } hBitmapTexture = CreateCompatibleBitmap(GetDC(NULL), desc.Width, desc.Height); SetBitmapBits(hBitmapTexture, desc.Width*desc.Height * 4, dptr); return (HBITMAP)CopyImage(hBitmapTexture, IMAGE_BITMAP, desc.Width, desc.Height, LR_CREATEDIBSECTION); } int main(array ^args) { HRESULT hr; // Supported feature levels. D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1 }; D3D_FEATURE_LEVEL d3dFeatLvl; ID3D11Device* pDevice = nullptr; ID3D11DeviceContext* pImmediateContext = nullptr; // Get device object. hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &pDevice, &d3dFeatLvl, &pImmediateContext); EXIT(hr); // Get DXGI device. IDXGIDevice* DxgiDevice = nullptr; hr = pDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&DxgiDevice)); // Get DXGI adapter. IDXGIAdapter* DxgiAdapter = nullptr; hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast(&DxgiAdapter)); if (FAILED(hr)) { Console::WriteLine("ERROR: failed to DxgiDevice->GetParent"); Console::Read(); return -1; } DxgiDevice->Release(); DxgiDevice = nullptr; // Get DXGI output. IDXGIOutput* DxgiOutput = nullptr; hr = DxgiAdapter->EnumOutputs(0, &DxgiOutput); if (FAILED(hr)) { Console::WriteLine("ERROR: failed to DxgiAdapter->EnumOutputs"); Console::Read(); return -1; } DxgiAdapter->Release(); DxgiAdapter = nullptr; DXGI_OUTPUT_DESC OutputDesc; DxgiOutput->GetDesc(&OutputDesc); // Query interface for Output1. IDXGIOutput1* DxgiOutput1 = nullptr; hr = DxgiOutput->QueryInterface(__uuidof(IDXGIOutput1), reinterpret_cast(&DxgiOutput1)); if (FAILED(hr)) { Console::WriteLine("ERROR: failed to DxgiOutput->QueryInterface"); Console::Read(); return -1; } DxgiOutput->Release(); DxgiOutput = nullptr; // Create desktop duplication. IDXGIOutputDuplication* DeskDupl = nullptr; hr = DxgiOutput1->DuplicateOutput(pDevice, &DeskDupl); if (FAILED(hr)) { Console::WriteLine("ERROR: failed to DxgiOutput1->DuplicateOutput"); Console::Read(); return -1; } DxgiOutput1->Release(); DxgiOutput1 = nullptr; DXGI_OUTDUPL_DESC OutputDuplDesc; DeskDupl->GetDesc(&OutputDuplDesc); /////////////////////////////////////////////////////////////////////////// ID3D11Texture2D* AcquiredDesktopImage = nullptr; IDXGIResource* DesktopResource = nullptr; DXGI_OUTDUPL_FRAME_INFO FrameInfo; // Get new frame. hr = DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource); if (FAILED(hr)) { Console::WriteLine("ERROR: failed to DeskDupl->AcquireNextFrame"); Console::Read(); return -1; } // If still holding old frame, destroy it. //if (AcquiredDesktopImage) //{ //AcquiredDesktopImage->Release(); //AcquiredDesktopImage = nullptr; //} // Query interface for IDXGIResource. hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast(&AcquiredDesktopImage)); if (FAILED(hr)) { Console::WriteLine("ERROR: failed to DesktopResource->QueryInterface"); Console::Read(); return -1; } DesktopResource->Release(); DesktopResource = nullptr; HBITMAP hBmp = (HBITMAP)ExtractBitmap(AcquiredDesktopImage, pDevice); /////////////////////////////////////////////////////////////////////////// System::Drawing::Bitmap::FromHbitmap(IntPtr(hBmp))->Save("Screenshot.bmp"); Console::WriteLine("END!"); Console::ReadKey(); return 0; } На вопрос где же была ошибка, могу сказать одно. Копируя чужой код в свой проект, проверяйте его, а так же читайте комментарии :)

Ответ 2



Подробный ответ дан здесь Суть в двух моментах: Однократного вызова AcquireNextFrame недостаточно для получения изображения экрана, так как "Frame" - это необязательно изображение экрана, а совокупность некоторой информации о изменении состояния рабочего стола (среди которой, иногда, может быть и изображение экрана). Для получения изображения нужно циклически вызывать AcquireNextFrame и сохранять все полученные результаты. Для корректного зацикливания нужно освободить Frame: IDXGIOutputDuplication* DeskDupl; //инициализация... DeskDupl->AcquireNextFrame(5000, &FrameInfo, &DesktopResource); //работа с Frame... hr = DeskDupl->ReleaseFrame();//освобождаем Frame if (FAILED(hr)) { //произошла ошибка } else { //можно получить следующий Frame }

суббота, 21 декабря 2019 г.

Работа с картинками через GDI+ VS DirectX

#c_sharp #net #directx #gdi #gdi+


Допустим, имеется задача преобразовать N картинок из одного формата в другой, с последующей
сменой размера картинок, уменьшение качества картинок и т п вещи.

Имеется ПК с Core i5 и видеокарта, которая поддерживает DirectX 9 с 512 МБ ОЗУ.

Интересует следующее:


Выполнится конвертирование через DirectX быстрее, чем через GDI+ ?
Предоставляет ли .NET какие-то обертки над DirectX, похожие на GDI+ по юзабельности?
Я знаю, что WPF использует для отрисовки аппаратное ускорение. Можно ли воспользоваться
какими-то его примитивами.
Какой подход используется в популярных пакетных конвертировщиках?
Они используют GDI+ или пишут свое более быстрое решение, которое не использует GDI+(Т.е
опять же CPU зависимые) или используют аппаратные возможности?

    


Ответы

Ответ 1



По сути я могу ошибаться, но скажу следующее: Приходилось работать с изображениями, с их генерацией, и в наследие доставался код на WinForms (с отрисовкой на GDI+), из которого сразу было решено выпилить любое упоминание WinForms и сделать всё это на том, что есть в WPF. 1) Работать стало быстрее, а если еще, как вы говорите конвертацию - сделайте её в пуле потоков Parallel.ForEach(); Тогда вообще будет очень хорошо. Только про потокобезопасность не забывайте. 2) Да, представляет. Может не в таком обилии как GDI+, но стоит разобраться и вам этого достаточно будет. Можно дополнить сторонними библиотеками на крайний случай. 3) Они используют как и CPU, так и GPU. Вообще использованием ресурсов занимается сам DX и пиковой производительности добиваются их совместным использованием. PS. Если я в чём-то не прав, то критика приветствуется.

Ответ 2



Как раз таки, DirectX использует аппаратное ускорение, у GDI с этим проблемы. .NET с шарпом имеют SharpDX - это как раз что то вроде обертки над DirectX, юзать не сказать что удобно, но в сравнении с тем же на Си++ разница чувствуется.

понедельник, 16 декабря 2019 г.

Высококачественное масштабирование двумерных текстур

#cpp #windows #directx #2d #texture


Имеется экземпляр ID3D11Texture2D, который представляет текстуру содержащую некоторый
текст небольшого размера. Задача состоит в уменьшении размера этой текстуры в 2 раза
таким образом, чтобы текст сохранил читабельность.

P.S. Возможно использование сторонних библиотек.
    


Ответы

Ответ 1



Хоть Вы и говорите, что распознавать текст не нужно, но кроме как найти этот текст на текстуре, сохранить его (в виде картинки), далее уменьшить текстуру и на неё наложить текст в оригинальном разрешении сложно придумать что - то более простое. Конечно даже такой вариант требует достаточно сложно алгоритма и будет работать только с простыми текстурами, вроде этой. Не забудьте закрасить фон проверить, что текст влезает в полученную текстуру, иначе уменьшить его

Ответ 2



Наилучший результат как при уменьшении, так и при увеличении дают свёрточные фильтры. При их использовании уменьшение изображения очень похоже на наивное уменьшение, когда мы просто выбираем пиксели с определённым интервалом. Но чтобы не появилась некрасивая рябь, фильтр берёт не просто пиксели, а взвешенную сумму всех соседей на некотором расстоянии от этого пикселя: Матрица — это представление ядра свёртки в виде таблицы дискретных значений. В данном случае показан расчёт нового цвета для левого верхнего пикселя на основании цвета его непосредственных соседей. Источник изображения: https://habrahabr.ru/post/142818/ Для того, чтобы найти коэффициенты взвешенной суммы, воспользуемся фильтром Ланцоша. Он не даёт муара, а также повышает контраст изображения, делая границы чётче (что особо важно для относительно мелкого текста, читаемость которого необходимо сохранить). Мы бы могли вычислить эту таблицу самостоятельно (ради чего весь текст выше и затевался), но так как я немного запутался и в формулах, и в GIMP-овом генераторе таблицы коэффициентов, предлагаю-таки воспользоваться готовой библиотекой. Соответственно, вам подойдёт любая библиотека, работающая с этим фильтром. Вот несколько примеров: avir, fpresize. Примечание: все эти библиотеки работают с изображением в оперативной, не видеопамяти. Соответственно применять их надо после загрузки из файла и перед отправкой в объект текстуры.

Ответ 3



Переношу в ответ информацию из комментариев, для истории. Да в общем-то, в текущем виде, ответ на вопрос - "никак". Текстура DirectX - обычное растровое изображение, уменьшение растрового изображения в 2 раза означает потерю 50% информации. Для шрифта 10 размера при стандартном DPI это значит, что он будет нечитаем - тут ничего не поделать. Но, более общая задача - представить информацию с рабочего стола пользователя в таком виде, что ее можно отобразить в любом масштабе, скорее всего решаема. Ведь текст в приложениях не зарождается изначально в растровом виде, а выводится вызовом вполне определенного набора функций из GDI, GDI+ и DirectWrite. Про механику DWM мало знаю. Но для текущей задачи он бесполезен, так как оперирует теми же текстурами. Я намекаю на другое - замена на целевой системе библиотек gdi и DirectWrite на свои взломанные версии, которые бы логировали аргументы, передаваемые функциям вывода текста, чтобы потом вызвать их с другим масштабом.

воскресенье, 15 декабря 2019 г.

Сложность с D3D хуком

#cpp #dll #directx #hook


Здравствуйте! Пытаюсь сделать меню для игрушки, но не могу разобраться в функции,
которая крашит программу после инъекции. Опишу все подробно с комментариями, если я
не прав, пожалуйста поправьте:
Создаю поток в процессе (инжект работает нормально) с таким содержимым:

    DWORD*vtbl = 0;
    //Здесь я записываю начальный адрес модуля 
    DWORD hD3D9 = (DWORD)LoadLibraryA("d3d9.dll");
    //ищу функцию Direct3DCreate или что то другое?
    DWORD table = MemHack->FindPattern(hD3D9, 0x128000, (PBYTE)"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86",
"xx????xx????xx");//
    // со смещением в 2 байта копируем указатель
    memcpy(&vtbl, (void*)(table + 2), 4);


Здесь table = 6ace76d3. Вот кусок памяти:

d3d9.Direct3DCreate9Ex+54AC - E8 71000000           - call        d3d9.Direct3DCreate9Ex+5522
d3d9.Direct3DCreate9Ex+54B1 - 33 C0                 - xor eax,eax
d3d9.Direct3DCreate9Ex+54B3 - C7 06 9C86CD6A        - mov [esi],d3d9.dll+869C { [6ACD9D30] }
d3d9.Direct3DCreate9Ex+54B9 - 89 86 08310000        - mov [esi+00003108],eax
d3d9.Direct3DCreate9Ex+54BF - 89 86 00310000        - mov [esi+00003100],eax


Далее идет вызов функции, который крашит программу:

// vtbl[42] указывает на функцию endScene размером 5 байт?
pEndScene = (oEndScene)Mem->Create_Hook((PBYTE)vtbl[42], (PBYTE)myEndScene, 5);


Приведу код функции, который в общем представлении я не понял:

void * cMemory::Create_Hook(BYTE *src, const BYTE *dst, const int len)
{
    BYTE *jmp;
    DWORD dwback;
    DWORD jumpto, newjump;

    VirtualProtect(src,len,PAGE_EXECUTE_READWRITE,&dwback);
    // вот отсюда я не понял зачем эта проверка вообще нужна
    if(src[0] == 0xE9)
    {
        jmp = (BYTE*)malloc(10);
        jumpto = (*(DWORD*)(src+1))+((DWORD)src)+5;
        newjump = (jumpto-(DWORD)(jmp+5));
        jmp[0] = 0xE9;
        *(DWORD*)(jmp+1) = newjump;
        jmp += 5;
        jmp[0] = 0xE9;
        *(DWORD*)(jmp+1) = (DWORD)(src-jmp);
    }
    else
    {
        jmp = (BYTE*)malloc(5+len);
        memcpy(jmp,src,len);
        jmp += len;
        jmp[0] = 0xE9;
        *(DWORD*)(jmp+1) = (DWORD)(src+len-jmp)-5;
    }
    src[0] = 0xE9;
    *(DWORD*)(src+1) = (DWORD)(dst - src) - 5;

    for(int i = 5; i < len; i++)
        src[i] = 0x90;

    VirtualProtect(src,len,dwback,&dwback);
    // здесь указатель на функцию?
    return (jmp-len);
}

    


Ответы

Ответ 1



Ваши ошибки: Игра написана на directX8, а не 9 версии, Вы пытаетесь хукнуть 9 версию, собственно LoadLibraryA("d3d9.dll"), нужно LoadLibraryA("d3d8.dll") и в таблице виртуальные методы там, на сколько я помню, имеют немного другую последовательность (для проверки того, какую версию dx использует приложения нужно всего лишь открыть отладчик и просмотреть список загруженных модулей или посмотреть инфо в википедии). Вы пытаетесь(скорее всего, это мои догадки) сделать иньекцию кода в процесс generals.exe, но это просто дочернее окно и оно не выполняет функции отрисовки игрового мира\меню и т.д., Вам нужен процесс game.dat. Вечером, когда приду домой, постараюсь переделать Ваш код в рабочее состояние и дополню ответ. Если Вам интересно, можете почитать интересную статью по Вашей теме. UPD: #include #include #include #pragma comment (lib, "d3d8.lib") #pragma comment (lib, "d3dx8.lib") #pragma comment( lib, "LIBCI.lib" ) #include "MessageControll.h" #include "ConsoleControll.h" //"Game.dat"+0056C9A4 // Hook Function HRESULT WINAPI HookedPresent(LPDIRECT3DDEVICE8 pDevice, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion); HRESULT WINAPI HookedReset(LPDIRECT3DDEVICE8 pDevice, D3DPRESENT_PARAMETERS *params); // Orig Function typedef HRESULT(WINAPI* tPresent)(LPDIRECT3DDEVICE8 pDevice, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion); typedef HRESULT(WINAPI *tReset)(LPDIRECT3DDEVICE8 pDevice, D3DPRESENT_PARAMETERS *pp); DWORD origReset = 0, origPresent = 0; tPresent _tPresent = nullptr; tReset _tReset = nullptr; ID3DXFont* pFont = nullptr; void createFont(IDirect3DDevice8* pDevice) { LOGFONT log_font = { 50, //height 0, //width; 0, // lfEscapement; 0, //lfOrientation; FW_BOLD, // lfWeight; FALSE, // lfItalic; FALSE, // lfUnderline; FALSE, // lfStrikeOut; DEFAULT_CHARSET, // lfCharSet; OUT_DEFAULT_PRECIS, //lfOutPrecision; CLIP_DEFAULT_PRECIS, // lfClipPrecision; ANTIALIASED_QUALITY,// lfQuality; DEFAULT_PITCH,// lfPitchAndFamily; "Tahoma"// lfFaceName[LF_FACESIZE]; }; HFONT font = CreateFont(10, 10, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, 0, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial"); if (D3DXCreateFontIndirect(pDevice, &log_font, &pFont) != D3D_OK) { console("D3DXCreateFontIndirect error!"); } } VOID D3DX_Font(LPDIRECT3DDEVICE8 Device_Interface) { RECT Rect = { 0,0,1000,1000 }; //if (Device_Interface != NULL) //{ // HFONT Logical_Font_Characteristics = CreateFont(16, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 2, 0, "Arial"); // if (Logical_Font_Characteristics != NULL) // { // D3DXCreateFont(Device_Interface, Logical_Font_Characteristics, &pFont); // DeleteObject(Logical_Font_Characteristics); // } //} if (pFont != NULL) { pFont->Begin(); pFont->DrawText("StackOverflow.ru!", -1, &Rect, 0, 0xFFFF0000); pFont->End(); } } //========================================================= //if (bDrawText && m_pFont) //{ // PrintTextB(m_pFont, // 250, 20, //x, y // 255, 255, 255, 255, //color values (and then alpha) // "Testing 1234567890!!!"); //} //My custom PrintText Function: //void drawText(const D3DXVECTOR4& area, const DWORD& color, TCHAR *text, ...) //{ // TCHAR buf[1024]{}; // // RECT FontRect = { // // (long)area.x, // (long)area.y, // (long)area.x + (long)area.z, // (long)area.y + (long)area.w // }; // // va_list vaList; // va_start(vaList, text); // //#ifdef UNICODE // vswprintf_s(buf, text, vaList); //#else // vsprintf_s(buf, text, vaList); //#endif // // va_end(vaList); // // pFont->Begin(); // pFont->DrawText(buf, -1, &FontRect, 0, color); // pFont->End(); //} void* DetourCreate(BYTE *src, const BYTE *dst, const int len) { BYTE *jmp; DWORD dwback, dwold; DWORD jumpto, newjump; VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &dwback); if (src[0] == 0xE9) { jmp = (PBYTE)malloc(10); VirtualProtect(jmp, 10, PAGE_EXECUTE_READWRITE, &dwold); jumpto = (*(DWORD*)(src + 1)) + ((DWORD)src) + 5; newjump = (jumpto - (DWORD)(jmp + 5)); jmp[0] = 0xE9; *(DWORD*)(jmp + 1) = newjump; jmp += 5; jmp[0] = 0xE9; *(DWORD*)(jmp + 1) = (DWORD)(src - jmp); } else { jmp = (PBYTE)malloc(5 + len); VirtualProtect(jmp, 5 + len, PAGE_EXECUTE_READWRITE, &dwold); memcpy(jmp, src, len); jmp += len; jmp[0] = 0xE9; *(DWORD*)(jmp + 1) = (DWORD)(src + len - jmp) - 5; } src[0] = 0xE9; *(DWORD*)(src + 1) = (DWORD)(dst - src) - 5; for (int i = 5; i < len; i++) { src[i] = 0x90; } VirtualProtect(src, len, dwback, &dwback); return (jmp - len); } void GetDevice9Methods() { D3DDISPLAYMODE ds{}; D3DPRESENT_PARAMETERS pp{}; IDirect3DDevice8* dev{}; HMODULE hD3D8Dll = GetModuleHandle("d3d8.dll"); //console("address \"d3d8.dll\" [%#x]", hD3D8Dll); //Инициализируем свой девайс, чтобы узнать адреса функций ресета и презента в виртуальной таблице методов if (hD3D8Dll) { IDirect3D8* d3d8 = Direct3DCreate8(D3D_SDK_VERSION); //console("address \"IDirect3D8\" [%#x]", d3d8); if (d3d8->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &ds) != D3D_OK) { d3d8->Release(); return; } pp.Windowed = 1; pp.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC; pp.BackBufferFormat = ds.Format; if (d3d8->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetForegroundWindow(), D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, &dev) == D3D_OK) { if (dev) { //Копируем адреса виртуальных методов origReset = *((DWORD*)(*(DWORD*)dev) + 14); origPresent = *((DWORD*)(*(DWORD*)dev) + 15); //console("address \"Reset method\" [%#x]", origReset); //console("address \"Present method\" [%#x]", origPresent); dev->Release(); d3d8->Release(); } } else { d3d8->Release(); return; } } } DWORD WINAPI InitThread(HINSTANCE hinstDLL) { //t_con.create(); GetDevice9Methods(); if (origReset && origPresent) { _tPresent = (tPresent)DetourCreate((PBYTE)origPresent, (PBYTE)HookedPresent, 5); _tReset = (tReset)DetourCreate((PBYTE)origReset, (PBYTE)HookedReset, 5); } while (!GetAsyncKeyState(VK_END)) { Sleep(1); } //t_con.del(); FreeLibraryAndExitThread(hinstDLL, 0); return 1; } HRESULT WINAPI HookedPresent(LPDIRECT3DDEVICE8 pDevice, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) { if (!pFont) { createFont(pDevice); } else { D3DX_Font(pDevice); } D3DRECT rec = { 10, 10, 30, 30 }; //pDevice->Clear(1, &rec, D3DCLEAR_TARGET, 0xFFFF0000, 1.0f, 0); return _tPresent(pDevice, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); } HRESULT WINAPI HookedReset(LPDIRECT3DDEVICE8 pDevice, D3DPRESENT_PARAMETERS *params) { return _tReset(pDevice, params); } BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH://Вызывается первым CreateThread(0, 0, LPTHREAD_START_ROUTINE(InitThread), hinstDLL, 0, 0); return 1; case DLL_PROCESS_DETACH://вызывается после освобождения библиотеки break; case DLL_THREAD_ATTACH://вызывается при создании потока break; case DLL_THREAD_DETACH://вызывается после разрушения потока break; } return TRUE; } Как пофиксить проблемы с нехваткой libci: Скачать саму либу и заинклудить в проект; Заинклудить дополнительные зависимости, как сказано тут. Результат:

понедельник, 9 декабря 2019 г.

Как в OpenGL или DirectX называется технология затенения углов?

#opengl #графика #directx #шейдеры #технологии


Это запеченные карты света? Или Есть какая то технология позволяющая сделать это
программно?




    


Ответы

Ответ 1



Этот эффект затенения на вашей картинке называется Ambient Occlusion или AO Он заключается в затенении поверхностей на основе информации об объектах (поверхностях) рядом. Реализаций AO много, например "пост-эффект" во фрагментном шейдере (Screen Space Ambient Occlusion или SSAO) на основе информации о положении пикселей и нормалях. Относительно прост и дешев, из за чего и популярен, не зависит от сложности сцены. К недостаткам алгоритма можно отнести невозможность обработки полу-прозрачных поверхностей + невозможность аппаратного сглаживания (antialiasing). SSAO можно отнести к технике так называемого отложенного рендеринга или deferred rendering (shading) , это такой фокус, когда вместо кадра на экране формирутся так называемый G-Buffer это несколько рендер текстур (render texture) в которые рисуется кадр, только кроме цвета в каждое значение пикселя записывается информация о: xyz пикселя относительно камеры векторе нормали к поверхности в этой точке любая другая информация (например о типе материала, глубине, и так далее) В последствии поверх всего экрана рисуется один треугольник, таким образом чтобы он закрыл весь экран. Это нужно чтобы вызвать фрагментный шейдер для каждого пикселя. В шейдере происходит вся магия. Там берется информация из сформированного недавно g-buffer и формируется кадр с освещением-затенением. Описанное выше это общая часть для множества алгоритмов освещения, затенения, фильтрации изображений и других странных фокусов вроде screen space reflections, работающих в экранном пространстве. т.е. их сложность растет не со сложностью сцены а с размером кадра. SSAO Если принять за освещенность точки сумму всех векторов фотонов из полусферы: и предположить что если рядом есть поверхность или объект, то фотоны через него не проходят, тогда получается что их можно не учитывать при расчете освещения. Улучшение результата возможно за счет учета нормалей для обработки плоских поверхностей: Так же дает хорошие результаты не простое монте-карло по соседним пикселям, а например, poisson sampling: Вот пример screen space AO на three.js. PS: заглядывайте сюда, попозже сделаю живой пример..

суббота, 30 ноября 2019 г.

XNA есть ли будущее? Какие альтернативы?

#xna #c_sharp #directx


Увы не обнаружил XNA для VS Desktop 2012 Express.
Нашел массу хаков, но они не внушают доверия.
Кто нибудь в курсе MS забил на XNA? Или все таки этот фреймворк будет развиваться?
Возможно это только слухи, но есть информация что MS не будет делать XNA для Desktop
и оставит ее только для мобильников и XBOX.
Если нет, какие есть альтернативы? Насколько я понимаю есть какие то средства для
использования DirectX в C#? Подскажите какие?    


Ответы

Ответ 1



В vs12 xna есть, но пока что только для windows phone. Скорее всего, позже сделают и для desktop'ов. Также советую присмотреться к SharpDX

Ответ 2



XNA добавляет гигантский оверхед по сравнению с нативным DirectX. Если нужно писать на C#, то правильным выбором будет SharpDX.

Ответ 3



Недавняя статья на хабре пролила свет на вопрос: Введение в разработку игр для Windows 8 с использованием XNA и MonoGame. Если точнее: есть открытая разработка - MonoGame (аналог XNA). Программирование на MonoGame позволяет почти не менять код для XNA. Так что ее можно считать полной альтернативой. В ответах так же был озвучен SharpDX, однако есть и другие: EaselJS ImpactJS Construct2 Delta Engine Unigine Ogre Engine Cocos2D Bepu Physics Jitter Physics Box2DX

среда, 15 мая 2019 г.

Использование DirectX в C#

Написал программу на C# (квадрат из 8 точек, повороты, масштабирование, сброс). Теперь нужно переделать её под DirectX, о котором я вообще ничего не знаю... Я даже не знаю как его подключать, могли бы вы посоветовать очень простую лекцию для выполнения этого простого задания...


Ответ

Подключить DX функции для создания графики можно установив пакет DirectX SDK который можно скачать с оффсайта Microsoft. А переделать под него - означает использовать в качестве функции рисования API DX'а(DirectDraw), самый банальный способ на мой взгляд и простой это написать ту же программу юзая Microsoft Visual C++, там вывод графики осуществляется по средствам DirectDraw. Это довольно обширная тема чтобы описать все этапы тут, ведь нам потребуется написать процедуру вызова окна, инициализации палитры и много чего другого, поэтому поделюсь ссылкой на ресурс который помог мне в этом разобраться. http://www.helloworld.ru/texts/comp/games/directx/ddraw/directx.htm

пятница, 1 марта 2019 г.

Перерисовка окна при изменении его размера

Только начал изучать DirectX и WinAPI. Написал простейшую программу: создаёт квадрат на всё окно и "раскрашивает" его шейдером. Но при уменьшении размера окна изображение сжимается, а при увеличении - растягивается. Как сделать, чтобы этого не происходило, т.е. при уменьшении, например, изображение не сжималось, а просто показывалась его меньшая область. Пробывал сделать это так:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // ...
switch (message) { // ...
case WM_SIZE: D3D11_VIEWPORT vp; vp.Width = (FLOAT)LOWORD(lParam); vp.Height = (FLOAT)HIWORD(lParam); vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; if (g_pImmediateContext) g_pImmediateContext->RSSetViewports(1, &vp); break;
// ... }
return 0; }
Думал, сработает, но получил странное поведение: при уменьшении размера окна изображение ещё сильнее уменьшается, а при увеличении остаётся неизменным. Как реализовать описанное ранее поведение правильно?


Ответ

Вне зависимости от фактических размеров окна его DirectX-ная система координат всегда будет описывать прямоугольник (0, 0)-(1, 1) (DX10) или (-1, -1)-(1, 1) (DX9). Для этого DirectX будет специально масштабировать (а в DX9 ещё и перемещать) координаты на кадровом буфере.
Соответственно, ваша задача заключается в применении матрицы трансформации, которая отменяла бы подобный эффект, «сжимая» обе координаты прямо пропорционально габаритам окна. Причём эта матрица должна применяться после применения всех остальных матриц, если они есть.
Для DirectX 11 (упомянутого в вопросе) матрица будет иметь следующий вид:
где: α — масштабный коэффициент, указывающий, сколько пикселей должно приходиться на единичный отрезок обзорной области. w, h — ширина и высота клиентской области окна; получать при каждом WM_SIZE с помощью LOWORD(lParam) и HIWORD(lParam) соответственно.

среда, 20 февраля 2019 г.

GLSL, HLSL Шейдер из нескольких файлов

Например есть 3 шейдера. параллакс, бамп, и солид. В каждом шейдере должны быть общие вычисления типа вычисления источников света.
Реально ли в GLSL и HLSL вынести эти функции в отдельный файл и потом их как бы подключать как .h файл к коду программы? Каковы конструкции, ключевые слова в шейдерных языках и что нужно сделать в коде программы для того чтобы их использовать?


Ответ

Насколько мне видится, самым универсальным решением для HLSL и GLSL может быть написание своего простенького препроцессора, который будет вместо строке #include("bump.glsl") вставлять код этого файла.
Также можно пытаться обойтись дефайнами и таким образом отключать или включать какие-то функции в шейдере.
P.S. В вопросе наверное стоило указать версию GLSL и HLSL, которую вы планируете использовать.

понедельник, 14 января 2019 г.

Подключение DirectX к проекту WPF

Пишу небольшую игрушку на WPF, и для одновременного воспроизведения многих звуков использую DirectSound. До этого пытался использовать AudioVideoPlayback, но была точно такая же проблема. Я добавил в проект ссылку на DirectSound.dll из папки DirectX. Прописал using Microsoft.DirectX.DirectSound; Вот эти строки отвечают за инициализацию: Microsoft.DirectX.DirectSound.Buffer sp; Microsoft.DirectX.DirectSound.Buffer deathSp; Device m_SoundDevice; В конструкторе окна выделяю память, подгружаю все: m_SoundDevice = new Device(); m_SoundDevice.SetCooperativeLevel((new system.Windows.Interop.WindowInteropHelper(this)).Handle, CooperativeLevel.Priority);
sp = new Microsoft.DirectX.DirectSound.Buffer("Shot2.wav", m_SoundDevice); deathSp = new Microsoft.DirectX.DirectSound.Buffer("Death1.wav", m_SoundDevice); Проблема состоит в том, что если в свойствах проекта стоит .NET 4.0 или выше, то при отладке студия иногда просто зависает, и закрывается только завершением процесса студии, иногда сразу же выдает ошибку, как при версиях 3.Х. Скомпилированный при этом экзешник выдает ошибку при запуске. Если понизить версию .NET до 3.5 или 3.0, то при отладке программа запускается, но вылетает при инициализации, которую я указал выше. Пишет следующее: Нет доступных исходных файлов. В стеке вызова содержится только внешний код. XamlParseException не обработано "Вызов конструктора для типа "Game.MainWindow", удовлетворяющего указанным ограничениям привязки, привел к выдаче исключения.": номер строки "3" и позиция в строке "9". Я не пойму, в чем причина. Где эта третья строка - тоже. В xaml в 3ей строке ничего интересного, как и в самом коде на 3ей строке.


Ответ

Уфф, я разобрался в проблеме. Причина была в том, что задавать CooperativeLevel для SoundDevice нельзя во время инициализации нашего MainWindow. Инициализацию всего DirectSound нужно вешать на событие OnLoad нашего главного окна, либо в любой момент после завершения его инициализации.

четверг, 29 ноября 2018 г.

Как работает программа показа живых обоев на рабочем столе (winVista/7/8/8.1)?

Существует программа DreamScenes, которая позволяет устанавливать видео обои на рабочий стол (обычное видео в формате dream/wmv/mpg). Также в VLC media player есть функция показа видео в режиме обоев (исходники данной программы доступны, но разобраться в них не получилось: Исходники). Как выглядит код, реализующий такую фичу? Пока я смогла реализовать такое "чудо" таким образом (примерный код, рабочий): #include #include #include
using namespace std;
void main() { WIN32_FIND_DATA File; HANDLE F; for(int i=0; i < 10; i++) { F=FindFirstFile("C:\\leto\\*.jpg", &File); if (F!=INVALID_HANDLE_VALUE) { do { ofstream bat("C:\\logo.bat", ios_base::out); bat << "REG ADD \"HKCU\\Control Panel\\Desktop\" /v Wallpaper /d \"C:\\leto\\" << File.cFileName <<"\" /f" << endl << "rundll32.exe user32.dll,UpdatePerUserSystemParameters" << endl; bat.close(); system("C:\\logo.vbs"); Sleep(200); } while (FindNextFile(F,&File)!=0); FindClose(F); } } system("pause"); } Описание: В папке находятся картинки (кадры анимации). Находим первую картинку, в батнике прописываем код, который меняет обои рабочего стола на мою картинку, запускаем vbs-скрипт, который позволяет выполнять батник в фоновом режиме, затем делаем паузу между выполнением следующей команды в программе С++ (0.2 сек). Таким образом наблюдается анимация на рабочем столе (но на первом проходе она виснет, почти не выполняется, потом работает, в этом и проблема). Как реализовать нормально, без зависа и кучи картинок, как в упомянутых в начале темы программах?


Ответ

Отрисовку можно сделать посредством DirectDraw, вот тут есть пример. Но, решение несовместимо с Aero, придется выключать.