使用 WebGPU 或 BabylonNative 时模拟越界视口

视口到底是什么?

在 Babylon.js 术语中,视口是屏幕上相机将绘制的矩形区域。它的值为xywidthheight,其中xy从屏幕左下角开始, 和width标准化height为屏幕宽度和高度。默认视口覆盖整个屏幕,因此它的xy都设置为0,它的widthheight都设置为1。像这样...

默认视口以紫色表示,覆盖整个屏幕。

大多数情况下,默认视口就足够了,您甚至不必考虑它,因为它允许您绘制整个屏幕,但如果您只想绘制屏幕的某个特定部分,该怎么办?这时,不起眼的视口就是您的朋友。例如,要绘制屏幕左下角的所有内容,您可以将相机的视口widthheight值更改为0.5。像这样...

将视口的宽度和高度更改为 0.5 会将其缩小为屏幕宽度的一半和屏幕高度的一半。

要将这个较小的视口移动到屏幕的右上角而不是左下角,您可以将相机的视口xy值更改为0.5。像这样...

将视口的 x 更改为 0.5 会将其移动到屏幕宽度的一半,将视口的 y 更改为 0.5 会将其移动到屏幕高度的一半。

到目前为止一切顺利,但如果视口矩形的定义方式使其超出屏幕边缘怎么办?例如,如果我们保留视口的xy设置为,0.5但将其widthheight0.5更改为 ,会怎么样0.6?像这样...

将视口的宽度和高度更改为 0.6 会使其超出屏幕边缘。

请注意,渲染的场景仍然填充与之前相同的视口面积,但现在视口的右上角超出了屏幕的边界,因此,如果您只看屏幕,则场景看起来比之前略微放大,并且更靠近右上角。可以通过增加视口的widthheight来更清楚地显示这一点0.75...

将视口的宽度和高度更改为 0.75 现在可以清楚地显示场景被放大并且已移近屏幕的右上角。

进一步实现此效果,我们可以将视口的xy值设置为负值,并使其widthheight大于屏幕尺寸。这将使场景看起来放大,同时仍然填满整个屏幕。像这样...

请注意,这里只有相机的视口值发生了变化,因此我们可以有效地放大场景,而无需在 3D 空间中移动相机或改变其视野,同时仍然以屏幕的全像素分辨率渲染场景!

听起来很简单,对吧?其实不然,因为……

问题

有些图形设备不允许你将视口的值设置为超出范围。这不是 OpenGL 视口规范中明确定义的内容,因此有些图形设备支持它,有些则不支持。对于那些不支持它的设备,视口的值被限制在屏幕大小,这意味着视口值不能以使得视口超出屏幕的方式定义。由于规范中的这种模糊性,WebGPU 和 BabylonNative 中使用的 bgfx 图形库都限制了视口值,以在所有可用硬件上保持一致。这意味着我之前展示的超出范围的视口缩放效果在针对 WebGPU 和/或 BabylonNative 时不起作用。

不幸的是,一些现有的代码库在设计时并未考虑到这些限制,我们在用 BabylonNative 替换应用程序的旧 3D 引擎时遇到了这个问题。更改应用程序的架构会很困难,因此我们决定尝试以不同的方式解决问题。我们使用了Babylon.js 材质插件

解决方案

视口本质上只是移动和缩放场景的几何图形以适应定义的矩形,因此当我问我们的图形大师 Alexis 如何解决我们遇到的越界限制时,他向我展示了如何使用 Babylon.js 材质插件 API 来修改场景中每种材质的顶点着色器,以与视口相同的方式移动和缩放几何图形。(如果您不熟悉材质插件文档,请查看此处。他们很好地解释了如何使用它们)。

这是一个Playground,显示了选中 UI 的“视口材质插件”enabled选项后顶点着色器材质插件的运行情况。如果您向下滚动到 Playground 代码的底部,您将看到此着色器代码被插入到场景使用的每个材质中...

gl_Position.x = gl_Position.x * viewport_w * viewport_h / viewport_w + (viewport_x + viewport_w - 1.0 + viewport_x) * gl_Position.w; 
gl_Position.y = gl_Position.y * viewport_h + (viewport_y + viewport_h - 1.0 + viewport_y) * gl_Position.w;

这是负责模拟视口的代码,它允许视口值距离屏幕任意远。而且由于它不使用视口图形 API,因此它与 WebGL、WebGPU 和 BabylonNative 的工作原理相同!代码本身相当容易理解。它只是缩放和移动给定的值,gl_Position这些值是您通常提供给视口 API 的值。唯一有点神秘的是gl_Position.w两行最后的乘法。那是什么w?这个问题的答案看起来很复杂,但它所做的就是根据它们在视锥中的深度调整最终的x和坐标。作为正常渲染管道的一部分,GPU 将通过执行来应用透视除法。由于我们希望应用“经受住”这次除法的平移(因为它必须发生在屏幕空间中),因此我们将平移预乘以。ygl_Position.xyz / gl_Position.wgl_Position.w

另一段值得注意的代码是使用剪刀矩形来防止在定义的视口之外进行任何绘制。从我目前展示的示例中看不出这一点,但如果您查看这个使用注释掉剪刀代码的天空盒的游乐场,就会看到背景是在定义的视口之外绘制的。在不使用材质插件的情况下更改相机的视口值时,GPU 会剪切视口外的所有内容,但我们需要在启用材质插件时手动执行此操作。这就是为什么我们在启用材质插件时为每个渲染调用打开和关闭剪刀的原因。尝试取消注释从第 113 行开始的代码,看看它对天空盒有什么影响。结果应该如下所示:https: //playground.babylonjs.com/#EXCRS4#8

总结

所以,就是这样!在某些情况下,视口可能是一个非常方便的工具,如果您发现需要使用界外视口,那么您可能需要使用我在此处展示的材质插件方法,尤其是在针对 WebGPU 或 BabylonNative 时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉达曼迪斯II

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值