视口到底是什么?
在 Babylon.js 术语中,视口是屏幕上相机将绘制的矩形区域。它的值为x
、y
、width
和height
,其中x
和y
从屏幕左下角开始, 和width
标准化height
为屏幕宽度和高度。默认视口覆盖整个屏幕,因此它的x
和y
都设置为0
,它的width
和height
都设置为1
。像这样…
大多数情况下,默认视口就足够了,您甚至不必考虑它,因为它允许您绘制整个屏幕,但如果您只想绘制屏幕的某个特定部分,该怎么办?这时,不起眼的视口就是您的朋友。例如,要绘制屏幕左下角的所有内容,您可以将相机的视口width
和height
值更改为0.5
。像这样…
要将这个较小的视口移动到屏幕的右上角而不是左下角,您可以将相机的视口x
和y
值更改为0.5
。像这样…
到目前为止一切顺利,但如果视口矩形的定义方式使其超出屏幕边缘怎么办?例如,如果我们保留视口的x
并y
设置为,0.5
但将其width
和height
从0.5
更改为 ,会怎么样0.6
?像这样…
请注意,渲染的场景仍然填充与之前相同的视口面积,但现在视口的右上角超出了屏幕的边界,因此,如果您只看屏幕,则场景看起来比之前略微放大,并且更靠近右上角。可以通过增加视口的width
和height
来更清楚地显示这一点0.75
…
进一步实现此效果,我们可以将视口的x
和y
值设置为负值,并使其width
和height
大于屏幕尺寸。这将使场景看起来放大,同时仍然填满整个屏幕。像这样…
请注意,这里只有相机的视口值发生了变化,因此我们可以有效地放大场景,而无需在 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 将通过执行来应用透视除法。由于我们希望应用“经受住”这次除法的平移(因为它必须发生在屏幕空间中),因此我们将平移预乘以。y
gl_Position.xyz / gl_Position.w
gl_Position.w
另一段值得注意的代码是使用剪刀矩形来防止在定义的视口之外进行任何绘制。从我目前展示的示例中看不出这一点,但如果您查看这个使用注释掉剪刀代码的天空盒的游乐场,就会看到背景是在定义的视口之外绘制的。在不使用材质插件的情况下更改相机的视口值时,GPU 会剪切视口外的所有内容,但我们需要在启用材质插件时手动执行此操作。这就是为什么我们在启用材质插件时为每个渲染调用打开和关闭剪刀的原因。尝试取消注释从第 113 行开始的代码,看看它对天空盒有什么影响。结果应该如下所示:https: //playground.babylonjs.com/#EXCRS4#8
总结
所以,就是这样!在某些情况下,视口可能是一个非常方便的工具,如果您发现需要使用界外视口,那么您可能需要使用我在此处展示的材质插件方法,尤其是在针对 WebGPU 或 BabylonNative 时。
RA/SD 衍生者AI训练营。发布者:chris,转载请注明出处:https://www.shxcj.com/archives/6211