Рендеринг искаженного/растянутого треугольника DirectX

В настоящее время я создаю простой движок DirectX, чтобы освежить некоторые свои знания в области графического программирования, но в настоящее время я, к сожалению, на некоторое время застрял. Я пытаюсь визуализировать простой треугольник с помощью движущейся камеры (пока только перевод), но треугольник, кажется, искажается всякий раз, когда я перемещаю камеру. То же самое происходит всякий раз, когда я перемещаю треугольник, но тогда искажение, конечно, происходит в противоположном направлении.

Вот как треугольник выглядит без движения камеры: Рендеринг искаженного/растянутого треугольника DirectX

Вот как это выглядит, когда я перемещаю камеру вправо: Рендеринг искаженного/растянутого треугольника DirectX

А вот как это выглядит, когда я поднимаю камеру: Рендеринг искаженного/растянутого треугольника DirectX

Первой моей мыслью было, что матрица WorldViewProjection как-то отключена, но я не вижу в этом никакой проблемы.

void Renderer::CreateViewProjectionMatrix()
{
    using namespace DirectX;

    // Create world matrix
    const XMMATRIX translation = XMMatrixTranslation(0.f, 0.f, 0.f);
    const XMMATRIX rotation = XMMatrixRotationRollPitchYaw(0.f, 0.f, 0.f);  // In radians
    const XMMATRIX scale = XMMatrixScaling(1.f, 1.f, 1.f);

    const XMMATRIX worldMatrix = scale * rotation * translation;
    XMStoreFloat4x4(&m_VertexConstantBuffer.worldMatrix, worldMatrix);

    // Create view matrix
    const XMFLOAT3 cameraForward{ 0.f, 0.f, 1.f };
    const XMFLOAT3 cameraUp{ 0.f, 1.f, 0.f };

    const XMVECTOR worldPos = XMLoadFloat3(&m_CameraPos);
    const XMVECTOR worldForward = XMLoadFloat3(&cameraForward);
    const XMVECTOR worldUp = XMLoadFloat3(&cameraUp);

    const XMMATRIX viewMatrix = XMMatrixLookToLH(worldPos, worldForward, worldUp);

    // Create projection matrix
    const float aspectRatioX = static_cast<float>(m_BackBufferDescription.Width) / m_BackBufferDescription.Height;
    const float FOV{ 45.f };
    const float nearZ{ 0.1f };
    const float farZ{ 100.f };
    const XMMATRIX projectionMatrix = XMMatrixPerspectiveFovLH(XMConvertToRadians(FOV), aspectRatioX, nearZ, farZ);

    // Store WVP matrix
    const XMMATRIX WVPMatrix = worldMatrix * viewMatrix * projectionMatrix;
    XMStoreFloat4x4(&m_VertexConstantBuffer.worldViewProjection, WVPMatrix);
}

Вот как создается треугольник, если он вам понадобится:

