什么是多视图

一般的3D程序都只有一个视图,对应整个窗口的客户区。多视图就是在一个窗口中放置多个视图,以便从不同的角度观察模型或者场景。很多图形软件都有这个功能,比如大家熟知的3DMax就有四个视图,分别是前视图,左视图,顶视图和透视图。还有一些游戏引擎也有类似的Demo,比如irrlicht引擎中的SplitScreen就是用多视图实现的,如下图。

什么是视口(viewport)?

用DirectX实现多视图有几种方法,可以使用多个Viewport,也可以使用多个Swap Chain,后者实现起来比较复杂,以后再做介绍,先看如何使用多个viewport来实现。那么到底什么是viewport呢?举个现实中的例子,假设你站在一个密封的房子里,这个房子只有一个很小的窗口,你现在就站在窗口前面,通过这个窗口你可以观察到外面的世界,那么这个窗口就相当于一个视口,而外面的世界就是3D中的场景。视口有以下几个属性,长度和宽度,为了确定窗口的位置,我们还需要一个左上角坐标。为了支持Z-Buffer,还需要两个深度值,分别是zMin, zMax,表示最小深度和最大深度。好了,这就是视口的定义。在D3D中,视口用下面的结构体来表示,X和Y表示视口的左上角坐标,Width和Height表示窗口的宽度和高度,MinZ和MaxZ表示Z-buffer的最小值和最大值。

typedef struct D3DVIEWPORT9 {
DWORD X;
DWORD Y;
DWORD Width;
DWORD Height;
float MinZ;
float MaxZ;
} D3DVIEWPORT9, *LPD3DVIEWPORT9;

实现多个视口渲染需要以下几个步骤。

  • 创建主窗口
  • 将主窗口分割为四个区域
  • 设置每个区域对应的视口并渲染

创建主窗口

WNDCLASSEX winClass ;

winClass.lpszClassName = "MultiViewports";
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = MsgProc;
winClass.hInstance = hInstance;
winClass.hIcon = NULL ;
winClass.hIconSm = NULL ;
winClass.hCursor = LoadCursor(NULL, IDC_ARROW) ;
winClass.hbrBackground = NULL ;
winClass.lpszMenuName = NULL ;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0; RegisterClassEx (&winClass) ; HWND hWnd = CreateWindowEx(NULL,
winClass.lpszClassName, // window class name
"MultiViewports", // window caption
WS_OVERLAPPEDWINDOW, // window style
32, // initial x position
32, // initial y position
600, // initial window width
600, // initial window height
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters // Create window failed
if(hWnd == NULL)
{
MessageBoxA(hWnd, "Create Window failed!", "Error", 0) ;
return -1 ;
}

分割主窗口

首先通过GetClientRect函数获得窗口的尺寸,然后将其横竖一分为二,这样整个窗口被分割为四部分,分别对应四个视口区域。如下图。这一步并没有实际的代码对应,而是在创建视口的时候完成的。如果窗口的左上角坐标是(x, y), 长宽分别是width和height,那么对应的四个视口分别是

viewport1 = {0, 0, width / 2, height / 2, 0.0f, 1.0f} ;
viewport2 = {width / 2, 0, width / 2, height / 2, 0.0f, 1.0f} ;
viewport3 = {0, height / 2, width / 2, height / 2, 0.0f, 1.0f} ;
viewport4 = {width / 2, height / 2, width / 2, height / 2, 0.0f, 1.0f} ;

设置视口并渲染

视口定义好以后,使用SetViewport函数进行设置,然后就可以绘制视口对应的场景了。由于一共需要设置四个视口,为了避免代码重复,这里设置一个Draw函数,用来绘制每个视口中的场景,该函数有两个参数,第一个是待绘制的视口,第二个是视口的背景颜色。每设置一个视口,就调用这个函数一次。

void Draw(D3DVIEWPORT9* viewport, DWORD color)
{
g_pd3dDevice->SetViewport(viewport) ;
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, color, 1.0f, 0 ); // Begin the scene
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// Draw teapot
g_pTeapotMesh->DrawSubset(0) ; // End the scene
g_pd3dDevice->EndScene();
}
}

