在其中心的 2D 纹理中实现旋转

Implementing Rotation In 2D Texture on its center

我还在学习 Directx 11。

我正在学习 Rastertek Directx 11 教程 11,我能够在屏幕上渲染 2D 纹理,这里是教程的 link:http://www.rastertek.com/dx11tut11.html

我想知道如何在其轴的中心旋转此纹理。我不知道从哪里开始。我尝试使用 z 旋转矩阵让它旋转,但它不围绕其中心旋转,而是围绕其他地方旋转。

我猜我们必须在 GraphicsClass::Render

中添加旋转
bool GraphicsClass::Render(float rotation)
{
    D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix, orthoMatrix;
    bool result;


    // Clear the buffers to begin the scene.
    m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

    // Generate the view matrix based on the camera's position.
    m_Camera->Render();

    // Get the world, view, projection, and ortho matrices from the camera and d3d objects.
    m_Camera->GetViewMatrix(viewMatrix);
    m_D3D->GetWorldMatrix(worldMatrix);
    m_D3D->GetProjectionMatrix(projectionMatrix);
    m_D3D->GetOrthoMatrix(orthoMatrix);

    // Turn off the Z buffer to begin all 2D rendering.
    m_D3D->TurnZBufferOff();

    // Put the bitmap vertex and index buffers on the graphics pipeline to prepare them for drawing.
    result = m_Bitmap->Render(m_D3D->GetDeviceContext(), 100, 100);
    if(!result)
    {
        return false;
    }

    // Render the bitmap with the texture shader.
    result = m_TextureShader->Render(m_D3D->GetDeviceContext(), m_Bitmap->GetIndexCount(), worldMatrix, viewMatrix, orthoMatrix, m_Bitmap->GetTexture());
    if(!result)
    {
        return false;
    }

    // Turn the Z buffer back on now that all 2D rendering has completed.
    m_D3D->TurnZBufferOn();

    // Present the rendered scene to the screen.
    m_D3D->EndScene();

    return true;
}

bool BitmapClass::Render(ID3D11DeviceContext* deviceContext, int positionX, int positionY)
{
    bool result;


    // Re-build the dynamic vertex buffer for rendering to possibly a different location on the screen.
    result = UpdateBuffers(deviceContext, positionX, positionY);
    if(!result)
    {
        return false;
    }

    // Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
    RenderBuffers(deviceContext);

    return true;
}


bool BitmapClass::UpdateBuffers(ID3D11DeviceContext* deviceContext, int positionX, int positionY)
{
    float left, right, top, bottom;
    VertexType* vertices;
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    VertexType* verticesPtr;
    HRESULT result;


    // If the position we are rendering this bitmap to has not changed then don't update the vertex buffer since it
    // currently has the correct parameters.
    if((positionX == m_previousPosX) && (positionY == m_previousPosY))
    {
        return true;
    }

    // If it has changed then update the position it is being rendered to.
    m_previousPosX = positionX;
    m_previousPosY = positionY;

    // Calculate the screen coordinates of the left side of the bitmap.
    left = (float)((m_screenWidth / 2) * -1) + (float)positionX;

    // Calculate the screen coordinates of the right side of the bitmap.
    right = left + (float)m_bitmapWidth;

    // Calculate the screen coordinates of the top of the bitmap.
    top = (float)(m_screenHeight / 2) - (float)positionY;

    // Calculate the screen coordinates of the bottom of the bitmap.
    bottom = top - (float)m_bitmapHeight;

    // Create the vertex array.
    vertices = new VertexType[m_vertexCount];
    if(!vertices)
    {
        return false;
    }

    // Load the vertex array with data.
    // First triangle.
    vertices[0].position = D3DXVECTOR3(left, top, 0.0f);  // Top left.
    vertices[0].texture = D3DXVECTOR2(0.0f, 0.0f);

    vertices[1].position = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
    vertices[1].texture = D3DXVECTOR2(1.0f, 1.0f);

    vertices[2].position = D3DXVECTOR3(left, bottom, 0.0f);  // Bottom left.
    vertices[2].texture = D3DXVECTOR2(0.0f, 1.0f);

    // Second triangle.
    vertices[3].position = D3DXVECTOR3(left, top, 0.0f);  // Top left.
    vertices[3].texture = D3DXVECTOR2(0.0f, 0.0f);

    vertices[4].position = D3DXVECTOR3(right, top, 0.0f);  // Top right.
    vertices[4].texture = D3DXVECTOR2(1.0f, 0.0f);

    vertices[5].position = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
    vertices[5].texture = D3DXVECTOR2(1.0f, 1.0f);

    // Lock the vertex buffer so it can be written to.
    result = deviceContext->Map(m_vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    if(FAILED(result))
    {
        return false;
    }

    // Get a pointer to the data in the vertex buffer.
    verticesPtr = (VertexType*)mappedResource.pData;

    // Copy the data into the vertex buffer.
    memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * m_vertexCount));

    // Unlock the vertex buffer.
    deviceContext->Unmap(m_vertexBuffer, 0);

    // Release the vertex array as it is no longer needed.
    delete [] vertices;
    vertices = 0;

    return true;
}

void BitmapClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
    unsigned int stride;
    unsigned int offset;


    // Set vertex buffer stride and offset.
    stride = sizeof(VertexType); 
    offset = 0;

    // Set the vertex buffer to active in the input assembler so it can be rendered.
    deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);

    // Set the index buffer to active in the input assembler so it can be rendered.
    deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);

    // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    return;
}

如何围绕纹理中心旋转纹理?

不完全是。它比那更微妙。让我先回顾一下转换的基础知识。

在 DirectX 和一般的 3D 图形中,变换通常由 4x4 仿射变换矩阵表示,该矩阵执行三个连续的几何变换:一个从本地 space 到世界 space(世界矩阵) ,一个从世界space到以相机为中心space(视图矩阵),一个从以相机为中心space到"homogenous clip space"(投影矩阵)。你通常会听到这被称为 WVP (World-View-Projection ) 矩阵。

单个变换矩阵是由组成它的每个单独变换矩阵相乘创建的。这个乘法过程 不是 可交换的,这意味着矩阵 AB 与矩阵 BA 不同。在变换的上下文中,AB 应用变换 A,然后变换 B,而 BA 应用变换 B,然后变换 A。如果 A 是关于 Z 的 45 度旋转,而 b 是沿 X 的 3 个单位的平移,AB 将旋转将对象移动 45 度并将其向右放置 3 个单位,而 BA 会将对象向右移动 3 个单位并将其摆动 45 度,就好像它通过一根杆连接到原点一样。下图以图形方式显示了这一点。

现在我们已经了解了基础知识,让我们继续讨论实际代码。

仔细查看,我可以看出您最初对问题的假设既是对的又是错的——您弄清楚了问题是什么,但误解了原因。

第一个主要问题 - 您的几何体完全在世界 space 中指定。如果几何永远不会移动,这很好,但是这个问题的重点是让所述几何移动,所以...

要解决此问题,请尽可能以最简单的方式构造形状:以原点为中心,边长为 1。这会将四个角更改为以下内容,假设 +X 在右边,+Y 在右边向上,+Z 出(进入屏幕):(-0.5, 0.5, 0), (0.5, 0.5, 0), (-0.5, -0.5, 0), (0.5, -0.5, 0)。这些分别代表左上角、右上角、左下角和右下角。

这还允许您在构造函数中创建一次顶点缓冲区,之后无需再次更新它,即使图像的大小在运行时发生变化也是如此。

其次,由于几何的原始规范已经在世界 space 中,我们不需要真实世界的矩阵。现在我们的几何图形是一个围绕局部原点的单位正方形,我们这样做了。为了获得与源位图大小相同的图像,我们制作了一个缩放矩阵,在 X 轴上按 m_bitmapWidth 缩放,在 Y 轴上按 m_bitmapHeight 缩放。然后我们将它乘以绕 Z 轴的旋转矩阵使其旋转,最后乘以平移矩阵将其移动到 positionXpositionY。我们可以重写UpdateBuffers如下:

bool BitmapClass::UpdateBuffers(int positionX, int positionY, float rotationAngle)
{
    D3DXMATRIX scaling, rotation, translation, worldMatrix;
    // If the position we are rendering this bitmap to has not changed,
    // don't update the world matrix since it currently has the correct 
    // parameters.
    if((positionX == m_previousPosX) && (positionY == m_previousPosY))
    {
        return true;
    }
    // If it has changed then update the position it is being rendered to.
    m_previousPosX = positionX;
    m_previousPosY = positionY;

    // scale, rotate, and translate our unit square
    D3DXMatrixScaling(&scaling, m_bitmapWidth, m_bitmapHeight, 1);
    D3DXMatrixRotationZ(&rotation, rotationAngle);
    D3DXMatrixTranslation(&translation, positionX, positionY, 0);

    //Now concatenate all the transformations together,
    D3DXMatrixMultiply(&worldMatrix, &scaling, &rotation);
    D3DXMatrixMultiply(&worldMatrix, &worldMatrix, &translation);

    // And tell D3D this is our new world matrix.
    m_D3D->SetWorldMatrix(worldMatrix);

    return true;
}

最后的编辑是删除 BitmapClass::Render 中对 UpdateBuffers 的调用,并在调用 GetWorldMatrix 之前进行调用。这将确保渲染过程使用正确的变换矩阵。