OpenGL Tutorials - Billboards

49 篇文章 0 订阅


原文: OpenGL-Turorial - Billboards

(2020.03.24 23:16 这里吐槽一下:我的天,CSDN更新后,写文章系统一堆BUG,写着写着,光标不会换行,即使按下多少次回车键都一样,然后保存草稿后,在刷新页面,发现之前写的一部分内容不见了。。。这。。。无语,多浪费心血啊。。。)

Billboards

Billboard 是3D世界中的2D元素。它不是一个在所有其他东西顶上显示的2D菜单,也不是一个可以让你旋转的3D平面;但介于他们之间的东西,如多数游戏中的:血条。

而Billboard区别在于他们的位置是可以指定的,但它们的朝向会一直的朝向镜头。

Solution #1 : The 2D way

解决方案1:2D的方法

这个方法超级简单。

仅仅计算在屏幕中的位置,并在这个位置上显示一个2D tex文本(查看教程11有)。

// Everything here is explained in Tutorial 3 ! There's nothing new.
glm::vec4 BillboardPos_worldspace(x,y,z, 1.0f);
glm::vec4 BillboardPos_screenspace = ProjectionMatrix * ViewMatrix * BillboardPos_worldspace;
BillboardPos_screenspace /= BillboardPos_screenspace.w;

if (BillboardPos_screenspace.z < 0.0f){
    // Object is behind the camera, don't display it.
}

噔噔!

从好的方面来说,这是非常简单的方法,并且billboard的大小不管离镜头距离多少都是一样的。但是2D tex文本本身是显示与其他东西顶层的,而且这种处理有可能还会影响到其他的渲染,并显示在这些对象顶上。

Solution #2 : The 3D way

解决方案2:3D的方式

这种方式一般来说是更好的,同时也不会太复杂。

处理目标是会保持对齐于镜头的,就算镜头移动:
在这里插入图片描述

你可以将这种方式认为是生成一个模型矩阵的方式,但其实更简单。

这个思路是:billboard的每一个角落掉都位于中心点,然后通过镜头的up(顶部)方向与right(右边)方向的向量来位置这些顶点:
在这里插入图片描述
当然,billboard的世界坐标我们是知道的,所以我们还需要镜头相对世界空间的up/right方向向量。

在相机空间下,相机的up向量就是(0,1,0)。要获取它的相对世界空间的向量,仅仅用它乘以一个Camera Space to World Space(相机空间到世界空间)的矩阵即可,就是View矩阵的逆矩阵。

有一个简便的方式同样可以获取:

CameraRight_worldspace = {ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]}
CameraUp_worldspace = {ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]}

(译者jave.lin:这里我要说明一下,ViewMatrix是视图矩阵,不是视图矩阵的逆矩阵,但其实使用其逆矩阵的方式获取会更加简便,只不过会需要更多性能,只要获取ViewMatrix的逆矩阵即可,而ViewMatrix是个正交基矩阵,所以其逆矩阵就是其转置矩阵,所以right = transpose(ViewMatrix)[0], up = traspose(ViewMatrix)[1]

一旦获取这些数据后,就可以非常简单的计算出最终的顶点位置:

vec3 vertexPosition_worldspace =
    particleCenter_wordspace
    + CameraRight_worldspace * squareVertices.x * BillboardSize.x
    + CameraUp_worldspace * squareVertices.y * BillboardSize.y;
  • particleCenter_worldspace 正如其名字描述的,就是billboard的世界坐标中心点位置。它是指定为uniform vec3
  • squareVertices 是网格的原点。squareVertices.x-0.5 就是左边的顶点,因此根据他来位移就是相对镜头左边方向移动(因为是乘以 CameraRight_worldspace的)
  • BillboardSize 是大小,使用世界单位,也是个uniform。

OK,到这里就完成了,不是很简单吗?
在这里插入图片描述
为了记录用,这里贴出squreVertices是怎么来的:

// The VBO containing the 4 vertices of the particles.
// VBO包含了粒子的四个顶点
 static const GLfloat g_vertex_buffer_data[] = {
 -0.5f, -0.5f, 0.0f,
 0.5f, -0.5f, 0.0f,
 -0.5f, 0.5f, 0.0f,
 0.5f, 0.5f, 0.0f,
 };

Solution #3 : The fixed-size 3D way

解决方案3:固定大小的3D方式

正如你上述所看到的,billboard的大小会相对相机的距离而改变。在某些情况下可以适用,但其他的一些情况,如:血条栏,你可能想要它是一个固定大小的。

也就是说,必须是在屏幕空间下移动他们的中心点或是角落点,这正是我们处理的:计算屏幕空间的中心点位置,再偏移它。

vertexPosition_worldspace = particleCenter_wordspace;
// Get the screen-space position of the particle's center
// 获取屏幕空间的粒子的中心点位置
gl_Position = VP * vec4(vertexPosition_worldspace, 1.0f);
// Here we have to do the perspective division ourselves.
// 这里我们自己处理透视除法
gl_Position /= gl_Position.w;

// Move the vertex in directly screen space. No need for CameraUp/Right_worlspace here.
// 直接在屏幕空间中移动顶点。这里就不需要CameraUp/Right_worldspace。
gl_Position.xy += squareVertices.xy * vec2(0.2, 0.05);

要记得这在渲染流水线里,是处于Normalized Device Coordinates(译者jave.lin:NDC可以参考:OpenGL Transformation 几何变换的顺序概要(MVP,NDC,Window坐标变换过程),所以它们轴上的值是在:-1~1之间的,另外注意这不是像素坐标。

如果你想要的是一个像素的大小,也简单:就使用(ScreenSizeInPixels / BillboardSizeInPixels ,就是:屏幕的像素大小 / Billboard的像素大小) BillboardSizeInScreenPercentageBillboard在屏幕的大小比例)来替代。
在这里插入图片描述

Solution #4 : Vertical rotation only

在某些系统中的模型,如很远的树和灯都用Billboard来处理。但你不想树歪了:它必须是垂直于地面的。所以你需要一个仅仅只旋转某个轴的系统。

好了,这个功能就留给读者当作练习!

(关于现实方式,我都再后面一篇文章都有描述,代码实现:Unity Shader - Billboard 广告板/广告牌

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值