less_retarded_wiki/billboard.md

33 lines
6 KiB
Markdown
Raw Normal View History

# Billboard
In [3D](3d.md) [computer graphics](graphics.md) billboard is a flat image placed in the scene that rotates so that it's always facing the camera. Billboards used to be greatly utilized instead of actual [3D models](3d_model.md) in old [games](game.md) thanks to being faster to render (and possibly also easier to create than full 3D models), but we can still encounter them even today and even outside retro games, e.g. [particle systems](particle_system.md) are normally rendered with billboards (each particle is one billboard). Billboards are also commonly called *[sprites](sprite.md)*, even though that's not exactly accurate.
There are two main types of billboards:
- Ones **rotating only about vertical axis**, i.e. billboards that change only their [yaw](yaw.md), they only face the camera in a top-down view of the scene. Such sprite may deform on the screen (when the camera is at different height level) just like 3D models do and when viewed completely from above will disappear completely. This may in some situations look better than other options (e.g. in [games](game.md) enemies won't appear lying on their back when seen from above).
- **Freely rotating** ones, i.e. ones that change all three [Euler angles](euler_angle.md) so that they ALWAYS face the camera from any possible angle. There may further be other two subtypes: billboards that align themselves with the camera's projection plane (they simply rotate themselves in the same way as the camera) which always end up on the screen as an undeformed and unrotated image, and billboards that face themselves towards the camera's position and copy the camera's [roll](roll.md) (though these may seem like two same things, they are not, for the latter we need to know the camera and billboard's positions, for the former we only need the camera's rotation). The former is simpler to implement and may also look better, so we normally don't even consider the latter.
Some billboards also choose their image based on from what angle they're viewed (e.g. an enemy in a game viewed from the front will use a different image than when viewed from the side, as seen e.g. in [Doom](doom.md)). Also some billboards intentionally don't scale and keep the same size on the screen, for example health bars in some games.
In older software billboards were implemented simply as image [blitting](blit.md), i.e. the billboard's scaled image would literally be copied to the screen at the appropriate position (this would implement the freely rotating billboard). Nowadays when rendering 3D models is no longer really considered harmful to performance and drawing pixels directly is less convenient, billboards are more and more implemented as so called [textured](texture.md) [quads](quad.md), i.e. they are really a flat square 3D model that may pass the same pipeline as other 3D models (even though in some frameworks they may actually have different [vertex shaders](vertex_shader.md) etc.) and that's simply rotated to face the camera in each frame (in [modern](modern.md) frameworks there are specific functions for this).
[Fun](fun.md) fact: in the old games such as [Doom](doom.md) the billboard images were made from photographs of actual physical models from clay. It was easier and better looking than using the primitive 3D software that existed back then.
## Implementation Details
The following are some possibly useful things for implementing billboards.
The billboard's position on the screen can be computed by projecting its center point in [world coordinates](world_space.md) with [modelview](modelview.md) and [projection](projection.md) matrices, just as we project vertices of 3D models.
The billboard's size on the screen shall due to [perspective](perspective.md) be multiplied by *1 / (tan(FOV / 2) * z)* where *FOV* is the camera's [field of view](fov.md) and *z* is the billboard's distance from camera's projection plane (which is NOT equal to the mere distance from the camera's position, that would create a [fisheye](fisheye.md) lens effect -- the distance from the projection plane can be obtained from the above mentioned [projection matrix](projection.md)). (If the camera's FOV is different in horizontal and vertical directions, then also the billboard's size will change differently in these directions.)
For billboards whose images depends on viewing angle we naturally need to compute the angle. We may do this either in 2D or 3D -- most games resort to the simpler 2D case (only considering viewing angle in a single plane parallel to the floor), in which case we may simply use the combination of [dot product](dot_product.md) and [cross product](cross_product.md) between the [normalized](normalization.md) billboard's direction vector and a normalized vector pointing from the billboard's position towards the camera's position (dot product gives the [cosine](cos.md) of the angle, the sign of cross product's vertical component will give the rest of the information needed for determining the exact angle). Once we have the angle, we [quantize](quantization.md) (divide) it, i.e. drop its precision depending on how many directional images we have, and then e.g. with a [switch](switch.md) statement pick the correct image to display. For the 3D case (possible different images from different 3D positions) we may first transform the sprite's 3D facing vector to [camera space](camera_space.md) with appropriate matrix, just like we transform 3D models, then this transformed vector will (again after quantization) directly determine the image we should use.
When implementing the free rotating billboard as a 3D quad that's aligning with the camera projection plane, we can construct the [model matrix](model_matrix.md) for the rotation from the camera's normalized directional vectors: *R* is camera's right vector, *U* is its up vector and *F* is its forward vector. The matrix simply transforms the quad's vertices to the coordinate system with bases *R*, *U* and *F*, i.e. rotates the quad in the same way as the camera. When using [row vectors](row_vector.md), the matrix is following:
```
R.x R.y R.z 0
U.x U.y U.z 0
F.x F.y F.z 0
0 0 0 1
```