概述

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

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

资源目录

(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. 统一各浏览器CSS 样式——CSS Reset

    html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, ...

  2. [CF961E] Tufurama

    Description: 有一天Polycarp决定重看他最喜爱的电视剧<Tufurama>.当他搜索"在线全高清免费观看Tufurama第3季第7集"却只得到第7季第 ...

  3. [HNOI2017/AHOI2017]影魔

    Description: 奈文摩尔有 \(n\) 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 \(1\) 到 \(n\).第 \(i\) 个灵魂的战斗力为 \(k_i\),灵魂们以点对的形 ...

  4. elment ui 图片上传遇到的一些问题

    图片上传返回200,message显示请上传图片 注意上图中的name字段要和服务器接受的name相同,这里我们是imgfile,默认name不是这个,所以要在el-upload组件上设置name属性 ...

  5. python之模块4

    1 模块与包 1.1 模块的定义 什么是模块 模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 为什么使用模块 在退出python解释器然后重新进入,之前定义的函数 ...

  6. sencha touch 选择器

    1 DOM元素选择器 Ext.DomQuery操作标准DOM元素 Ext.query(selector, [root]) : HTMLElement[] // 调用Ext.dom.Query.sele ...

  7. bootstrap学习总结

    bootstrap网站下载: 谷歌浏览器访问:http://github.com/twbs/bootstrap/    右上角(clone or download) 编译版bootstrap:http ...

  8. 用GO开发企业级分布式云存储系统

    一.基础架构 二.开发工具

  9. 国内阿里maven仓库镜像maven配置文件maven仓库速度快

    国内连接maven官方的仓库更新依赖库,网速一般很慢,收集一些国内快速的maven仓库镜像以备用. 最新更新:2016年11月11日 18:05:40 阿里云提供Maven私服,我把配置文件贴一下,自 ...

  10. shell编程学习笔记(十二):Shell中的break/continue跳出循环

    在循环遍历中,可以添加对应判断条件跳出循环,跳出循环可以使用break/continue,这个跟java语言是一样的,break是指跳出整个循环,continue是指跳出当前循环体,继续下一项循环. ...