Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GLSL Shaders texture alpha splatting

I'm trying to do shader used to load four textures detail tiles on a terrain by merging them according a a fifth image where r,g,b and a components are used to determine how much of each texture should be blended together. The blending works fine, but when I try adding my "mixmap" image, it fails, because of a problem with texture coordinates I guess.

First, here are the shaders:
Vertex shader

void main() {
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
}

Fragment Shader

uniform sampler2D Texture0;
uniform sampler2D Texture1;
uniform sampler2D Texture2;
uniform sampler2D Texture3;
uniform sampler2D Mixmap;

varying vec3 pos;

void main()
{

    vec4 texel0 = texture2D(Texture0, gl_TexCoord[0].st).rgba;
    vec4 texel1 = texture2D(Texture1, gl_TexCoord[0].st).rgba;
    vec4 texel2 = texture2D(Texture2, gl_TexCoord[0].st).rgba;
    vec4 texel3 = texture2D(Texture3, gl_TexCoord[0].st).rgba;
    vec4 mixmapTexel = texture2D(Mixmap, gl_TexCoord[0].st).rgba;

    texel0 *= mixmapTexel.r;
    texel1 = mix(texel0,  texel1, mixmapTexel.g);
    texel2 = mix(texel1, texel2, mixmapTexel.b);
    gl_FragColor = mix(texel2, texel3, mixmapTexel.a);
}

As I said the blending works fine. The problem comes from the fact that the values read from my mixmap aren't the right ones.

Here is some more explanation on what I'm doing. I'm building a paging terrain system loading the terrain from heightmaps. Then I want to use my mixmap image to represent with the rgba components how much of each texture should be blended according to height.

  • r is water

  • g is sand

  • b is grass

  • a is rock

So I need to be able to get the correct pixel value from my shader to correctly blend my textures.

Blending between crate and text

Here is an example of blending between a crate and a text, so that you can clearly see how the texture is applied.

Now if I use a simple mixmap image (half red, half green), which should give me the crates on the left side of the terrain, and the text on the right, I get with argb alpha splattingexample rgba image

Here is part of the terrain generation process: it iterates through an array of vertices and create the terrain triangles

void TerrainPage::generateDisplayList()
{
    // create one display list
    mDisplayListIndex = glGenLists(1);

    // compile the display list, store a triangle in it
    glNewList(mDisplayListIndex, GL_COMPILE);
    glFrontFace( GL_CW  ); //   Vertex are added clockwise. Used to calculate normals
    std::vector<Vertex>::iterator it;
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE);
    texShader.enable();
    texShader.bindTexture(mTexture0, "Texture0", 0);
    texShader.bindTexture(mTexture1, "Texture1", 1);
    texShader.bindTexture(mTexture2, "Texture2", 2);
    texShader.bindTexture(mTexture3, "Texture3", 3);
    texShader.bindTexture(mMixmapTexture, "Mixmap", 4);
    Vertex v;
    int j=0;
    glEnable(GL_TEXTURE_2D);
    //mTexture.bind();
    glBegin(GL_TRIANGLE_STRIP);
    for(int i = 0; i<mVertices.size(); i++) {
        if(i%(2*mWidth) == 0) glEnd(); glBegin(GL_TRIANGLE_STRIP);
        v = mVertices[i];
        glTexCoord2f(v.texcoords[0], v.texcoords[1]);
        glVertex3f(v.position[0], v.position[1], v.position[2]);
    }
    glEnd();
    glDisable(GL_TEXTURE_2D);
    texShader.disable();
    glEndList();
}

If needed, I can provide more screenshots, some of my code too.

As a follow-on to the answer provided, I tried to do it by calculating the UV in the shader.

First, here is the new shader

Vertex shader

varying vec4 VertexPosition;

void main() {

    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
    VertexPosition = gl_ModelViewMatrix * gl_Vertex;;
}

Fragment shader

uniform sampler2D Texture0;
uniform sampler2D Texture1;
uniform sampler2D Texture2;
uniform sampler2D Texture3;
uniform sampler2D Mixmap;

varying vec4 VertexPosition;
float side = 500.;

void main()
{

   vec4 texel0 = texture2D(Texture0, gl_TexCoord[0].st).rgba;
   vec4 texel1 = texture2D(Texture1, gl_TexCoord[0].st).rgba;
   vec4 texel2 = texture2D(Texture2, gl_TexCoord[0].st).rgba;
   vec4 texel3 = texture2D(Texture3, gl_TexCoord[0].st).rgba;
   vec4 mixmapTexel = texture2D(Mixmap, VertexPosition.xz/(2.*side)).rgba;

   texel0 *= mixmapTexel.r;
   texel1 = mix(texel0,  texel1, mixmapTexel.g);
   //texel2 = mix(texel1, texel2, mixmapTexel.b);
   //vec4 tx  = mix(texel2, texel3, mixmapTexel.a);
   //vec4 tx = mixmapTexel; //vec4(1, 1, 1, 1.);
    gl_FragColor = texel1;

    //if(test > 250. )
    //    gl_FragColor = vec4(1.,1.,1.,1.);
}

And here are the results

enter image description here

But if I move the camera:

enter image description here

As you can see the crate and text are side by side this time. But it looks like I'm computing in screen coordinates instead of world coordinates. I must have gotten confused again with the coordinate system. I'll try and find the right one! I just want to make sure that I'm in the right direction. I'll look for the multi texture coordinates too, it might be more convenient once I figured out how it works ;)

like image 811
geenux Avatar asked Nov 17 '12 19:11

geenux


1 Answers

If I'm understanding you correctly, you'll need different texture coords for your mixmap. Right now it's using the the entire mixmap per cell which is why you get half text, half crate per cell.

Try using multiple texcoords. You can also calculate the correct UV in the shader by having it by the current x pos of the vertex divided by the length of the terrain in the x direction and same for y. If you scale you're terrain though, be sure to multiply the length of the terrain by the scale as well.

If you don't see what I'm saying, every cell of the terrain is getting UVs of either: (0,0), (1,0), (0,1), or (1,1). This is fine for the crate and text texture but the mixmap should be much more fractional and diverse.

Here is a link to another terrain tutorial. It uses DirectX, but the vertex/UV generation is comparable. The biggest difference is that he uses one big texture for the entire terrain which is what you need to be doing for your mixmap but not for your other textures.

I think I figured out whats wrong. Store the Vertex position explicitly. Don't multiply it by the modelviewmatrix ie:

varying vec4 VertexPosition;

void main() {

    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
    //VertexPosition = gl_ModelViewMatrix * gl_Vertex;;
    VertexPosition = gl_Vertex;
}

This will fix it, sorry I threw your fixed shader into rendermonkey and it no longer moves with the camera. Hope this helps. I was getting the panning but now it looks like:

Fixed render

like image 197
zero298 Avatar answered Sep 27 '22 19:09

zero298