Reputation: 720
I am trying to use a 2D texture array as a texture atlas. I have the following code to render a single square. The square renders if I replace the texture lookup in the fragment shader with a simple colour output, and it renders one of the textures properly if I use and set up a simple 2D texture instead of a 2d Texture Array. However, when trying to create and use the 2d Texture array, I simply get a black screen. What gives?
//Shader Sources
const GLchar* vertexsource =
"#version 330\n"
"in vec2 position;"
"in vec2 texcoord;"
"out vec2 f_texcoord;"
"void main() {"
" gl_Position = vec4(position, 0.0, 1.0);"
" f_texcoord = texcoord;"
"}";
const GLchar* fragmentsource =
"#version 330\n"
"in vec2 f_texcoord;"
"out vec4 color;"
"uniform float time;"
"uniform sampler2DArray sample;"
"void main() {"
" float layer = step(sin(time), 0.0);"
" color = texture(sample, vec3(f_texcoord, 0.0));\n"
" //color = vec4(1.0,1.0,1.0,1.0);\n"
"}";
//Vertex Data
GLfloat vertices[] = {
//Verts Texture verts
-0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.0f, 1.0f
};
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
Shaders::SetLogger(log_);
VertexShader vertexshader("D3 Vertex Shader", vertexsource);
FragmentShader fragshader("D3 Frag Shader", fragmentsource);
program = new ShaderProgram("D3 Shader Program");
program->AttachShader(vertexshader);
program->AttachShader(fragshader);
program->LinkProgram();
glUseProgram(program->glIdentifier());
GLint posattrib = program->GetAttributeLocation("position");
GLint texattrib = program->GetAttributeLocation("texcoord");
//Dont actually need this because only one Texture unit in use
GLint coloruniform = program->GetUniformLocation("sample");
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
glEnableVertexAttribArray(posattrib);
glVertexAttribPointer(posattrib, 2, GL_FLOAT, GL_FALSE,
4 * sizeof(GLfloat),
(GLvoid*)0);
glEnableVertexAttribArray(texattrib);
glVertexAttribPointer(texattrib, 2, GL_FLOAT, GL_FALSE,
4 * sizeof(GLfloat),
(GLvoid*)(2 * sizeof(GLfloat)));
//glUniform1i(coloruniform, 0);
int width, height;
unsigned char* image = SOIL_load_image("sample.png", &width, &height,
0, SOIL_LOAD_RGBA);
int width2, height2;
unsigned char* image2 = SOIL_load_image("sample2.png", &width2, &height2,
0, SOIL_LOAD_RGBA);
errorstream << glGetError() << " ";
glActiveTexture(GL_TEXTURE0);
GLuint texture;
glGenTextures(1, &texture);
errorstream << glGetError() << " ";
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
errorstream << glGetError() << " ";
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA,
width, height, 2,
0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)0);
errorstream << glGetError() << " ";
log_->Debug("Requested memory for texture");
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0,
0, 0, 0,
width, height, 1,
GL_RGBA, GL_UNSIGNED_BYTE, image);
errorstream << glGetError() << " ";
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0,
0, 0, 1,
width2, height2, 1,
GL_RGBA, GL_UNSIGNED_BYTE, image2);
errorstream << glGetError() << " ";
glUniform1i(coloruniform, 0);
errorstream << glGetError() << " ";
log_->Debug(errorstream.str());
And the actual drawing code:
GLint timeuniform = program->GetUniformLocation("time");
GLint sampleuniform = program->GetUniformLocation("sample");
glActiveTexture(GL_TEXTURE0);
glUniform1f(timeuniform, frametimer_.RunningTime().asSeconds());
glUniform1i(sampleuniform, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
window_->display();
Edit: Edited in suggested changes suggested by jozxyqk
Upvotes: 2
Views: 2597
Reputation: 17256
[EDIT] Possible causes:
The texture/sampler isn't bound correctly (see below).
The texture is incomplete (also see this).
The common issue is GL expects mipmaps by default (and most tutorials/example code start without mipmaps). A simple fix is: glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
For details, see GL4.5 specs, sec 8.17 "Texture Completeness ".
A texture is said to be complete if all the texture images and texture parameters required to utilize the texture for texture application are consistently defined.
...
Using the preceding definitions, a texture is complete unless an of the following conditions hold true:
- The minification filter requires a mipmap (is neither NEAREST nor LINEAR), and the texture is not mipmap complete.
The texture coordinates are wrong (e.g. all zero and the first texel happens to be black). Easily verifiable by using the coords for pixel colour.
The above is working and the data in the image is just black.
To bind a texture,
Call glActiveTexture
, with GL_TEXTURE0 + texIndex
This isn't needed during setup, but selects the texture unit to bind to before drawing.
https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glActiveTexture.xml
Call glBindTexture
This should be after glActiveTexture
(a related post on opengl.org)
Set the sampler uniform glUniform1i(samplerLocation, texIndex)
← the main issue, I think
texIndex
is 0
for GL_TEXTURE0
, 1
for GL_TEXTURE1
etc. Using GL_TEXTURE0 + texIndex
is a handy shortcut.
Your code uses GL_TEXTURE0
instead of the index, which is 0x84C0
or 33984
.
Upvotes: 3