http://www.cnblogs.com/gongminmin/archive/2010/11/21/1883392.html

多年前NVIDIA就发布了3D Vision技术,能提供多种立体渲染的效果。随着2009年的电影阿凡达所带来的世界性3D狂潮,你是否也想在自己的程序中加入立体渲染呢?

3D Vision的原理

根据http://developer.nvidia.com/object/3d_stereo_dev.html,3D Vision的原理如下:

在驱动内部,所有3D场景都渲染两次——一次用左眼,一次用右眼。驱动会自动“在线”修改典型的3D游戏vertex shader,所以在执行期可以产生正确的图像。

注 意加粗的几个词所透露出来的信息。首先,你的每一个Draw call都被驱动变成了两个Draw call;其次,立体化的过程是自动的,无法自由控制;第三,它只能处理典型的vertex shader,而不是任意的vertex shader,比如sky box的vertex shader,就往往是个“非典型”的。NVIDIA的思路就是,把一切都封装起来,只有个别参数可以让开发人员和用户调整。程序能做的事就只能是把一切 交给驱动,祈祷最终结果正确,极其被动。

其实,立体渲染没有那么stupid。比如,图形引擎可以分别从左右眼主动生成2张图像,都是 用正确的vertex shader,然后交给图形API。这么做就可以保证整条流水线都是支持立体的,包括视锥裁剪,结果就是所有物体均能100%渲染正确。而不会像3D Vision的方法,不但vertex shader必须“典型”,还得没法处理被场景管理器裁掉的物体(比如左眼能看到,右眼看不到的物体)。再比如Crytek在CryEngine3里的立 体渲染方法,把生成的单一图像通过image warp的方式分别得到左右眼的结果,不必渲染2遍,就能在性能基本不降低的情况下进行立体渲染。这些方式都不能使用自动的3D Vision,必须要通过生成2张image的方式才能实现。

既然放弃了自动的3D Vision,我们就来开始探索如何手动把两张图提交给驱动,控制驱动产生立体渲染的方法。

尝试1:NVAPI

3D Vision并没有提供提交2张图像的方法,那就看看师出同门的NVAPI。NVAPI是NVIDIA提供的一个SDK,可以直接访问GPU和驱动的功能。本来我满怀欣喜地认为NVAPI一定给出了手动控制立体渲染的方法,结果发现公开版本的NVAPI最多也就提供了打开和关闭立体渲染这样的功能,并不能实现我们想要的。根据http://en.wikipedia.org/wiki/Nvidia_3D_Vision, 在专有版本的NVAPI(也就是签署了NDA的版本)包含有显式控制的功能。但这个专有的版本很难申请到,对于大公司还好,对于小作坊、业余开发的爱好者 之流,就根本没机会了(经常是提交申请后什么反应都没有)。对于开源开发就成了噩梦,NDA的东西无法随着开源软件一起发布,所以这条路成了死胡同。难道 就没有办法了吗?

尝试2:OpenGL QuadBuffer

OpenGL本身就提供了GL_LEFT_BACK、GL_RIGHT_BACK、GL_LEFT_FRONT和GL_RIGHT_FRONT四个缓冲区,内建了立体的支持。但这种方法的缺点也是明显的:

  1. 只支持OpenGL。游戏主流的D3D均无法使用quad buffer。
  2. 在Windows上,一般的OpenGL驱动均不支持quad buffer,只有Quadro支持。

可以认为,这条路也失败了。难道,就真的没有办法了吗?

尝试3:3D Video

NVIDIA在发布3D Vision的同时,也发布了一款叫做3D Video player的软件,可以播放立体影片,并提供了一些样片下载。这些影片只是普通的视频格式,用一般播放器播放的时候是左右眼并排排列的,如下图所示:

但 用3D Video player播放的时候,就能呈现出立体效果。在播放视频的情况下,驱动没有3D信息、没有vertex shader,所以肯定不是用上文所说的“自动”方式得到立体效果的。虽然3D Video player非常可能用了专有的NVAPI,我仍然希望它是用一般的方法做到的。在GDC 2009上,NV的演讲提到了3D Video的显示方法,经过测试果然成功!那是NV留的一个后门,用来显示以有的立体数据。

3D Video细节

