Direct3D 11 Tutorial 5: 3D Transformation_Direct3D 11 教程5:3D转型
概述
在上一个教程中,我们从模型空间到屏幕渲染了一个立方体。 在本教程中,我们将扩展转换的概念并演示可以通过这些转换实现的简单动画。
本教程的结果将是围绕另一个轨道运行的对象。 展示转换以及如何将它们组合以实现期望的效果将是有用的。 在我们介绍新概念时,未来的教程将在此基础上构建。
资源目录
(SDK root)\Samples\C++\Direct3D11\Tutorials\Tutorial05
转型
在3D图形中,变换通常用于对顶点和矢量进行操作。 它还用于将它们在一个空间中转换为另一个空间。 通过与矩阵相乘来执行变换。 通常有三种类型的原始变换可以在顶点上执行:平移(相对于原点位于空间中),旋转(相对于x,y,z帧的方向)和缩放(距离 起源)。 除此之外,投影变换用于从视图空间到投影空间。 XNA Math库包含的API可以方便地构建矩阵,用于多种用途,例如平移,旋转,缩放,世界到视图转换,视图到投影转换等。 然后,应用程序可以使用这些矩阵来转换其场景中的顶点。 需要对矩阵变换有基本的了解。 我们将简要介绍下面的一些示例。
平移
平移是指在空间中移动或移位一定距离。 在3D中,用于翻译的矩阵具有形式。
1 0 0 0
0 1 0 0
0 0 1 0
a b c 1
其中(a,b,c)是定义移动方向和距离的向量。 例如,要沿X轴(负X方向)移动顶点-5单位,我们可以将其与此矩阵相乘:
1 0 0 0
0 1 0 0
0 0 1 0
-5 0 0 1
如果我们将此应用于以原点为中心的立方体对象,则结果是该框向负X轴移动5个单位,如图5所示,在应用平移之后。
图1.平移的影响

在3D中,空间通常由原点和来自原点的三个唯一轴定义:X,Y和Z.计算机图形中通常使用多个空间:对象空间,世界空间,视图空间,投影空间和屏幕空间。
图2.在对象空间中定义的立方体

旋转
旋转是指围绕穿过原点的轴旋转顶点。 三个这样的轴是空间中的X,Y和Z轴。 2D中的示例是逆时针旋转矢量[1 0] 90度。 旋转的结果是向量[0 1]。 用于围绕Y轴顺时针旋转1度的矩阵看起来像这样:
cosΐ 0 -sinΐ 0
0 1 0 0
sinΐ 0 cosΐ 0
0 0 0 1
图6显示了围绕Y轴旋转以原点为中心45度的立方体的效果。
图3.围绕Y轴旋转的效果

缩放
缩放是指沿轴方向放大或缩小矢量分量的大小。 例如,矢量可以沿所有方向按比例放大或仅沿X轴按比例缩小。 为了扩展,我们通常在下面应用缩放矩阵:
p 0 0 0
0 q 0 0
0 0 r 0
0 0 0 1
其中p,q和r分别是沿X,Y和Z方向的比例因子。 下图显示了沿X轴缩放2并沿Y轴缩放0.5的效果。
图4.缩放的效果

多重转换
要将多个变换应用于矢量,我们可以简单地将矢量乘以第一个变换矩阵,然后将得到的矢量乘以第二个变换矩阵,依此类推。 因为向量和矩阵乘法是关联的,我们也可以先将所有矩阵相乘,然后将向量乘以乘积矩阵,得到相同的结果。 下图显示了如果我们将旋转和平移转换结合在一起,立方体将如何结束。
图5.旋转和平移的效果

