2011-06-07

Minecraft mapping - Tiled rendering

This is the fourth in a series of articles about my attempts to write a Minecraft mapping program using Python. Last week, we made a simple texture and rendered it with OpenGL. For the amount of work involved, it wasn't terribly impressive! Today we're going to change the fragment shader to do the tiled rendering that we described before.

The fragment shader needs to calculate what colour each pixel should be. As input, it takes the texture coordinates that were output by the vertex shader. We have a single great big square with texture coordinate (0,0) in the bottom left and texture coordinate (1,1) in the top right. We want to texture this with a 512×512 grid of square textures drawn from our 16×16 grid of textures in our texture atlas.

The fragment shader needs to do two things with the texture coordinate. One is to look up our 512×512 texture that is our Minecraft map region - it can do that directly with no extra calculation. The other is to take that texture coordinate, consider it as a point in a grid of 512×512 cells, and figure out where it falls within its cell. We call this position theta. A theta of (0,0) corresponds to the bottom left of the cell, and (1,1) to the top right of the cell.

Given the sample taken from our map texture, we extract the red value to get a code between 0 and 255 that indicates a cell in our texture atlas. We call the position of that cell phi. The bottom-left cell in the texture atlas has phi=(0,0), the top-right cell has phi=(15, 15).

Finally, combining phi and theta identifies the position to sample from the texture atlas for the fragment we're rendering. Here's the new shader:

fragment_shader = '''\
#version 130

const float TILE_COUNT = 512.0;
const float INV_TILE_COUNT = 1.0 / TILE_COUNT;

uniform sampler2D texture_atlas;
uniform usampler2D map_texture;

in vec2 texcoord;
out vec4 fragcolor;

void main()
{
    vec2 theta;

    
    theta = (mod(texcoord, INV_TILE_COUNT) * TILE_COUNT);
    uvec4 map_sample = texture(map_texture, texcoord);
    uint uphi = map_sample.x % 16u;
    uint vphi = 15u - (map_sample.x / 16u);
    vec2 phi = vec2(uphi, vphi);
    vec2 atlas_point = phi / 16.0 + theta / 16.0;

    fragcolor = texture2D(texture_atlas, atlas_point);
}
'''

The complete source file is here:opengl2.py There are a few other changes. I've also tried enabled mip-mapping on the texture atlas, while limiting it to four levels, because beyond that the mip-maps would start blending together unrelated tiles. Unfortunately, I think I've still not quite got it working right, but that won't be immediately obvious. I might talk about it more in a later article, but it's going to need some work.

Here's the end result. (Click it to view it full size.) As you'll note, for now we've used the diagnostic texture for both the map texture and the texture atlas. On Friday we'll add some controls to zoom and pan so that we can actually see the whole thing and next week we'll start reading from Minecraft files.

No comments:

Post a Comment