根据前面找到的材料,3D Video的处理方法如下:

  1. 把左右眼图像拷入一张大纹理中,大纹理的宽为w * 2,高为h + 1(w和h分别是原图像的宽和高)。左眼在左边,右眼在右边。
  2. 大纹理的最后一行加入特别的标志(此为关键所在)。
  3. 用StretchRect把大纹理拷入Back buffer。
  4. 当Back buffer显示出来的时候就是立体的了。

看来,一切玄机尽在“特别的标志”中。正是那个标志,让驱动把纹理识别为立体图像,在StretchRect和Present的时候做特殊处理。该标志的定义是这样的:

// Stereo Blit defines
#define NVSTEREO_IMAGE_SIGNATURE 0x4433564e //NV3D

typedef struct _Nv_Stereo_Image_Header
{
unsigned int dwSignature;
unsigned int dwWidth;
unsigned int dwHeight;
unsigned int dwBPP;
unsigned int dwFlags;
} NVSTEREOIMAGEHEADER, *LPNVSTEREOIMAGEHEADER;

// ORed flags in the dwFlags fiels of the _Nv_Stereo_Image_Header structure above
#define     SIH_SWAP_EYES               0×00000001
#define     SIH_SCALE_TO_FIT            0×00000002

填充的方式:

D3DLOCKED_RECT lr;
pSurf->LockRect(&lr, NULL, 0);

// 填到最后一行
LPNVSTEREOIMAGEHEADER pSIH =
reinterpret_cast<LPNVSTEREOIMAGEHEADER>(static_cast<unsigned
char *>(lr.pBits) + (lr.Pitch * height));

pSIH->dwSignature = NVSTEREO_IMAGE_SIGNATURE;
pSIH->dwBPP = 32;
pSIH->dwFlags = SIH_SWAP_EYES;
pSIH->dwWidth = gImageWidth*2;
pSIH->dwHeight = gImageHeight;

pSurf->UnlockRect();

这个标志可以在纹理建立的时候就填充上,以后每一帧只需要把左右眼的图像拷进去就行了。

Direct3D 10/11的实现

前面举得例子用的是D3D 9,而在D3D10/11中,情况又会如何呢?D3D10/11没有了StretchRect,取而代之的是CopyResource和CopySubresourceRegion,两者都没有缩放的能力。不管了,试试再说:

D3D11_BOX box;
box.left = 0;
box.right = w;
box.top = 0;
box.bottom = h;
box.front = 0;
box.back = 1;
d3d_imm_ctx->CopySubresourceRegion(backbuffer, 0, 0, 0, 0, surf, 0, &box);

结果成功了!NV的驱动仍然会根据那个标志来特殊化CopySubresourceRegion。

至此,D3D9/D3D10/D3D11/OpenGL下都可以通过2张图像来控制3D Vision显示出立体效果,你的程序也可以用同样的方法快步进入立体的行列。在KlayGE 3.11中,stereo模式也使用本文所述的方法。

未解决的问题


分别获得左右眼图像的过程中,应该是要关闭3D
Vision的,否则左右眼图像也都会分别变成立体的,性能大减。通过NVAPI的NvAPI_Stereo_Deactivate确实可以关闭3D
Vision,但这样的话连最后需要3D
Vision的StretchRect等也失效了。由于某些原因NvAPI_Stereo_Activate这个函数出现在头文件和库文件中,却没出现在
文档里,对该函数的调用似乎需要等到下一帧才会启用3D Vision。结果还是不正确。暂时的解决方法是把左右眼图像略微减小,比如高度从h变成h –
2,驱动发现小于front buffer的渲染就会暂时关闭3D Vision。

更多游戏引擎、图形编程、业界资讯,请见http://www.klayge.org