创建轨道
在本教程中,我们将转换两个多维数据集。 第一个将旋转到位,而第二个将围绕第一个旋转,同时在其自己的轴上旋转。 这两个立方体将具有与其关联的自己的世界变换矩阵,并且该矩阵将在渲染的每个帧中重新应用于该矩阵。
XNA Math中有一些函数可以帮助创建旋转,平移和缩放矩阵。
- 围绕X,Y和Z轴执行的旋转分别使用函数XMMatrixRotationX,XMMatrixRotationY和XMMatrixRotationZ来完成。 它们创建围绕主轴之一旋转的基本旋转矩阵。 围绕其他轴的复杂旋转可以通过将它们中的几个相乘来完成。
- 可以通过调用XMMatrixTranslation函数来执行转换。 此函数将创建一个矩阵,用于转换参数指定的点。
- 使用XMMatrixScaling完成缩放。 它仅沿主轴缩放。 如果需要沿任意轴缩放,则可以将缩放矩阵与适当的旋转矩阵相乘以实现该效果。
第一个立方体将旋转到位,并作为轨道的中心。 立方体沿Y轴旋转,应用于相关的世界矩阵。 这是通过调用以下代码中显示的XMMatrixRotationY函数来完成的。 立方体每帧旋转一定量。 由于立方体被假设为连续旋转,因此旋转矩阵所基于的值随每帧递增。
// 1st Cube: Rotate around the origin
g_World1 = XMMatrixRotationY( t );
第一个立方体将旋转到位,并作为轨道的中心。 立方体沿Y轴旋转,应用于相关的世界矩阵。 这是通过调用以下代码中显示的XMMatrixRotationY函数来完成的。 立方体每帧旋转一定量。 由于立方体被假设为连续旋转,因此旋转矩阵所基于的值随每帧递增。
// 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;
需要注意的一点是,这些操作不是可交换的。 应用转换的顺序很重要。 试验转化顺序并观察结果。
由于所有变换函数都将根据参数创建新矩阵,因此它们旋转的量必须递增。 这是通过更新“时间”变量来完成的。
// Update our time
t += XM_PI * 0.0125f;
在进行渲染调用之前,必须为着色器更新常量缓冲区。 请注意,世界矩阵对于每个多维数据集都是唯一的,因此会为每个传递给它的对象进行更改。
//
// 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 );
深度缓冲区
本教程还有另外一个重要的补充,那就是深度缓冲区。 没有它,较小的轨道立方体在围绕后者的后部时仍会被绘制在较大的中心立方体的顶部。 深度缓冲区允许Direct3D跟踪绘制到屏幕的每个像素的深度。 Direct3D 11中深度缓冲区的默认行为是检查屏幕上绘制的每个像素与屏幕空间像素的深度缓冲区中存储的值。 如果正在渲染的像素的深度小于或等于深度缓冲器中已经存在的值,则绘制像素并且将深度缓冲器中的值更新为新绘制的像素的深度。 另一方面,如果正在绘制的像素的深度大于深度缓冲器中已经存在的值,则丢弃该像素并且深度缓冲器中的深度值保持不变。
示例中的以下代码创建深度缓冲区(DepthStencil纹理)。 它还创建深度缓冲区的DepthStencilView,以便Direct3D 11知道将其用作深度模板纹理。
// 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;
为了使用这个新创建的深度模板缓冲区,教程必须将其绑定到设备。 这是通过将深度模板视图传递给OMSetRenderTargets函数的第三个参数来完成的。
g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, g_pDepthStencilView );
与渲染目标一样,我们还必须在渲染之前清除深度缓冲区。 这可确保先前帧的深度值不会错误地丢弃当前帧中的像素。 在下面的代码中,教程实际上是将深度缓冲区设置为最大量(1.0)。
//
// Clear the depth buffer to 1.0 (max depth)
//
最终效果