在Draw函数内部,使用D3D函数SetViewport来设置viewport,设置完成以后要立即绘制该视口对应的场景,倘若一次性设置四个视口,然后在绘制每个视口对应的场景,那么后面的场景就会覆盖前面的,无法达到预期效果,所以正确的顺序是

设置视口1

绘制视口1的场景

设置视口2

绘制视口2的场景

设置视口3

绘制视口3的场景

设置视口4

绘制视口4的场景

而下面这样则是不对的

设置视口1

设置视口2

设置视口3

设置视口4

绘制视口1的场景

绘制视口2的场景

绘制视口3的场景

绘制视口4的场景

注意Present函数每个frame调用一次即可,而不是每次设置viewport都调用,那样的话屏幕会闪烁。为了分别从不同角度观察模型,可以为每个视口单独设置camera,分别对应前视图,左视图,顶视图及透视图。

1. 设置前视图

// Setup camera, front view
D3DXVECTOR3 eyePt(0.0f, 0.0f, -5.0f) ;
D3DXVECTOR3 lookAt(0.0f, 0.0f, 0.0f) ;
D3DXVECTOR3 upVec(0.0f, 1.0f, 0.0f) ;
SetupCamera(&eyePt, &lookAt, &upVec) ;
// Draw top-left viewport
D3DVIEWPORT9 viewport1 = {0, 0, vpWidth, vpHeight, 0.0f, 1.0f} ;
Draw(&viewport1, 0xffff0000) ;

2. 设置左视图

// Setup camera, left view
eyePt = D3DXVECTOR3(-5.0f, 0.0f, 0.0f) ;
SetupCamera(&eyePt, &lookAt, &upVec) ;

// Draw top-right viewport
D3DVIEWPORT9 viewport2 = {vpWidth, 0, vpWidth, vpHeight, 0.0f, 1.0f} ;
Draw(&viewport2, 0xff00ff00) ;

3. 设置顶视图

// Setup camera, top view
eyePt = D3DXVECTOR3(0.0f, 5.0f, 0.0f) ;
upVec = D3DXVECTOR3(0.0f, 0.0f, 1.0f) ;
SetupCamera(&eyePt, &lookAt, &upVec) ;

// Draw bottom-left viewport
D3DVIEWPORT9 viewport3 = {0, vpHeight, vpWidth, vpHeight, 0.0f, 1.0f} ;
Draw(&viewport3, 0xff0000ff) ;

4. 设置透视图

// Setup camera, perspective view
eyePt = D3DXVECTOR3(-3.0f, 3.0f, -3.0f) ;
upVec = D3DXVECTOR3(1.0f, 2.0f, 1.0f) ;
SetupCamera(&eyePt, &lookAt, &upVec) ;

// Draw bottom-right viewport
D3DVIEWPORT9 viewport4 = {vpWidth, vpHeight, vpWidth, vpHeight, 0.0f, 1.0f} ;
Draw(&viewport4, 0xffffff00) ;

渲染

// Present the back-buffer contents to the display
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

好了,看一下效果图

