2
\$\begingroup\$

Tiled terrain. User wants to build a structure. He presses build and for each tile there is an "allow" or "disallow" tile sprite added to the scene. FPS drops right away, since there are 600+ tiles added to the screen. Since map equals screen, there is no scrolling. I came to an idea to make an allow grid covering the whole map and mask the disallow fields.

Approach 1:

  • Create allow and disallow grid textures.
  • Draw a polygon on screen.
  • Pass both textures to the fragment shader.
  • Determine the position inside the polygon and use color from allowTexture if the fragment belongs to the allow field, disallow otherwise

Problem: How do I know if I'm on the field that isn't allowed if I cannot pass the matrix representing the map (enum FieldStatus[][] (Allow / Disallow)) to the shader? Therefore, inside the shader I don't know which fragments should be masked.

Approach 2:

  • Create allow texture.
  • Create an empty texture buffer same size as the allow texture
  • Memset the pixels of the empty texture to desired color for each pixel that doesn't allow building.
  • Draw a polygon on screen.
  • Pass both textures to the fragment shader.
  • Use texture2 color if alpha > 0, texture1 color otherwise.

Problem: I'm not sure what is the right way to manipulate pixels on a texture. Do I just make a buffer with width*height*4 size and memcpy the color[] to desired coordinates or is there anything else to it? Would I have to call glTexImage2D after every change to the texture?

Another problem with this approach is that it takes a lot more work to get a prettier effect since I'm manipulating the color pixels instead of just masking two textures.

varying vec2 TexCoordOut;

uniform sampler2D Texture1;
uniform sampler2D Texture2;

void main(void){
    vec4 allowColor = texture2D(Texture1, TexCoordOut);
    vec4 disallowColor = texture2D(Texture2, TexCoordOut);

if(disallowColor.a > 0){
    gl_FragColor= disallowColor;
}else{
    gl_FragColor= allowColor;
}}

enter image description here

I'm working with OpenGL on Windows. Any other suggestion is welcome.

\$\endgroup\$
  • \$\begingroup\$ If each tile is as big as a pixel, you could just render points and use disjoint sets. Have you tried this? \$\endgroup\$ – Mokosha Nov 3 '13 at 20:24
  • \$\begingroup\$ Tiles are 32x32 pixels. \$\endgroup\$ – user1304844 Nov 3 '13 at 21:53
1
\$\begingroup\$

I believe both your approaches are pretty solid.
With your first approach :

  • Create allow and disallow grid textures.
  • Create the array representing the map
  • Draw a polygon on screen.
  • Pass both textures to the fragment shader.
  • Determine the position inside the polygon and use color from allowTexture if the fragment belongs to the allow field, disallow otherwise

You create an array of your map with size = width * height of the matrix and pass it into the shader as an array of booleans using glUniform1iv ( or safer ints because the driver will first convert your boolean into int and then into boolean in the shader ). But with this approach you have to set the maximum size of the array inside the shader ( uniform int map[320*320] ), because GLSL does not allow the creation of an array of unknown size, and pass the current size of the array as a uniform.

With your second approach :

  • Create allow and disallow grid textures.
  • Create an empty texture buffer same size as the number of tiles
  • Memset the pixels of the empty texture to desired value for each tile that doesn't allow building.
  • Draw a polygon on screen.
  • Pass textures to the fragment shader.
  • Use allowTexture color if maskTexture.r > 0.0 or disallowTexture color otherwise.

You create the texture buffer with the size of width * height and set each pixel with the desired value. Then you create the texture with a format which has one channel ( GL_R8 or GL_R8I for example ) and every time you update it with glSubTexImage2D. Keep in mind that you should set the filtering to NEAREST or use textureFetch, otherwise the texture fetch will give you incorrect results.

An important thing to note, for both approached, is that you should update the buffers only when there is a change in the allow/Disallow map.

In my personal opinion, i will go with the second approach since it does not bound me to a maximum size of the map.

\$\endgroup\$
  • \$\begingroup\$ I like both solutions. The solution to first approach sounds simple and that is what I initially had in mind, except that I didn't know that I can pass an array of predefined size to Shader. I thought I'm stuck with vec2/3/4 and mat2/3/4 data structures. Is there an example of passing an array to the shader? Regarding the second approach, I'd never think of using w*h buffer and NEAREST filtering to mask the tile. Just awesome. \$\endgroup\$ – user1304844 Nov 5 '13 at 9:42
  • 1
    \$\begingroup\$ You can declare the uniform in the shader with 'uniform int mask[320*320];' and then you pass it from your engine with 'glUniform1iv(mask_location, 320*320, mask_map);' where mask_map is the map in your engine. The 'v' at the end of glUniform means that you pass an array of that type. ( in our case an array of integers ). \$\endgroup\$ – Anastasios G Nov 5 '13 at 12:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.