• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
SeraphLance

Rendering Blocky Terrain Tiles

4 posts in this topic

I'm trying to render a MineCraft-esque heightmapped terrain in blocks. The specifics are as follows:

1. Each tile is a cube in 3-space.
2. Theoretically infinite expansion.
3. Each Tile having an independent texture. This is the tricky one for me.
4. Each texture covers all sides of a block. Eventually, I may need full skins like MC uses, but I'm not concerned with that at the moment.

I've got #2 covered with noise functions. #1 is trivial in isolation, but I encounter problems coupling it with 3.

My original idea/implementation was texture atlasing. However, atlasing a uniform terrain like this requires a lot of vertex duplication (36 vertices for every cube, because no vertices can be shared between anything beyond the quad level for atlasing to work), and batch limits mean I need an enormous number of draw calls for even a small sample. The other alternative I thought of was to create a new VBO for every texture, and make the textures separate. The problem with this is that it requires at least as many draw calls as there are tile textures, even on small geometry samples. This doesn't scale terribly well.

How do people actually do it? It doesn't seem like a terribly complicated problem, and everyone else seems to manage just fine.
0

Share this post


Link to post
Share on other sites

Both of your naive solution will work very well.

 

I've started using your second solution: 1dynamic vb per texture.

- Then i've subdivided my terrain in chunk.

- Then i've added atlasing.

- Then i've added what you call "skinnin" at a later stage.

 

My best advice, do it then optimize! Dont optimize what you dont need to.

The only true thing about software design is: "The best solution is always the simplest solution".

In other words, the most naive solution is ALWAYS the best one.

When that first naive solution dont fit your needs/problems anymore; move forward.

0

Share this post


Link to post
Share on other sites

If you're just using unit cubes anyway, you can limit yourself to per-face information and with a texture atlas get away with a few bytes per visible face. After all it just takes a few bits to encode the face, one byte to index the texture atlas and 3 bytes for the x,y,z position. So even if you blow this up to 4x32bit to make your hardware happy, you can save a lot of memory in return for a little work in the geometry shader.

 

My version basically passes two ivec4. One with position and texture info, one with lighting info. x and z are stored in position.x, y is stored in position.y (z and w are basically wasted). each coordinate in the light info has the sun and block light info for one face vertex.

 

So you have uniform info that never changes (face vertices for a unit cube), some that changes per frame (daylight), some that changes per chunk (chunk coordinate offset), and the actual geometry for each visible face.

 

Major downside if you aim at a MC clone: MC doesn't exclusively use unit cubes. Though there are enough of them to consider rendering those and the "special" blocks separately, especially if you just want to experiment and see how far you can push it.

0

Share this post


Link to post
Share on other sites

- Then i've added atlasing.

 

What's the point of atlasing if you tie each VBO to a unique texture?blink.png

 

 

My best advice, do it then optimize! Dont optimize what you dont need to.
The only true thing about software design is: "The best solution is always the simplest solution".
In other words, the most naive solution is ALWAYS the best one.
When that first naive solution dont fit your needs/problems anymore; move forward.

 

The problem is that the first solution isn't good enough now.  I haven't yet coded the second one, but it's a non-trivial task if I'm not even sure whether it will be sufficient.

 

If you're just using unit cubes anyway, you can limit yourself to per-face information and with a texture atlas get away with a few bytes per visible face. After all it just takes a few bits to encode the face, one byte to index the texture atlas and 3 bytes for the x,y,z position. So even if you blow this up to 4x32bit to make your hardware happy, you can save a lot of memory in return for a little work in the geometry shader.

 

My version basically passes two ivec4. One with position and texture info, one with lighting info. x and z are stored in position.x, y is stored in position.y (z and w are basically wasted). each coordinate in the light info has the sun and block light info for one face vertex.

 

So you have uniform info that never changes (face vertices for a unit cube), some that changes per frame (daylight), some that changes per chunk (chunk coordinate offset), and the actual geometry for each visible face.

 

Major downside if you aim at a MC clone: MC doesn't exclusively use unit cubes. Though there are enough of them to consider rendering those and the "special" blocks separately, especially if you just want to experiment and see how far you can push it.

 

This sounds interesting, but I really have no clue what you're describing.  I'm not sure if my tiles are all going to be unit or not, but I do know that whatever dimensions they are, they will be uniform, so I won't have half-tiles or anything like that.  I'm not really making a MC clone, it's just the easiest way to describe the kind of terrain I'm trying to make.  My terrain won't even have overhangs or be destructible.

