开发者社区> 吞吞吐吐的> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

用DirectX实现多视图渲染

简介:
+关注继续查看

什么是多视图

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

2011032520215979.png

什么是视口(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 = {00, width / 2, height / 20.0f1.0f} ;
viewport2 
= {width / 20, width / 2, height / 20.0f1.0f} ;
viewport3 
= {0, height / 2, width / 2, height / 20.0f1.0f} ;
viewport4 
= {width / 2, height / 2, width / 2, height / 20.0f1.0f} ;

2011032519084724.png

设置视口并渲染

视口定义好以后,使用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.0f0.0f-5.0f) ;
D3DXVECTOR3 lookAt(
0.0f0.0f0.0f) ;
D3DXVECTOR3 upVec(
0.0f1.0f0.0f) ;
SetupCamera(
&eyePt, &lookAt, &upVec) ;
// Draw top-left viewport
D3DVIEWPORT9 viewport1 = {00, vpWidth, vpHeight, 0.0f1.0f} ;
Draw(
&viewport1, 0xffff0000) ;
复制代码

2. 设置左视图

复制代码
// Setup camera, left view
eyePt = D3DXVECTOR3(-5.0f0.0f0.0f) ;
SetupCamera(
&eyePt, &lookAt, &upVec) ;

// Draw top-right viewport
D3DVIEWPORT9 viewport2 = {vpWidth, 0, vpWidth, vpHeight, 0.0f1.0f} ;
Draw(
&viewport2, 0xff00ff00) ;
复制代码

3. 设置顶视图

复制代码
// Setup camera, top view
eyePt = D3DXVECTOR3(0.0f5.0f0.0f) ;
upVec 
= D3DXVECTOR3(0.0f0.0f1.0f) ;
SetupCamera(
&eyePt, &lookAt, &upVec) ;

// Draw bottom-left viewport
D3DVIEWPORT9 viewport3 = {0, vpHeight, vpWidth, vpHeight, 0.0f1.0f} ;
Draw(
&viewport3, 0xff0000ff) ;
复制代码

4. 设置透视图

复制代码
// Setup camera, perspective view
eyePt = D3DXVECTOR3(-3.0f3.0f-3.0f) ;
upVec 
= D3DXVECTOR3(1.0f2.0f1.0f) ;
SetupCamera(
&eyePt, &lookAt, &upVec) ;

// Draw bottom-right viewport
D3DVIEWPORT9 viewport4 = {vpWidth, vpHeight, vpWidth, vpHeight, 0.0f1.0f} ;
Draw(
&viewport4, 0xffffff00) ;
复制代码

渲染

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

好了,看一下效果图

2011032422202486.png

== THE END ==

Happy Coding!!!


本文转自zdd博客园博客,原文链接:http://www.cnblogs.com/graphics/archive/2011/03/26/1994366.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
构建Docker镜像指南,含实战案例
本文是博主学习docker 镜像的记录,希望对大家有所帮助
80 0
TF之LiR:基于tensorflow实现手写数字图片识别准确率
TF之LiR:基于tensorflow实现手写数字图片识别准确率
62 0
用aliplayer如何实现视频的连续播放?
场景 假如有多个视频,在上一个视频播放完毕时,自动播放下一个视频,要怎么处理的呢?需要按使用的播放器类型和切换的地址格式,采用不同的实现方式。 直播地址方式 这种方式最简单,h5和flash的行为都是一致的,只需要订阅'ended', 在ended事件里,调用loadByUrl方法, 参数为下一个视频的地址。
5704 0
阿里云机器学习技术分享1——图像识别之TensorFlow实现方法【视频+PPT】
阿里云AI之图像识别技术是如何实现的!?视频+PPT干货奉上 讲师简介:赵昆 阿里巴巴机器学习技术专家 欢迎加入阿里云机器学习大家庭,**钉钉群:11768691** , QQ群:567810612 一、阿里云机器学习之图像识别实践-基础篇: 观看视频:http://cloud.
3499 0
可输入的下拉框(简易实现)
第一种效果 (带自动匹配)这个效果再之前的的博客里面已经讲到过了,还没有看过的小伙伴可以移步→ http://www.cnblogs.com/zhangxiaoyong/p/5763432.html 第二种效果 今天主要讲第二种效果,也比较简单,先看下效果 实现 页面部分 1...
1210 0
weex项目构建
weex已经上线几个月了,热度一直不减,给github上面star已经有5000+,很多人对weex还不是很了解,weex是一个跨三端、动态化解决方案,方案中借鉴react native的优点并且进行针对性优化补强,这篇博文简单带着大家玩一下weex的项目构建~
3457 0
Weex Android 文字渲染优化
# Weex Android 文字渲染优化 ## 背景 在做Weex Android适配工作的时候,发现当`Text`没有设置高度,需要Weex根据文字内容、样式,计算出宽高的时候,在小米手机上可能会出现文字截断现象。 例如,前端期望如下图所示的渲染效果: 然而在小米手机上的渲染效果却是下面这样,默认标题那一段最后一行的文本被截断了: ## 原因 在
7711 0
4849
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载