In a recent thread I mentioned/came up with a method of drawing tiles without needing a quad for each tile to avoid a vertex bottleneck by using a fragment shader. I've implemented it.

The whole map in this test program is 2048x2048 tiles, and they are all drawn as a single quad (no culling on the CPU). Zooming out so that all tiles are visible, FPS drops to around 450-500 as the texture cache won't be able to do its magic. However, in that case tiles are smaller than screen pixels, and the whole map just looks like randomly colored pixels.
The renderer is not limited to pure 2D rendering in any way. It's possible to just rotate and scale the rendered quad to achieve lots of effects. For example, isometric tiles:

It uses an RGB texture to keep tile indices per tile and a 2D texture array for tiles to prevent bleeding between tiles when linear filtering is used. The most important part is the fragment shader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #extension GL_EXT_texture_array : enable
uniform vec2 mapSize;
uniform sampler2D tileTexture; uniform sampler2DArray tilesetTexture;
void main() { vec2 texCoords = gl_TexCoord[0].st; vec2 tileResult = texture2D(tileTexture, texCoords / mapSize).rg; float tile = dot(tileResult, vec2(65535, 256.0)); gl_FragColor = texture2DArray(tilesetTexture, vec3(fract(texCoords), tile)); } |
which calculates the tile from 2 color channels and then looks up the tile in the texture array. This could be implemented in OpenGL 2 too by using a pure tile set, but it would have severe bleeding between bordering tiles when using filtering. However, mipmaps do not work as they produce weird 2 pixel seams between tiles for me.
Here is the Java and GLSL shader source, plus a test tileset:
http://www.mediafire.com/?flc36t8uq6floaw You need the LWJGL jars in the classpath and the natives directory in the VM commands. It also needs OpenGL 3 to run, but it is possible to work around this.
I hope someone finds it useful!