在程序中使用NV 3D Vision 【转】的更多相关文章

  1. 从0开发3D引擎(十):使用领域驱动设计,从最小3D程序中提炼引擎(上)

    目录 上一篇博文 下一篇博文 前置知识 回顾上文 最小3D程序完整代码地址 通用语言 将会在本文解决的不足之处 本文流程 解释本文使用的领域驱动设计的一些概念 本文的领域驱动设计选型 设计 引擎名 识 ...

  2. 从0开发3D引擎(十一):使用领域驱动设计,从最小3D程序中提炼引擎(第二部分)

    目录 上一篇博文 本文流程 回顾上文 解释基本的操作 开始实现 准备 建立代码的文件夹结构,约定模块文件的命名规则 模块文件的命名原则 一级和二级文件夹 api_layer的文件夹 applicati ...

  3. 从0开发3D引擎(十二):使用领域驱动设计,从最小3D程序中提炼引擎(第三部分)

    目录 上一篇博文 继续实现 实现"DirectorJsAPI.init" 实现"保存WebGL上下文"限界上下文 实现"初始化所有Shader&quo ...

  4. 基于3D Vision眼镜的OSG立体显示 【转】

    http://blog.csdn.net/qq_20038925/article/details/50510565 OSG 立体显示 3D Vision眼镜:所实现的是被动立体. 1.本人最近在做os ...

  5. 【MFC】如何在MFC创建的程序中更改主窗口的属性 与 父窗口 WS_CLIPCHILDREN 样式 对子窗口刷新的影响 与 窗体区域绘制问题WS_CLIPCHILDREN与WS_CLIPSIBLINGS

    如何在MFC创建的程序中更改主窗口的属性 摘自:http://blog.sina.com.cn/s/blog_4bebc4830100aq1m.html 在MFC创建的单文档界面中: (基于对话框的, ...

  6. C#中Winform程序中如何实现多维表头【不通过第三方报表程序】

    问题:C#中Winform程序中如何实现多维表头. 在网上搜了很多方法,大多数方法对于我这种新手,看的都不是很懂.最后在新浪博客看到了一篇比较易懂的文章:[DataGridView二维表头与合并单元格 ...

  7. 微信小程序中事件

    微信小程序中事件 一.常见的事件有 类型 触发条件 最低版本 touchstart 手指触摸动作开始 touchmove 手指触摸后移动 touchcancel 手指触摸动作被打断,如来电提醒,弹窗 ...

  8. 小程序中使用threejs

    webgl调试 起初使用threejs 在小程序里面调试,明明是按着官方的文档来,但是会发现开发者工具上面会提示getContext,经过一翻摸索,发现webgl调试只能在手机端调试. 总结:webg ...

  9. C# 程序中嵌入百度地图

    本例是对WinForm中使用百度地图的简要介绍.百度地图目前支持Android开发,IOS开发,Web开发,服务接口,具体可以参照'百度地图开放平台'. [动态加载百度地图]涉及到的知识点: WebB ...

随机推荐

  1. Leetcode 649.Dota2参议院

    Dota2参议院 Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇) Dota2 参议院由来自两派的参议员组成.现在参议院希望对一个 Dota2 游戏里的改变作出决定.他们以一 ...

  2. (转载) Linux五种IO模型

    转载:http://blog.csdn.net/jay900323/article/details/18141217     Linux五种IO模型及分析   目录(?)[-] 概念理解 Linux下 ...

  3. hdu6136[模拟+优先队列] 2017多校8

    有点麻烦.. /*hdu6136[模拟+优先队列] 2017多校8*/ #include <bits/stdc++.h> using namespace std; typedef long ...

  4. Java实现简单的socket通信

    今天学习了一下java如何实现socket通信,感觉难点反而是在io上,因为java对socket封装已经很完善了. 今天代码花了整个晚上调试,主要原因是io的flush问题和命令行下如何运行具有pa ...

  5. FZU 2041 二分枚举

    思路:先O(n)预处理出ri[i][j],le[i][j],分别表示第i个位置向右边移动出j个空格需要的步数,表示第i个位置向左边移动出j个空格需要的步数. 然后枚举间隙处,二分判段最大间隔. #in ...

  6. openssl RSA 内存读取密钥

    主要注意一下密钥的格式 #include <openssl/pem.h> #include <openssl/err.h> bool CEncipher::CreatePubK ...

  7. vue-element-admin开发模式下style标签热更新失效[解决办法]

    参考:https://forum.vuejs.org/t/vue-cli-3-x-style/46306/3 vue.config.js添加配置 css: { sourceMap: false, mo ...

  8. mybatis 关联查询时,从表只返回第一条记录解决办法

    如果两表联查,主表和明细表的主键都是id的话,明细表的多条只能查询出来第一条. 造成以上情况可能的原因: 1.级联查询的时候,主表和从表有一样的字段名的时候,在mysql上命令查询是没问题的.但在my ...

  9. 【程序打包工具 Inno Setup】转

    原文转自 https://jingyan.baidu.com/article/36d6ed1f50ecfc1bcf4883aa.html

  10. android中提示&对话框----AlertDialog

    AlertDialog(对话框) 一.对话框的基本使用流程 step1:创建AlertDialog.Buider; step2:调用setIcon()设置图标,setTitle()或者setCusto ...