在其中心的 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 轴的旋转矩阵使其旋转,最后乘以平移矩阵将其移动到 positionX
和 positionY
。我们可以重写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
之前进行调用。这将确保渲染过程使用正确的变换矩阵。
我还在学习 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 轴的旋转矩阵使其旋转,最后乘以平移矩阵将其移动到 positionX
和 positionY
。我们可以重写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
之前进行调用。这将确保渲染过程使用正确的变换矩阵。