0

I'm using LWJGL to draw "tiles", or textured 2D squares on the screen. However, the texture coordinate is always (0, 0) and therefore the textured square only uses the first pixel colour to fill it.

This is my vertex shader:

#version 330 core

in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;

out vec4 pass_Color;
out vec2 pass_TextureCoord;

void main(void) {
    gl_Position = in_Position;

    pass_Color = in_Color;
    pass_TextureCoord = in_TextureCoord;
} 

And this is my fragment shader:

#version 330 core

uniform sampler2D texture_diffuse;

in vec4 pass_Color;
in vec2 pass_TextureCoord;

out vec4 out_Color;

void main(void) {
    out_Color = pass_Color;
    // Override out_Color with our texture pixel
    out_Color = texture(texture_diffuse, pass_TextureCoord);
} 

And this is essentially the code I'm using to draw the square:

ARBShaderObjects.glUseProgramObjectARB(shaderProgram);  

glBindTexture(GL_TEXTURE_2D, sprite.getId());

glBegin(GL11.GL_QUADS); 
glVertex2d(screenMinX, screenMinY);
glTexCoord2d(0.0, 0.0);
glVertex2d(screenMaxX, screenMinY);
glTexCoord2d(1.0, 0.0);
glVertex2d(screenMaxX, screenMaxY);
glTexCoord2d(1.0, 1.0);
glVertex2d(screenMinX, screenMaxY);
glTexCoord2d(0.0, 1.0);
glEnd();

// release the shader
ARBShaderObjects.glUseProgramObjectARB(0);

I cannot fix it because I don't know how the above code works in the first place. I have not told the shaders what in_Position, in_Color or in_TextureCoord are but the first two seem to work just fine. It is in_TextureCoord, which is eventually passed to the fragment shader, that seems to have a constant value of (0, 0) - I have determined that by setting the output colour of the fragment shader to have one of the channels equal to the X-coordinate of the texture coordinate. It remained a solid colour throughout the square, indicating that there was no change in texture coordinate.

The square produced with the code above should be textured but instead is painted a solid colour - the first pixel of the texture given. How can I change the code to make the texture coordinate change accordingly? If I appear to have some misunderstanding to how this all fits together, please correct me.

This is the tutorial I used to try to accomplish the above.

p.s. I am aware that the Java snippet is using deprecated immediate-mode, but I don't know how to use glDrawArrays or any other commonly suggested method to accomplish the same. Could you help me to change this?

curz46
  • 11
  • 4
  • "And this is essentially the code I'm using to draw the square:" Well you never set any texture coordinates so why do you expect them to be anything other than 0, 0? – tkausl Aug 17 '17 at 16:15
  • @tkausl How can I set the texture coordinates for each individual pixel? It confuses me since OpenGL appears to handle pixels internally. – curz46 Aug 17 '17 at 16:19

2 Answers2

3

I am aware that the Java snippet is using deprecated immediate-mode, but I don't know how to use glDrawArrays or any other commonly suggested method to accomplish the same. Could you help me to change this?

Since you do not need the attribute in_Color anymore, you have to delete the attribute from the vertex shader (and of course also pass_Color from the vertex shader and the fragment shader).
Otherwise, you have to expand my solution logically by the color attribute.

Set up an array for the vertex positions an for the texture coordinates:

float[] posData = {
    screenMinX, screenMinY, 0.0, 1.0,
    screenMaxX, screenMinY, 0.0, 1.0,
    screenMaxX, screenMaxY, 0.0, 1.0,
    screenMinX, screenMaxY, 0.0, 1.0 };

float[] texData = { 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 };

Generate a vertex array object:

int  vaoObj = glGenVertexArrays();
glBindVertexArray(vaoObj);

Generate array buffers for the vertices and texture coordinates, enable the attribute indices and associate them buffers to the attribute indices:

FloatBuffer posBuffer = MemoryUtil.memAllocFloat(posData.length);
posBuffer.put(posData).flip();
FloatBuffer texBuffer = MemoryUtil.memAllocFloat(texData.length);
texBuffer.put(texData).flip();

int vboPosObj = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboPosObj);
glBufferData(GL_ARRAY_BUFFER, posBuffer, GL_STATIC_DRAW);

// index 0 to associate with "in_Position"            
glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, 0); 
glEnableVertexAttribArray(0); // 0 = attribute index of "in_Position"

int vboTexObj = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboTexObj);
glBufferData(GL_ARRAY_BUFFER, texBuffer, GL_STATIC_DRAW);

// index 0 to associate with "in_TextureCoord"
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(1); // 1 = attribute index of "in_TextureCoord"

Release the vertex array object:

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);


You have to specify the attribute indices of the attributes in_Position and in_TextureCoord.

Either you use explicit layout specifications in the vertex shader:

layout (location = 0) in vec4 in_Position;
layout (location = 1) in vec2 in_TextureCoord;

Or you specify the attribute indices in the shader program, right before you link the shader program (glLinkProgram).

glBindAttribLocation(shaderProgramID, 0, "in_Position");
glBindAttribLocation(shaderProgramID, 1, "in_TextureCoord");


If the object is to be drawn, it is sufficient to bind the Vertex Array Object:

glBindVertexArray(vaoObj);
glDrawArrays(GL_QUADS, 0, 4); // 4 = number of vertices 
glBindVertexArray(0);

Note, if a buffer objects or a vertex array object is not further used, it has to be deleted, to prevent memory leaks. Buffer objects are deleted by glDeleteBuffers and vertex array objects are deleted by glDeleteVertexArrays. Buffer objects are not "created under" vertex array objects, it is not sufficient to delete the vertex array object only (see OpenGL Vertex Array/Buffer Objects)

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thanks for replying. I have tried this before but it achieves no change. This is the case as well now. – curz46 Aug 17 '17 at 16:21
  • Thank you very much. This solved my issue, although I had to remove two instances of `glBindVertexArray(0)` in the large code block (moving one below the `glDrawArrays` call) in order to prevent an `EXCEPTION_ACCESS_VIOLATION` on `glDrawArrays`. I also had to change `GL_TRIANGLES` to `GL_QUADS` to have it draw squares as it did originally. I think this answer will be my go-to resource for rendering in future. – curz46 Aug 17 '17 at 18:02
  • You could look into this [Book - OpenGL Programming Guide 8th Edition](https://www.ics.uci.edu/~gopi/CS211B/opengl_programming_guide_8th_edition.pdf). It describes OpenGL in detail. Also, what you are trying to do, is discussed directly in the first chapter. – bitQUAKE Aug 18 '17 at 07:49
  • @Rabbid76 something I think you should include in your answer: if doing this in a loop, it is necessary to use `glDeleteVertexArrays` and (possibly) `glDeleteBuffers` to prevent quite severe memory leaks. – curz46 Aug 19 '17 at 22:33
0

If you use need to use box to show some texture image that will be fit the full box area you can use two triangles for it and following parameters for -1,-1 to 1,1 area (that can be used with appropriate shaders to show).

Vertex (two triangles coordinates):

-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f

Then use can use following texture coordinates for full size:

0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
Alex
  • 1,297
  • 1
  • 16
  • 12