Direct3D 11 Tutorial 5: 3D Transformation_Direct3D 11 教程5:3D转型的更多相关文章
- Direct3D 11 Tutorial 1: Basics_Direct3D 11 教程1:基础
Github-LearnDirectX-DX3D11 tutorial01 概述 在这第一篇教程中,我们将通过介绍创建最小Direct3D应用程序所必需的元素.每一个Direct3D应用程序必需拥有这 ...
- Java 11 Tutorial
Java 11 Tutorial 参考 https://blog.csdn.net/sihai12345/article/details/82889827 原文 https://winterbe.co ...
- GTAC 2015将于11月10号和11号召开
今年的GTAC注册已经结束,将会在11月10号和11号在Google马萨诸塞州剑桥办公室召开.大家可以关注https://developers.google.com/google-test-autom ...
- 《zw版·Halcon-delphi系列原创教程》 3d汽车模型自动区域分割
<zw版·Halcon-delphi系列原创教程> 3d汽车模型自动区域分割 目前,图像分析,在3D设计,机器视觉方面拥有很广.这个Halcon脚本是3d汽车模型自动区域分割,很简单才20 ...
- int和integer;Math.round(11.5)和Math.round(-11.5)
int是java提供的8种原始数据类型之一.Java为每个原始类型提供了封装类,Integer是java为int提供的封装类.int的默认值为0,而Integer的默认值为null,即Integer可 ...
- AltiumDesigner14板框定义及3D显示图文教程
很多人都跟我反应说AD14不能定义板框大小,或者说是不知道怎么定义板框的大小,确实AD14的操作和AD13或者是AD10的版本斗有一些差异,尤其是现在最新的版本AltiumDesigner14.3.1 ...
- C++11 并发指南一(C++11 多线程初探)
引言 C++11 自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些 C++11 的新特性,今后几篇博客我都会写一些关于 C++11 的特性,算是记录一下自己学到的东西吧, ...
- [连载]Tutorial series: learning how to write a 3D soft engine from scratch in C#, TypeScript or JavaScript[英]
MSDN中的一篇博文链接:Tutorial series: learning how to write a 3D soft engine from scratch in C#, TypeScript ...
- 本周MySQL官方verified/open的bug列表(11月15日至11月21日)
本周MySQL verified的bug列表(11月15日至11月21日) 1. Bug #70923 Replication failure on multi-statement INSERT ...
随机推荐
- setdest 和cbrgen工具的使用,出现的错误
在路径 ~/ns-2.34/indep-utils/cmu-scen-gen/setdest下运行 ./setdest -n 250 -p 0.0 -M 10.0 -t 10 -x 1500 -y 1 ...
- leetCode中老出现的针对一个int中每个数字的处理
一个int比如322,我想找happy number就得3平方加2平方再加2平方,怎样找到一个一个的数字,就是322%10,得到2,然后/10,然后再%,就可以依次求得每位上的数字 happy num ...
- 使用git(window)在github上存项目教程
要托管到github,那你就应该要有一个属于你自己的github帐号,所以你应该先到github.com注册 打开浏览器 在地址栏输入地址:github.com 填写用户名.邮箱.密码 点击Sign ...
- ES6 模板字面量
模板字面量 解决的问题 1.多行字符串 一个正式的多行字符串的概念 2.基本的字符串格式化 将变量的值嵌入字符串的能力 3.HTML转义 向HTML插入安全转换后的字符串的能力 (1)基础语法 相当于 ...
- JS 模仿块级作用域
function outputNumbers(count) { for (var i=0; i<count; i++) { console.log(i); } var i; // 重新声明变量 ...
- UltraEdit使用(工具类似于notepad++)
打开多个文件,在多个文件中切换,鼠标点/ Ctrl+Tab自动换行的设置: 高级-->配置-->编辑器-->自动换行,制表符设置-->默认为每个文件启用自动换行 去掉自动备份设 ...
- 删除office拥有多个都需要激活的授权信息
首先确认office目录下存在“ospp.vbs”文件,可以搜索确认文件路径. 我的是在C:\Program Files\Microsoft Office\Office16 然后以管理员身份打开cm ...
- Python中将array类型不按科学计数法存在文件中的方法
直接上代码: from numpy import *import numpy as npDrug_array = zeros((708,708),dtype = int)f = open('D:\ma ...
- C/JS_实现选择排序
1.js var arr = prompt("请输入一个数组(以“,”隔开):").split(",").map(function(data){ return ...
- Ajax发送请求,并接受字符串
前台: $.ajax({ type: 'POST', url: url, dataType: "json", success : function(data){ alert(&qu ...