In the previous tutorial, we rendered a cube from the model space to the screen. In this tutorial, we will extend the conversion concept and demonstrate simple animations that can be implemented through these conversions.
The results of this tutorial will be objects running around another track. It is useful to demonstrate conversions and how they are combined to achieve the desired effect. When we introduce the new concept, future tutorials will be built on this basis.
(SDK root) \ samples \ c ++ \ direct3d11 \ tutorials \ tutorial05
In 3D graphics, transformations are usually used to operate on top points and vectors. It is also used to convert them into another space in one space. Perform the transformation by multiplying the matrix. There are usually three types of original transformations that can be executed on the vertex: Translation (relative to the origin in space), rotation (relative to the direction of X, Y, Z frames) and zoom (distance origin ). In addition, the projection transformation is used from the view space to the projection space. The xNa Math Library contains APIs that allow you to easily construct matrices for a variety of purposes, such as translation, rotation, scaling, world-to-view conversion, and view-to-projection conversion. Then, applications can use these matrices to convert vertices in their scenarios. A basic understanding of matrix transformation is required. We will briefly introduce some examples below.
Translation refers to moving or moving a certain distance in space. In 3D, the matrix used for translation has a form.
1 0 0 0 0 1 0 0 0 0 1 0 a b c 1
(A, B, C) is the vector that defines the moving direction and distance. For example, to move the vertex-5 units along the X axis (negative X direction), we can multiply it by this matrix:
1 0 0 0 0 1 0 0 0 0 1 0 -5 0 0 1
If we apply this to a cube object centered on the origin, the result is that the box moves five units to the negative X axis, as shown in figure 5, after applying the translation.
Figure 1. Impact of Translation
In 3D, space is usually defined by three unique axes from the origin point and from the origin point: X, Y, and Z. in computer graphics, multiple spaces are usually used: object space, World Space, view space, projection space, and screen space.
Figure 2. cube defined in Object Space
Rotation refers to rotating the vertex around the axis that passes through the origin. These three axes are the X, Y, and Z axes in the space. The example in 2D is to rotate the vector [1 0] 90 degrees counterclockwise. The rotation result is a vector [0 1]. The matrix used to rotate 1 degree clockwise around the Y axis looks like this:
cos? 0 -sin? 0 0 1 0 0 sin? 0 cos? 0 0 0 0 1
Figure 6 shows the effect of rotating a cube 45 degrees centered around the Y axis.
Figure 3. Effect of rotation around Y axis
Scaling refers to enlarging or downgrading the vector component along the axis. For example, a vector can be scaled up in proportion in all directions or scaled down only along the X axis. To expand, we usually apply the scaling matrix below:
p 0 0 0 0 q 0 0 0 0 r 0 0 0 0 1
P, Q, and R are proportional factors along the X, Y, and Z directions, respectively. 2 zooming along the X axis and 0.5 zooming along the Y axis is displayed.
Figure 4. Scaling Effect
To apply multiple transformations to a vector, we can simply multiply the vector by the first transformation matrix, then multiply the obtained vector by the second transformation matrix, and so on. Because vector and matrix multiplication are associated, we can also multiply all matrices first, and then multiply the vectors by the product matrix to get the same result. Shows how the cube ends if we combine rotation and Heping transformation.
Figure 5. Effects of rotating and moving
In this tutorial, We will convert two cubes. The first one will be rotated in place, and the second one will be rotated around the first one, and at the same time on its own axis. The two cubes will have their own world transformation matrix associated with them, and the matrix will be re-applied to the matrix in each rendered frame.
There are some functions in xNa math to help create a rotation, translation, and scaling matrix.
- The xmmatrixrotationx, xmmatrixrotationy, and xmmatrixrotationz functions are used to rotate the X, Y, and Z axes respectively. They create a basic rotation matrix that rotates around one of the main axes. Complex rotation around other axes can be done by multiplying several of them.
- You can call the xmmatrixtranslation function to perform the conversion. This function creates a matrix for converting the points specified by the parameter.
- Use xmmatrixscaling to complete scaling. It scales only along the main axis. If you need to scale along any axis, You can multiply the zoom matrix with the appropriate rotation matrix to achieve this effect.
The first cube will rotate in place and serve as the center of the orbit. The Cube is rotated along the Y axis and applied to the world matrix. This is shown by calling the following code:XmmatrixrotationyFunction. A cube rotates a certain amount per frame. Since the cube is assumed to be a continuous rotation, the value of the rotation matrix increases with each frame.
// 1st Cube: Rotate around the origin g_World1 = XMMatrixRotationY( t );
The first cube will rotate in place and serve as the center of the orbit. The Cube is rotated along the Y axis and applied to the world matrix. This is done by calling the xmmatrixrotationy function shown in the following code. A cube rotates a certain amount per frame. Since the cube is assumed to be a continuous rotation, the value of the rotation matrix increases with each frame.
// 2nd Cube: Rotate around origin XMMATRIX mSpin = XMMatrixRotationZ( -t ); XMMATRIX mOrbit = XMMatrixRotationY( -t * 2.0f ); XMMATRIX mTranslate = XMMatrixTranslation( -4.0f, 0.0f, 0.0f ); XMMATRIX mScale = XMMatrixScaling( 0.3f, 0.3f, 0.3f ); g_World2 = mScale * mSpin * mTranslate * mOrbit;
Note that these operations are not interchangeable. The order of application conversion is very important. Test the conversion sequence and observe the results.
Since all the transformation functions will create a new matrix based on the parameters, their rotation volume must increase progressively. This is done by updating the "time" variable.
// Update our time t += XM_PI * 0.0125f;
The constant buffer must be updated for the shader before rendering calls. Note that the world matrix is unique for each cube, so every object passed to it is changed.
// // Update variables for the first cube // ConstantBuffer cb1; cb1.mWorld = XMMatrixTranspose( g_World1 ); cb1.mView = XMMatrixTranspose( g_View ); cb1.mProjection = XMMatrixTranspose( g_Projection ); g_pImmediateContext->UpdateSubresource( g_pConstantBuffer, 0, NULL, &cb1, 0, 0 ); // // Render the first cube // g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 ); g_pImmediateContext->VSSetConstantBuffers( 0, 1, &g_pConstantBuffer ); g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 ); g_pImmediateContext->DrawIndexed( 36, 0, 0 ); // // Update variables for the second cube // ConstantBuffer cb2; cb2.mWorld = XMMatrixTranspose( g_World2 ); cb2.mView = XMMatrixTranspose( g_View ); cb2.mProjection = XMMatrixTranspose( g_Projection ); g_pImmediateContext->UpdateSubresource( g_pConstantBuffer, 0, NULL, &cb2, 0, 0 ); // // Render the second cube // g_pImmediateContext->DrawIndexed( 36, 0, 0 );
This tutorial also provides an important addition, that is, the depth buffer. Without it, the smaller orbital cube will still be drawn at the top of the larger Central cube when surrounding the back of the latter. The depth buffer allows direct3d to track the depth of each pixel drawn to the screen. The default behavior of the depth buffer in direct3d 11 is to check the values stored in the depth buffer for each pixel drawn on the screen and the pixel in the screen space. If the depth of the rendered pixel is smaller than or equal to the existing value in the depth buffer, draw the pixel and update the value in the depth buffer to the depth of the newly drawn pixel. On the other hand, if the depth of the pixel being drawn is greater than the existing value in the depth buffer, the pixel is discarded and the depth value in the depth buffer remains unchanged.
The following code in the example creates a deep buffer (depthstencel texture ). It also createsDepthstencilviewSo that direct3d 11 knows to use it as a deep template texture.
// Create depth stencil texture D3D11_TEXTURE2D_DESC descDepth; ZeroMemory( &descDepth, sizeof(descDepth) ); descDepth.Width = width; descDepth.Height = height; descDepth.MipLevels = 1; descDepth.ArraySize = 1; descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; descDepth.SampleDesc.Count = 1; descDepth.SampleDesc.Quality = 0; descDepth.Usage = D3D11_USAGE_DEFAULT; descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL; descDepth.CPUAccessFlags = 0; descDepth.MiscFlags = 0; hr = g_pd3dDevice->CreateTexture2D( &descDepth, NULL, &g_pDepthStencil ); if( FAILED(hr) ) return hr; // Create the depth stencil view D3D11_DEPTH_STENCIL_VIEW_DESC descDSV; ZeroMemory( &descDSV, sizeof(descDSV) ); descDSV.Format = descDepth.Format; descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; descDSV.Texture2D.MipSlice = 0; hr = g_pd3dDevice->CreateDepthStencilView( g_pDepthStencil, &descDSV, &g_pDepthStencilView ); if( FAILED(hr) ) return hr;
To use this newly created deep template buffer, the tutorial must bind it to the device. This is done by passing the depth template View to the third parameter of the omsetrendertargets function.
g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, g_pDepthStencilView );
Like the rendering target, we must also clear the depth buffer before rendering. This ensures that the depth value of the previous frame does not mistakenly discard the pixels in the current frame. In the following code, the tutorial sets the depth buffer to the maximum number (1.0 ).
// // Clear the depth buffer to 1.0 (max depth) //
Direct3d 11 tutorial 5: 3D transformation_direct3d 11 tutorial 5: 3D Transformation