HRESULT Renderer::CreateTriangle()
{
    // Create triangle geometry
    const BaseVertexInput triangleVertices[] =
    {
        { DirectX::XMFLOAT3{ -0.5f,-0.5f, 0.0f }, DirectX::XMFLOAT3{}, DirectX::XMFLOAT2{} },
        { DirectX::XMFLOAT3{  0.5f,-0.5f, 0.0f }, DirectX::XMFLOAT3{}, DirectX::XMFLOAT2{} },
        { DirectX::XMFLOAT3{  0.0f, 0.3f, 0.0f }, DirectX::XMFLOAT3{}, DirectX::XMFLOAT2{} }
    };

    // Create vertexBuffer
    const CD3D11_BUFFER_DESC vertexDescription{ sizeof(triangleVertices), D3D11_BIND_VERTEX_BUFFER};

    D3D11_SUBRESOURCE_DATA vertexData;
    ZeroMemory(&vertexData, sizeof(D3D11_SUBRESOURCE_DATA));    // Stops writes being compiled away if it isn't being read immeadiatly
    vertexData.pSysMem = triangleVertices;                      // Initialization data
    vertexData.SysMemPitch = 0;                                 // Distance from beginning line of texture to the next line (only for 2D & 3D texture)
    vertexData.SysMemSlicePitch = 0;                            // Distance from beginning of one depth level to the next   (only for 3D texture)

    HRESULT result = m_pDevice->CreateBuffer
    (
        &vertexDescription,
        &vertexData,
        m_pVertexBuffer.GetAddressOf()
    );

    if (FAILED(result))
    {
        Logger::Log(L"ERROR - Failed to create a vertexBuffer");
        return result;
    }

    // Create indexBuffer
    const unsigned short triangleIndices[]
    {
        0,2,1
    };

    m_IndexCount = ARRAYSIZE(triangleIndices);

    const CD3D11_BUFFER_DESC indexDescription{ sizeof(triangleIndices), D3D11_BIND_INDEX_BUFFER };

    D3D11_SUBRESOURCE_DATA indexData;
    ZeroMemory(&indexData, sizeof(D3D11_SUBRESOURCE_DATA));
    indexData.pSysMem = triangleIndices;
    indexData.SysMemPitch = 0;
    indexData.SysMemSlicePitch = 0;

    result = m_pDevice->CreateBuffer
    (
        &indexDescription,
        &indexData,
        m_pIndexBuffer.GetAddressOf()
    );

    if (FAILED(result))
    {
        Logger::Log(L"ERROR - Failed to create an indexBuffer");
        return result;
    }

    // Creation success
    m_SuccesfullCreation = true;
    return result;
}

Я изучил: матрицу WVP, мой вершинный шейдер, создание буфера вершин,...

Я пытался сделать транспонирование(RightHandMatrix(...)) из руководства Microsoft, но тогда вообще не вижу никакого треугольника.

Кроме того, я больше понятия не имею, где искать. Обязательно спросите, могу ли я/нужно предоставить еще код.

Обновлено:

Вот мой полный вершинный шейдер:


cbuffer CB_World : register(b0)    // Register for GPU access (b. for constant buffers)
{
    matrix g_WorldViewProjection;   // World to projection space
    matrix g_World;                 // World space
};

struct VS_INPUT
{
    float3 position : POSITION;
    float3 normal : NORMAL;
    float2 uv : TEXCOORD0;
};

struct VS_OUTPUT
{
    float4 position : SV_POSITION;  // System value
    float3 normal : NORMAL;
    float2 uv : TEXCOORD0;
};

VS_OUTPUT VSMain(VS_INPUT input)
{
    VS_OUTPUT output;
    
    output.position = mul(float4(input.position, 1.0), g_WorldViewProjection);
    output.normal = normalize(mul(input.normal, (float3x3) g_World));
    output.uv = input.uv;
    
    return output;
}

Гитхаб: https://github.com/RenzoDepoortere/DistortedRender_AidProject

Код C++ выглядит нормально, но вам нужно будет опубликовать код вершинного шейдера, потому что это похоже на проблему преобразования вершин.

Maico De Blasio 28.08.2024 07:53

@MaicoDeBlasio Я обновил пост, добавив код вершинного шейдера.

RenzoDP 28.08.2024 09:59

Было бы лучше с полностью простым проектом воспроизведения. Дьявол прячется в деталях

Simon Mourier 28.08.2024 18:10

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

Maico De Blasio 29.08.2024 05:43

@SimonMourier, ты хочешь, чтобы я отправил весь проект? или просто начать сначала?

RenzoDP 29.08.2024 09:16

@MaicoDeBlasio, блин, в любом случае спасибо за попытку. Я попробую транспонировать матрицу еще раз, а в противном случае я могу попытаться переделать части рендеринга.

RenzoDP 29.08.2024 09:18

Вы можете опубликовать это где-нибудь в Интернете (github, что угодно).

Simon Mourier 29.08.2024 17:03

@SimonMourier добавил к посту ссылку на GitHub

RenzoDP 29.08.2024 23:00

