概述

在上一个教程中,我们从模型空间到屏幕渲染了一个立方体。 在本教程中,我们将扩展转换的概念并演示可以通过这些转换实现的简单动画。

本教程的结果将是围绕另一个轨道运行的对象。 展示转换以及如何将它们组合以实现期望的效果将是有用的。 在我们介绍新概念时,未来的教程将在此基础上构建。

资源目录

(SDK root)\Samples\C++\Direct3D11\Tutorials\Tutorial05

Github

转型

在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转型的更多相关文章

  1. Direct3D 11 Tutorial 1: Basics_Direct3D 11 教程1:基础

    Github-LearnDirectX-DX3D11 tutorial01 概述 在这第一篇教程中,我们将通过介绍创建最小Direct3D应用程序所必需的元素.每一个Direct3D应用程序必需拥有这 ...

  2. Java 11 Tutorial

    Java 11 Tutorial 参考 https://blog.csdn.net/sihai12345/article/details/82889827 原文 https://winterbe.co ...

  3. GTAC 2015将于11月10号和11号召开

    今年的GTAC注册已经结束,将会在11月10号和11号在Google马萨诸塞州剑桥办公室召开.大家可以关注https://developers.google.com/google-test-autom ...

  4. 《zw版·Halcon-delphi系列原创教程》 3d汽车模型自动区域分割

    <zw版·Halcon-delphi系列原创教程> 3d汽车模型自动区域分割 目前,图像分析,在3D设计,机器视觉方面拥有很广.这个Halcon脚本是3d汽车模型自动区域分割,很简单才20 ...

  5. int和integer;Math.round(11.5)和Math.round(-11.5)

    int是java提供的8种原始数据类型之一.Java为每个原始类型提供了封装类,Integer是java为int提供的封装类.int的默认值为0,而Integer的默认值为null,即Integer可 ...

  6. AltiumDesigner14板框定义及3D显示图文教程

    很多人都跟我反应说AD14不能定义板框大小,或者说是不知道怎么定义板框的大小,确实AD14的操作和AD13或者是AD10的版本斗有一些差异,尤其是现在最新的版本AltiumDesigner14.3.1 ...

  7. C++11 并发指南一(C++11 多线程初探)

    引言 C++11 自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些 C++11 的新特性,今后几篇博客我都会写一些关于 C++11 的特性,算是记录一下自己学到的东西吧, ...

  8. [连载]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 ...

  9. 本周MySQL官方verified/open的bug列表(11月15日至11月21日)

    本周MySQL verified的bug列表(11月15日至11月21日) 1. Bug #70923    Replication failure on multi-statement INSERT ...

随机推荐

  1. Linux x86_64 APIC中断路由机制分析

    不同CPU体系间的中断控制器工作原理有较大差异,本文是<Linux mips64r2 PCI中断路由机制分析>的姊妹篇,主要分析Broadwell-DE X86_64 APIC中断路由原理 ...

  2. linux修改文件为可执行文件

    修改shell为可执行文件 chmod +x test2.sh chmod 751 file   给file的属主分配读.写.执行(7)的权限,给file的所在组分配读.执行(5)的权限,给其他用户分 ...

  3. 考前停课集训 Day2 非

    因为太长了 所以一天一天分开发 Day2 昨天晚上没开黑车 没脱衣服就睡了 可能是我难受了…… 新的一天. 早上好. 我没去晨跑,早上先和团长集合了,没看见rkbudlo来 于是就先吃饭了 去机房的时 ...

  4. python网络编程(一)

    socket简介 1.本地的进程间通信(IPC)有很多种方式,例如 队列 同步(互斥锁.条件变量等) 以上通信方式都是在一台机器上不同进程之间的通信方式,那么问题来了 网络中进程之间如何通信? 2. ...

  5. .net 4.0 中的特性总结(一):dynamic

    在新版本的C#中,dynamic关键词是一个很重要的新特性,现在你可以创建动态对象并在运行时再决定它的类型.而且.net 4.0为CLR加入了一组为动态语言服务的运行时环境,称为DLR(Dynamic ...

  6. 并查集---java模板

    并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中.这一类问题近几年来反复出 ...

  7. 180400之pycharm快捷方式汇总

    1.Pycharm中快捷键大全,遇到一个更新一个 撤销与反撤销:Ctrl + z,Ctrl + Shift + z 缩进.不缩进:Tab.Shift + tab 运行:Shift + F10 批量注释 ...

  8. Linux之磁盘分区篇

    作业三: 1)   开启Linux系统前添加一块大小为20G的SCSI硬盘 2)   开启系统,右击桌面,打开终端 3)   为新加的硬盘分区,一个主分区大小为10G,剩余空间给扩展分区,在扩展分区上 ...

  9. golang time打印出的值是62135596800的来源

    ' 减去62135596800是将"以公元1年1月1日0点为基准"改成"以1970年1月1日0点"为基准 所以,数据库datetime的默认值 : 0000-0 ...

  10. 开源GIS浅谈 【转】

    http://blog.csdn.net/happyduoduo1/article/details/51773850 谈到GIS软件,首先让我们想到的是GIS界的龙头大哥ESRI公司旗下的ArcGIS ...