Texture artifacts when rendering multiple textures on a single mesh

Started by
4 comments, last by Digitalfragment 7 years, 4 months ago

Hi,

I'm trying to render multiple textures on a single mesh. What i'm doing is binding three different textures using texture units and i'm adding those textureIDs to my vertex buffer. So my vertex buffer looks something like this


                [vec3           , vec4        , vec2    , vec3  , float    ]
Vertex Buffer = [vertex position, vertex color, texcoord, normal, textureID]

and in my pixel shader I have an array of texture samplers and I render the correct texture using textureID as an index to the array.

This works great, my only problem is that i'm getting some texture artifacting. From the looks of it some textures are partly being drawn on top of other textures.

I don't know if this a good way of drawing multiple textures on a mesh. If you guys know a more efficient way of doing this I would love to know about it.

here is how I draw everything


void init()
{
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, size, data, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

//--------

void Draw()
{

//I think the problem is this block of code 
//----- 
    glActiveTexture(GL_TEXTURE0 + 0); 
   glBindTexture(GL_TEXTURE_2D, texture1); 
    glUniform1i(glGetUniformLocation(glshader->GetProgramID(), "textures[0]"), 0); 

    glActiveTexture(GL_TEXTURE0 + 1); 
    glBindTexture(GL_TEXTURE_2D, texture2); 
    glUniform1i(glGetUniformLocation(glshader->GetProgramID(), "textures[1]"), 1); 

    glActiveTexture(GL_TEXTURE0 + 2); 
    glBindTexture(GL_TEXTURE_2D, texture3); 
    glUniform1i(glGetUniformLocation(glshader->GetProgramID(), "textures[2]"), 2); 
//----- 

    vertexBuffer->Bind(); 
    indexBuffer->Bind(); 

    glDrawElements(GL_TRIANGLES, indexBuffer->GetIndicesCount(), GL_UNSIGNED_INT, nullptr); 

    indexBuffer->Unbind(); 
    vertexBuffer->Unbind();
}

here is my vertex shader


#version 450 core
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec4 inColor;
layout(location = 2) in vec2 inUV;
layout(location = 3) in vec3 inNormals;
layout(location = 4) in float inTextureID;

struct data
{
	vec2 UV;
	float textureID;
};

out data shared_data;

void main()
{
	shared_data.UV = inUV;
	shared_data.textureID = inTextureID;
}

here is my pixel shader


#version 450 core

struct data
{
	vec2 UV;
	float textureID;
};

in data shared_data;

out vec4 color;
uniform sampler2D textures[3];

void main() 
{
	int texID = int(shared_data.textureID);
	color = texture(textures[texID], shared_data.UV);
}
Advertisement

man..... I can't believe I wasted all day just to solve this problem.

adding this code in my pixel shader fixed the issue.


if(texID == 0)
	color = texture(textures[0], shared_data.UV);
if(texID == 1)
	color = texture(textures[1], shared_data.UV);
if(texID == 2)
	color = texture(textures[2], shared_data.UV);

I don't understand why the above code works but this creates artifacts....


color = texture(textures[texID], shared_data.UV);

they are basically the same thing. aren't they?

My OpenGL is pretty rusty, but while they look the same, the fix is closer to what the hardware will be doing which is pretty poor for performance. This is due to the fact that you aren't supposed to dynamic index across uniforms. The fact that it worked at all surprises me, the artifact you are seeing is probably wavefronts that are spanning two textures bugging out. But instead your fix will actually sample all 3 textures and then discard the 2 that it doesnt need. Shaders don't really branch in the traditional cpu sense, but execute all paths. In DX you can force a branch using [branch] before the if, i'm not sure what the GL equivelant is.

To take this approach 'the 'fast way', you need to instead use "Array Textures" https://www.opengl.org/wiki/Array_Texture
This basically lets you use a vec3 texture coordinate where z maps to the array element index.

I'm not sure OpenGL/GLSL allows for dynamic indexing for arrays. See specification for GLSL:

Variables of the same type can be aggregated into arrays by declaring a name followed by brackets ( [ ] ) enclosing an optional size. When an array size is specified in a declaration, it must be an integral constant expression (see section 4.3.3 “Constant Expressions”) greater than zero.

Although theoretically using Shader Storage Buffer Objects - https://www.opengl.org/wiki/Shader_Storage_Buffer_Object. I hit the same problem multiple times already, and luckily in D3D12 with Shader Model 5.1 you can dynamically index into array. Of course OpenCL/CUDA also allows for this, but they are not really suited for writing shaders (sigh... interop... sigh...).

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

Well my engine supports both DirectX 11 and OpenGL 4+. So if you have advice for either APIs that would be great.

I will check out Array Textures and see if I can make it work. What is the equivalent of that in D3D11?

I did read somewhere that Array Textures in OpenGL is evil. I forgot why though. That was a long time ago.

Its Texture Arrays in directx instead. The setup isn't too much different to a standard Texture2D - its an extra parameter "ArraySize" on the TEXTURE2D_DESC when creating the ID3D11Texture2D
The limitation with these is that all textures must be the same resolution.

This topic is closed to new replies.

Advertisement