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. JVM虚拟机系列(一)类的加载

    JAVA虚拟机系列(一) 类的加载 目录 1 类的初始化过程 2 详解初始化时的各个阶段 一.类初始化的过程 先来看一个CLASS文件在整体生命周期里会遇到的阶段: xxxx.class ---> ...

  2. PAT1028

    某城镇进行人口普查,得到了全体居民的生日.现请你写个程序,找出镇上最年长和最年轻的人. 这里确保每个输入的日期都是合法的,但不一定是合理的——假设已知镇上没有超过200岁的老人,而今天是2014年9月 ...

  3. GitLab-CI环境搭建与操作手册

    第一章 系统安装简介 1.1. 系统结构 GitLab-CI持续集成服务主要包括gitlab.runner 2个模块.Gitlab主要负责代码文件的管理:runner则负责版本编译.存储.推送等任务. ...

  4. oracle组合分区

    由于Interval分区是针对range的,11g-12.1版本,目前只有Interval—*一共3种Interval的复合分区 range-list方法: partition by range (u ...

  5. js复制到粘贴板

    http://www.cnblogs.com/52fhy/p/5383813.html(移动端有兼容性问题) 要页面加载完直接绑定事件,否则第一次点击是绑定事件,第二次才触发事件 移动端需要设置tex ...

  6. Python之数据结构:元组

    一.元组 tupleA=(1,2,3,4,5,6) print tupleA 1.元组支持的运算符 tup1+tup2 tup1*2 3 in tup2 for i in tup2: print i ...

  7. JavaScript 笔记(2) -- 类型转换 & 正则表达 & 变量提升 & 表单验证

    目录:  typeof, null, undefined, valueOf() 类型转换 正则表达式 错误: try, catch, throw 调试工具 变量提升 strict 严格模式 使用误区 ...

  8. 如何清除全部的NSUserDefaults储存的数据。

    今天做项目遇到,如何清除全部的NSUserDefaults储存的数据. 方法1:找到所有的key然后remove掉 代码: [objc] view plain copy /** * 清除所有的存储本地 ...

  9. [LeetCode] Valid Number 确认是否为数值

    Validate if a given string is numeric. Some examples:"0" => true" 0.1 " => ...

  10. Bluedroid之GKI

    1. 概述 GKI以库libbt-brcm_gki.a的形式提供给Bluedroid使用 该层是一个适配层,适配了OS相关的进程.内存相关的管理,还可以用于线程间传递消息 主要通过变量gki_cb(结 ...