Edited by SeraphLance
0

Share this post


Link to post
Share on other sites
The first step to save a lot of memory and/or unnecessary rendering is to think in visible sides of a cube. If your cubes describe a plane, then drawing all 6 sides for each cube means 5/6th of that work is completely pointless. For example, since you don't want overhangs, none of your cubes will ever need a bottom. On average, I wouldn't expect more than 2-3 sides of a cube being visible. So whenever a neighboring cube exists (and isn't transparent), there is no point in drawing that side.
 
If all your cubes are uniform, then storing the actual vertices for each cube means a huge amount of redundant information. For example, each cube in relation to its position will have these vertices:
 
Vector4 faceCoords[6][4] =
{
     {   Vector4(0,1,0,0), Vector4(0,0,0,0), Vector4(0,1,1,0), Vector4(0,0,1,0), } //Left
     {   Vector4(1,1,1,0), Vector4(1,0,1,0), Vector4(1,1,0,0), Vector4(1,0,0,0), } //Right
     {   Vector4(0,1,1,0), Vector4(0,0,1,0), Vector4(1,1,1,0), Vector4(1,0,1,0), } //Front
     {   Vector4(1,1,0,0), Vector4(1,0,0,0), Vector4(0,1,0,0), Vector4(0,0,0,0), } //Back
     {   Vector4(0,1,0,0), Vector4(0,1,1,0), Vector4(1,1,0,0), Vector4(1,1,1,0), } //Top
     {   Vector4(1,0,0,0), Vector4(1,0,1,0), Vector4(0,0,0,0), Vector4(0,0,1,0), } //Bottom
};
 
Instead of dumping a million copies of this information in a vertex buffer, it is stored in a uniform array used by the geometry shader. Texture coordinates can be hard coded in the shader (just be sure to always start with the top left vertex of each face). I'm reducing the shader code to position and texture coordinates.
 
void EmitFaceVertex(vec4 pos, vec3 uvt)
{
    gl_Position = mvp * pos;
    texCoord = uvt;
    EmitVertex();
}
 
void main() 
{
    ivec4 pos = position[0];
    int textureID = pos.w;
    int face = pos.z;
 
    pos = ivec4(chunkPos, 0) + ivec4(pos.x >> 4, pos.y, pos.x & 0xF, 1);
    
    EmitFaceVertex( pos + faceCoords[face][0], vec3(0, 0, textureID) );
    EmitFaceVertex( pos + faceCoords[face][1], vec3(0, 1, textureID) );
    EmitFaceVertex( pos + faceCoords[face][2], vec3(1, 0, textureID) );
    EmitFaceVertex( pos + faceCoords[face][3], vec3(1, 1, textureID) );
 
    EndPrimitive();
}
 
The chunk position is a uniform shader variable set once before drawing a chunk.
Above code is sticking to the MC chunk size of 16x16 cubes, which means the cubes x and z position relative to the chunk position fits in 4bits each. The y coordinate gets a whole byte (for 256 possible positions).
 
The actual "vertex buffer" stores "vertices" as consisting of 4 bytes. "x" contains the x and z position (hence the bit fiddling), "y" is actually y, z contains the index of the face to draw (0 = left, 1 = right, ...) and w is the textures index in the atlas.
 
 
You can minimize this even more, if you don't allow different textures for different sides of the cube. Then you don't send z as the index of the side to draw, but instead use one bit for each side. The shader then just iterates from 0-6, checks if that bit is set and then outputs the appropriate quad.
 
for (i = 0; i < 6; ++i)
{
    if (pos.z & (1<<i))
    {
        EmitFaceVertex( pos + faceCoords[i][0], vec3(0, 0, textureID) );
        EmitFaceVertex( pos + faceCoords[i][1], vec3(0, 1, textureID) );
        EmitFaceVertex( pos + faceCoords[i][2], vec3(1, 0, textureID) );
        EmitFaceVertex( pos + faceCoords[i][3], vec3(1, 1, textureID) );
 
        EndPrimitive();
    }
}
 
That way you can squeeze an entire cube into 32bits.
 
However, if the cubes are the result of a simple height map, you will probably want to use bigger chunks.
Edited by Trienco
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0