PDA

View Full Version : OpenGL drawing 500k triangles



dex3rdx
03-13-2014, 10:04 AM
I want to render big, izometric map. I am using OpenTK and aiming for OpenGL 3.3. Perfect option would be to draw 512x512 map of tiles, which is 262144 blocks -> 524288 triangles. The map is divided into chunks, mostly static but animations would be nice. I was trying to do this in some ways already:
1. Using Geometry Shader to generate chunk. I know its not what GS is for, but it seems to be fast. And it was. Problem is, that i have to send all blocks data to GPU every frame, becouse there is no way I can think of to buffer this on GPU (Uniform Buffers have very restricted limits for reasons I dont understand so I can't use it). That created a bottleneck on dedicated GPUs, althought with integrated GPUs for obvious reasons it worked well.

2. Calculating position of every block once, store it on VBO (UVs and Vertices) and than draw with BindVertexArray and DrawArrays. The problem was for 256x256 map integrated GPU was using >1500 MB for some reason and was giving me Out of memory. Same for 512x512 with dedicated. According with my calculations it should take only 4 MB (524288 floats) but it didn't. Even for small numbers like 144x144 blocks it was drawing with ~20FPS...

3. (Done only early, with MonoGame) Render each Chunk to texture (what with FBO on OpenGL?) and than draw needed textures to the screen. But that is ridiculous memory wise and wont work for higher textures resolution.

At this moment Im out of ideas, searching web didnt get me any reasonable solution. I wanted to see how its done in OpenTTD since its open source (its doing exactly what I need), but I have not understand much from its bizzare and complicated code.

Sorry for bad English, it's not my first language.

Brokenmind
03-13-2014, 10:22 AM
I'm afraid I don't understand some of your points, but I've done something like this recently and I'll just describe the procedure here, you could then point out where your approach differs from mine:

Store big isometric map in a texture (512x512 = 262144 floats)
Create VBO of a 32x32-triangles chunk
Collect instancing information to draw the whole area with chunks, with overly large chunks in the background and small, detailed chunks in the foreground
Each chunk draws only the portion of the large map that is assigned to it

An image clarifying the precision and dimension of the chunks can be found here (http://s14.directupload.net/images/user/131101/qbgnqrid.png).

As you stated, the uniform buffers have a very restricted capacity. In my software, I fill a texture in each frame which allows me to surpass this restriction. Each pixel is then interpreted as the information necessary for one chunk, which could be its position (x, y) and its size (x, y).

dex3rdx
03-13-2014, 11:33 AM
I should to add very importand information: its 2D isometric game. I thought its obvious since its isometric but I see not.



Create VBO of a 32x32-triangles chunk
In my software, I fill a texture in each frame which allows me to surpass this restriction. Each pixel is then interpreted as the information necessary for one chunk, which could be its position (x, y) and its size (x, y).

Thanks for your reply. I have some questions:
1. Where are you "interpreting" information from that texture? In geometry shader?
2. If so, why you need VBO of triangles and how do you retive information from texture ( texture2D method?). If not, why are you creating that texture?

Brokenmind
03-13-2014, 12:50 PM
I misinterpreted the isometric keyword... but could you add a screenshot of what you have achieved so far?

The information are interpreted in the vertex shader.
Let's say for simplicity that the chunk consists of only one quad. The instancing information now says: position (x, y) = (0.5, 0.5), size (x, y) = (0.25, 0.25). The quad will then have the coordinates (0.5, 0.5), (0.5, 0.75), (0.75, 0.75), (0.75, 0.5). If these are at the same time your texture coordinates, you simply access the texture via texture(samplerTexture, textureCoordinate) and assign the values you need to the quad (in my case, the texture is a heightmap). For chunks of 32x32x2 triangles, the whole procedure is the same, only that the values must be interpolated in 1/32*size steps and so on.

dex3rdx
03-13-2014, 01:00 PM
Here is some code to help you understand my case:


private static float[] VDataBlock = new float[] {
-1.0f, 0.0f, // 0
1.0f, 1.0f, // 2
1.0f, -1.0f, // 4
3.0f, 0.0f // 6
};


public class NChunk
{
public float[] vertices = new float[CHUNK_SIZE * CHUNK_SIZE * 8];
public float[] UVs = new float[CHUNK_SIZE * CHUNK_SIZE * 8];
public int[] buffers = new int[CHUNK_SIZE * CHUNK_SIZE];

public NChunk()
{
float x, y;
for (int k = 0; k < CHUNK_SIZE; k++)
{
for (int l = 0; l < CHUNK_SIZE; l++)
{
x = (l - k) * 1.939f;
y = (l + k) * 0.969f;

int w = (k * 8) + (l * 8 * CHUNK_SIZE);

vertices[w + 0] = VDataBlock[0] + x;
vertices[w + 1] = VDataBlock[1] + y;
vertices[w + 2] = VDataBlock[2] + x;
vertices[w + 3] = VDataBlock[3] + y;
vertices[w + 4] = VDataBlock[4] + x;
vertices[w + 5] = VDataBlock[5] + y;
vertices[w + 6] = VDataBlock[6] + x;
vertices[w + 7] = VDataBlock[7] + y;

UVs[w + 0] = 0.0f;
UVs[w + 1] = 0.5f;
UVs[w + 2] = 0.5f;
UVs[w + 3] = 1.0f;
UVs[w + 4] = 0.5f;
UVs[w + 5] = 0.0f;
UVs[w + 6] = 1.0f;
UVs[w + 7] = 0.5f;

int bh = GL.GenVertexArray();
GL.BindVertexArray(bh);
GL.EnableVertexAttribArray(0);
GL.EnableVertexAttribArray(1);

Util.PrintGLError();
int wvbh = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, wvbh);
float[] blockVertices = new float[8];
Array.Copy(vertices, w, blockVertices, 0, 8);
GL.BufferData<float>(BufferTarget.ArrayBuffer, new IntPtr(sizeof(float) * 8), blockVertices, BufferUsageHint.StaticDraw);
Util.PrintGLError();
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, IntPtr.Zero);
Util.PrintGLError();

int wuvspb = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, wuvspb);
float[] blockUVs = new float[8];
Array.Copy(UVs, w, blockUVs, 0, 8);
GL.BufferData<float>(BufferTarget.ArrayBuffer, new IntPtr(sizeof(float) * 8), blockUVs, BufferUsageHint.StaticDraw);
Util.PrintGLError();
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 0, IntPtr.Zero);
Util.PrintGLError();



buffers[k * CHUNK_SIZE + l] = bh;
}
}
}
}


private static void RenderChunk(NChunk c)
{
for (int k = 0; k < CHUNK_SIZE; k++)
{
for (int l = 0; l < CHUNK_SIZE; l++)
{
GL.BindVertexArray(c.buffers[k * CHUNK_SIZE + l]);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
}
}

I wanted to store in UBO pretty much fields from NChunk, than draw it very fast with GS, not with DrawArrays. For some reason this try with VBO is very slow.

SS of 144x144 map (biggest that will fit on integrated GPU - Intel HD 4000, with CHUNK_SIZE = 16 and code as above). It runs at 20 FPS. Whitle lines becouse of problem with mipmaps/linear interp. and alpha blending.
http://scr.hu/0w1v/fbw3n
http://scr.hu/0w1v/frgne