Saturday, 27 July 2013

Rendering transparent blocks

Rendering a block with transparent sections (eg a block of glass) is relatively straightforward.  You need to create an image which has an alpha channel (a mask which indicates the transparent parts of the image).  This can be rendered as a standard block.  For example, if the numbered cube from the earlier block rendering tutorials is modified so that each face has an alpha channel where the white sections are made transparent:

Some important things to note:
  • You can only see faces which point in your direction.  Any faces which are pointing the other way (eg faces 2 and 0 in the pictures above) are not visible until you walk to the other side of the block (as per faces 4 and 5 above).
  • You need to override isOpaqueCube() to return false, otherwise the adjacent faces of adjacent blocks won't be rendered properly, for example see below.  For more information on the various effects of isOpaqueCube, see here.
isOpaqueCube returns true - grass under the block is not rendered
  • The transparency is either on or off - a pixel is either fully transparent or fully opaque.  For exampe - if your alpha channel has 50% transparency, it will be treated as opaque. 
In order to render partially transparent blocks such as ice, Minecraft uses a different rendering "pass".  During WorldRenderer.updateRenderer(), two passes are performed:
  • pass 0 (all pixels either "opaque" or "fully transparent"), back faces not rendered
  • pass 1 (0 - 100% transparency; back faces visible).
Each block is rendered on either pass 0 or pass 1, as determined by Block.getRenderBlockPass() and Block.canRenderInPass().  For example - Ice normally renders in pass 1.  The picture below shows the difference between ice rendered in pass 1 (left) and pass 0 (right).

Ice rendered in pass 1 (left) or pass 0 (right).

If our example cube is converted to have a striped alpha channel on the "2" face, i.e. from top to bottom 0%, 25%, 50%, 75%:
then it renders as follows in the two different passes:
Pass 0 render (left) vs pass 1 render (right)
The key differences are:
  • Pass 1 renders 25%, 50%, 75% transparency as partially transparent.
  • The back faces (eg 3) are visible in pass 1
It is possible to render a block in both passes, but this is not generally useful.

If multiple transparent blocks are placed next to each other, the internal faces are all visible (the two "4" faces in the picture below).  This is different from (for example) glass and ice, where these "internal faces" are not shown.
Minecraft achieves this using the shouldSideBeRendered() method.  For example - BlockGlass overrides shouldSideBeRendered(), and returns false if the adjacent block is also a glass block.  (see BlockBreakable.shouldSideBeRendered()).
BlockGlass.shouldSideBeRendered(): left = vanilla code; right = overridden to always return true