2
votes

Using a texture, I'm trying to pass data to my shader so it knows what color each fragment should be. I'm attempting to create a voxel-type terrain (Minecraft style voxels) using 8-bit ints, with each RGBA value being a different color specified on the shader. The value 1 might be green and 2 might be brown for example.

If my math is correct, a 2048 x 2048 sized texture is the exact size needed for the voxel terrain data:

2048 x 2048 sized texture = 4194304 pixels.

8 x 8 = 64 "chunks" loaded at once.
32 x 32 x 256 = 262144 voxels in a chunk.
64 x 262144 = 16777216 voxels.

For each pixel in the texture I can use RGBA as individual values, so divide it by 4: (Each voxel is therefore 1 byte which is fine as values will be less than 200.)

16777216 / 4 = 4194304 pixels.

That said, I'm having trouble getting the correct texture coordinates to represent the 3D terrain. This is my code at the moment which works fine for a flat plane:

Fragment shader:

int modint( int a, int b )
{
    return a - int( floor( float( a ) / float( b ) ) * float( b ) );
}

void main() {
    // divide by 4096 because we're using the same pixel twice in each axis
    vec4 data = texture2D(uSampler, vec2(verpos.x / 4096.0, verpos.z / 4096.0));

    vec2 pixel_of_target = verpos.xz;
    int _x = int( pixel_of_target.x );
    int _y = int( pixel_of_target.y );
    int X = modint( _y, 2 ) * 2 + modint( _x, 2 );

    vec4 colorthing;
    float blockID;

    if (X == 0) blockID = data.x;
    else if (X == 1) blockID = data.y;
    else if (X == 2) blockID = data.z;
    else if (X == 3) blockID = data.w;

    if (blockID == 1.0) gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
    else if (blockID == 2.0) gl_FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );
    else if (blockID == 3.0) gl_FragColor = vec4( 0.0, 0.0, 1.0, 1.0 );
    else if (blockID == 4.0) gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );
}

So basically my texture is a 2D map containing slices of my 3D data, and I need to modify this code so it calculates the correct coordinates. Anyone know how I would do this?

2

2 Answers

0
votes

Assuming that your texture will contain 64 slices of your terrain in a 8x8 grid, the following lookup should work:

vec2 texCoord = vec2((verpos.x / 8.0 +   mod(verpos.y, 8.0)) / 4096.0,
                     (verpos.x / 8.0 + floor(verpos.y / 8.0)) / 4096.0));
vec4 data = texture2D(uSampler, texCoord);
... Rest of your shader as above

Your texture then should contain a full 2D slice of the terrain at height 0, then next to it a slice at height 1, until height 7 at the rightmost position. In the next row are heights 8 - 15 and so on.

Having said that, you should normally try to avoid ifs in shader code, because it slows down shader processing quite a bit. If webGL supports arrays (which it does to my knowledge), you can store all your colors in an array and do an array-lookup instead of the if-chain

0
votes

the texture coordinate should be

vec4 data = texture2D(uSampler, vec2(verpos.x / (4 * 2048.0), verpos.z / 2048.0));

and then the byte to read would be given by

int index = (verpos.x/2048) % 4;

if index == 0 pick data.r, if 1 data.g, if 2 data.b and so on...