Код в вопросе не соответствует коду в проекте. Здесь вы используете XMMatrixLookToLH находясь в проекте XMMatrixLookAtLH. Для меня результат выглядит так, как будто он был создан с помощью кода с использованием XMMatrixLookAtLH. Также прочтите о минимально воспроизводимом примере.

user7860670 01.09.2024 15:51

@user7860670 user7860670 Извините, вы правы. Между публикацией вопроса и публикацией ссылки на GitHub были внесены некоторые незначительные изменения (в основном просто удаление комментариев, изменение имен переменных с «куба» на «треугольник», а «большим» изменением является функция XMMatrixLookAtLH/XMMatrixLookToLH). Я обновил код, чтобы более точно представить то, что у меня было на момент публикации вопроса. Кроме того, что касается минимально воспроизводимого примера, я не уверен, насколько еще я смогу сэкономить на проекте, поскольку на самом деле есть только основная функция и средство рендеринга + inputManager для удобства отладки.

RenzoDP 01.09.2024 17:13

Кроме того, замена XMMatrixLookAtLH на XMMatrixLookToLH дает тот же результат.

RenzoDP 01.09.2024 17:17

изменение XMMatrixLookAtLH на XMMatrixLookToLH воспроизводит тот же результат" - сейчас в это утверждение сложно поверить. Они по-разному относятся ко второму аргументу. Передача 0.f, 0.f, 1.f в качестве второго аргумента в XMMatrixLookToLH означает, что при изменении положения камеры (передается как первый аргумент) направление обзора будет сохраняться параллельно оси Z. Передача 0.f, 0.f, 1.f в качестве второго аргумента в XMMatrixLookAtLH означает, что при изменении положения камеры (снова передается в качестве первого аргумента) направление обзора будет изменено на 0.f, 0.f, 1.f (то есть камера будет повернута).

user7860670 01.09.2024 17:42

@user7860670 user7860670 Я не совсем удачно это сформулировал. Я имел в виду, что получил те же результаты, используя XMMatrixLookToLH с cameraForward в качестве второго аргумента и XMMatrixLookAtLH с cameraPosition + cameraForward в качестве второго аргумента. Я предполагал, что вы уже ознакомились с проектом.

RenzoDP 01.09.2024 18:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
13
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я загрузил и собрал проект, проблема, похоже, представляет собой комбинацию двух:

  1. использование XMMatrixLookAtLH вместо XMMatrixLookToLH. Они по-разному относятся ко второму аргументу. Передача 0.f, 0.f, 1.f в качестве второго аргумента XMMatrixLookToLH означает, что при изменении положения камеры (передается как первый аргумент) направление обзора будет сохраняться параллельно оси Z. Передача 0.f, 0.f, 1.f в качестве второго аргумента в XMMatrixLookAtLH означает, что при изменении положения камеры (снова передается в качестве первого аргумента) направление обзора будет изменено на точку 0.f, 0.f, 1.f. (камера будет повернута).
  2. Отсутствие вызова транспонирования в конце:
const XMMATRIX WVPMatrix = XMMatrixTranspose(worldMatrix * viewMatrix * projectionMatrix);

Результат с XMMatrixLookToLH после того, как камера немного сдвинута вправо:

Результат с XMMatrixLookAtLH после того, как камера немного сдвинута вправо, что также приводит к тому, что она продолжает смотреть в одну и ту же точку 0.f, 0.f, 1.f:

Ого, такую ​​мелочь, которую я пропустил 😭. Но спасибо большое, я это очень ценю. Никогда бы не подумал, что мне понадобится транспонирование в самом конце.

RenzoDP 01.09.2024 18:47

@RenzoDP Вам также следует прочитать Зачем нам нужно использовать «транспонирование» преобразованной матрицы?

user7860670 01.09.2024 18:54

Хорошо, это имеет смысл, тогда мне, вероятно, тоже следует перенести свою матрицу мира. Спасибо!

RenzoDP 01.09.2024 19:49

Другие вопросы по теме