用DirectX实现多视图渲染的更多相关文章

  1. Rails :布局和视图渲染

    原文地址: http://guides.ruby-china.org/layouts_and_rendering.html Rails 布局和视图渲染 本文介绍 Action Controller 和 ...

  2. angularjs如何在视图渲染结束之后,或者render之后执行指令中的link方法呢?

    angularjs如何在视图渲染结束之后,或者render之后执行指令中的link方法 关键字: $timeout app.directive("myDirective",func ...

  3. SpringMVC核心——视图渲染(包含视图解析)问题

    一.本来想说的是返回值处理问题,但在 SpringMVC 中,返回值处理问题的核心就是视图渲染.所以这里标题叫视图渲染问题. 本来想在上一篇文章中对视图解析进行说明的,但是通过源码发现,它应该算到视图 ...

  4. spring mvc DispatcherServlet详解之四---视图渲染过程

    整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的最后一步:视图渲染.视图渲染的过程是在获取到ModelAndView后的过程 ...

  5. 为什么你应该抛弃Express的视图渲染引擎

    Nodejs Express框架的一个被人们广为使用的特性是它的渲染引擎.Express视图渲染引擎允许Controller提供一个视图名称和视图模型对象给Express,然后返回由HTTP响应流输出 ...

  6. 微信小程序教学第四章第二节(含视频):小程序中级实战教程:详情-视图渲染

    § 详情 - 数据渲染 本文配套视频地址: https://v.qq.com/x/page/x055550lrvd.html 开始前请把 ch4-2 分支中的 code/ 目录导入微信开发工具 这一节 ...

  7. thinkphp3.2笔记(3)视图渲染 模板的赋值与显示 系统变量

    一  视图 1  视图渲染 渲染模板输出最常用的是使用display方法,调用格式:display('[模板文件]'[,'字符编码'][,'输出类型'])模板文件的写法支持下面几种:用法 描述不带任何 ...

  8. ZendFramework-2.4 源代码 - 关于MVC - View层 - 视图渲染器、视图插件管理器

    <?php // 1. 视图渲染器 class PhpRenderer implements Renderer, TreeRendererInterface { /** * 插件管理器 */ p ...

  9. Vue视图渲染原理解析,从构建VNode到生成真实节点树

    前言 在 Vue 核心中除了响应式原理外,视图渲染也是重中之重.我们都知道每次更新数据,都会走视图渲染的逻辑,而这当中牵扯的逻辑也是十分繁琐. 本文主要解析的是初始化视图渲染流程,你将会了解到从挂载组 ...

随机推荐

  1. 基于nopcommerce b2c开源项目的精简版开发框架Nop.Framework

    http://www.17ky.net/soft/70612.html?v=1#0-sqq-1-39009-9737f6f9e09dfaf5d3fd14d775bfee85 项目详细介绍 该开源项目是 ...

  2. 【AtCoder】AGC032

    AGC032 A - Limited Insertion 这题就是从后面找一个最靠后而且当前可以放的,可以放的条件是它的前面正好放了它的数值-1个数 如果不符合条件就退出 #include <b ...

  3. sparkStreaming消费kafka-0.8方式:direct方式(存储offset到zookeeper)

    生产中,为了保证kafka的offset的安全性,并且防止丢失数据现象,会手动维护偏移量(offset) 版本:kafka:0.8 其中需要注意的点: 1:获取zookeeper记录的分区偏移量 2: ...

  4. php图文合成文字居中(png图片合成)

    header('Content-type:text/html;charset=utf-8'); /** * png图文合成 by wangzhaobo * @param string $pic_pat ...

  5. Codechef CHSIGN Change the Signs(May Challenge 2018) 动态规划

    原文链接http://www.cnblogs.com/zhouzhendong/p/9004583.html 题目传送门 - Codechef CHSIGN 题意 第一行,一个数$T$,表示数据组数. ...

  6. 如何用 Python 模糊搜索文件

    一.我的文件在哪里? 1.告诉计算机文件在哪 使用路径描述位置 绝对路径——从根目录写到底 内置模块OS 路径 目录 文件 其他系统操作 2.描述文件的特征 用条件判断来筛选 3.对比后打印文件名 用 ...

  7. 使用loadrunner录制脚本的思路和注意要点

    基本思路如下图: 注意要点有如下几点: 1.性能测试往往需要准备大批量的数据,大批量数据的生成方法有很多种,常见的有: (1)编写SQL语句来插入数据 (2)使用DataFactory等专业的数据生成 ...

  8. dubbo spring bean id冲突

    service-security-provider应用有provider和consumer配置文件 其中secutrity-consumer引用两个服务 <dubbo:reference int ...

  9. UVA 814 The Letter Carrier's Rounds

    大致翻译: 对于电子邮件应用程序,您需要描述发生在成对mta之间的基于smtp的通信.发送方 的用户代理向发送消息传输代理(MTA)提供格式化的消息.发送MTA使用SimpleMail 传输协议(SM ...

  10. 大数据环境完全分布式搭建hbase-0.96.2-hadoop2

    1.上传hbase安装包 2.解压 3.配置hbase集群,要修改3个文件 (首先zookeeper集群已经安装好了 并且启动 hadoop启动) 注意:要把hadoop的hdfs-site.xml和 ...