PDA

View Full Version : Applying texture coordinates with shaders



JimJones
04-06-2011, 08:33 AM
Can I use glTexCoordPointer to specify texture coordinates for my shaders, or do I need to pass them in some other way? My program works fine aside from applying the texture. I have excluded what I believe to be irrelevant code in the interests of brevity.

I thought I should be able to do something like this:


glTexCoordPointer(2, GL_FLOAT, 0, tcp);
//where tcp is a pointer to an array of GLfloats containing my texcoords
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); //not sure about this line



And in my draw function:


glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glVertexAttribPointer(
position, /* attribute */
3, /* size */
GL_FLOAT, /* type */
GL_FALSE, /* normalized? */
0, /* stride */
(void*)0 /* array buffer offset */
);
glEnableVertexAttribArray(position);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);


glDrawElements(
GL_TRIANGLES,
3 * g_total_tris,
GL_UNSIGNED_SHORT,
(GLvoid*) 0
);

glDisableVertexAttribArray(position);


and then in the vertex shader something like


varying vec2 tex_coord;

void main()
{
tex_coord = gl_MultiTexCoord0.st;
}

and in the fragment shader



varying vec2 tex_coord;
uniform sampler2D tex;
void main()
{
gl_FragColor = gl_Color * texture2D(tex, tex_coord);
}


However, in debugging it appears that using texture2D(tex, tex_coord) or texture2D(tex, gl_TexCoord[0].st) in gl_FragColor will always paint my object black. The texture is definitely being sampled correctly, because I can manually specify an arbitrary vec2 value for the texcoord and paint the entire object a colour from one part of my texture. Should I be passing the texture coordinates into the shader using a uniform variable? Is the problem that the shader overrides the texcoordspointer function?

Help is greatly appreciated, as I have spent hours and hours reading through the red book, opengl.org and random internet tutorials to find an answer.

kyle_
04-06-2011, 11:46 AM
You appear to be doing everything correctly (at least i cant see a problem here).
Here are few tips:
1. Verify that tex_coord is good in frag shader (output it to gl_FragColor directly, see whats there).
2. glTexEnvf does nothing for you, its fixed-function only and you use shaders
3. Post real code if possible (your vertex shader is pseudocode)
4. You may want to pass your texcoords as generic attributes (as you have done with position) - this will simplify your code (but probably wont help with your issue)

Alfonse Reinheart
04-06-2011, 12:13 PM
Wait: are you combining buffer objects (your positions via glVertexAttribPointer) with non-buffer object arrays (your texture coordinates)? If so, don't do that. It may work, but it's really bad form.

Either all of your attributes should come from buffer objects or none of them should.

JimJones
04-07-2011, 09:44 AM
Well, I managed to get the texture mapped properly (it still didn't look quite right, but I think that was more an issue with clipping and vertex transformation), but I did it using the 'really bad form' way, so I obviously want to do it the right way.


Okay, so I've tried putting the texture coordinates in a buffer and passing them in as follows:



GLfloat *texCoords_array;
int texCoords_array_size;

Code to set size of array and fill with tex coords


GLuint texCoord_buffer;
texCoord_buffer = glGetAttribLocation(program, "texCoord_buffer");


glGenBuffers(1, &texCoord_buffer);
glBindBuffer(GL_ARRAY_BUFFER, texCoord_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * texCoords_array_size, texCoords_array, GL_STATIC_DRAW);


glBindBuffer(GL_ARRAY_BUFFER, texCoord_buffer);
glVertexAttribPointer(
texCoord_buffer, /* attribute */
2, /* size */
GL_FLOAT, /* type */
GL_FALSE, /* normalized? */
0, /* stride */
(void*)0 /* array buffer offset */
);
glEnableVertexAttribArray(texCoord_buffer);

glDrawElements(
GL_TRIANGLES,
3 * g_total_tris,
GL_UNSIGNED_SHORT,
(GLvoid*) 0
);

glDisableVertexAttribArray(position);
glDisableVertexAttribArray(texCoord_buffer);

and in the vertex shader:


attribute vec2 texCoord_buffer;
varying vec2 tex_coord;

void main() {

tex_coord[0] = texCoord_buffer[0];
tex_coord[1] = texCoord_buffer[1];

}


I would have thought moving the coordinates from texCoord_buffer into tex_coord would be redundant, but if I use texCoord_buffer in the fragment shader I just get a black model.

and the fragment shader:




varying vec2 tex_coord;

void main() {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * texture2D(tex, tex_coord);
}

I presume I'm doing something wrong with getting the buffer into the shader. Should I be using glTexCoordPointer at all? How do the shaders loop through array buffer objects?

kyle_
04-07-2011, 11:41 AM
I would have thought moving the coordinates from texCoord_buffer into tex_coord would be redundant
Why would you think so?


but if I use texCoord_buffer in the fragment shader I just get a black model
texCoord_buffer is an attribute - its accessible only in vertex shader.


I presume I'm doing something wrong with getting the buffer into the shader. Should I be using glTexCoordPointer at all? How do the shaders loop through array buffer objects?
You appear to be doing everything correctly (at least in the code you shown), and nope, dont use glTexCoordPointer.

V-man
04-08-2011, 10:15 AM
You can simplify this

tex_coord[0] = texCoord_buffer[0];
tex_coord[1] = texCoord_buffer[1];

into this

tex_coord = texCoord_buffer;

because both are of type vec2. It would be only 1 MOV instruction.

Here you go
http://www.opengl.org/wiki/GLSL_:_common_mistakes#Use_the_Swizzle

JimJones
04-11-2011, 09:11 AM
Been hacking at this for ages now and still can't get it to work using a texcoord buffer, so going to post more code:

First the program makes resources as such:



int Draw::make_resources()
{
vertex_buffer = make_buffer(
GL_ARRAY_BUFFER,
g_vertex_buffer_data,
3 * sizeof(GLfloat) * g_total_verts
);
element_buffer = make_buffer(
GL_ELEMENT_ARRAY_BUFFER,
g_element_buffer_data,
3 * sizeof(GLushort) * g_total_tris
);
for (int i =0; i < g_texCoords_array_size; i += 2)
{
g_texCoords_array[i+1] = 1.0 - (g_texCoords_array[i+1]); //t value must be flipped to get UV mapping
}



texCoord_buffer = make_buffer(
GL_ARRAY_BUFFER,
g_texCoords_array,
sizeof(GLfloat) * g_texCoords_array_size
);


//Make textures here
g_textures[0] = read_texture(g_tex);
if (g_textures[0] == 0)
return 0;
glGenTextures(1, &amp;tex_id);

vertex_shader = make_shader(
GL_VERTEX_SHADER,
"src/hello-gl.v.glsl"
);
if (vertex_shader == 0)
return 0;

fragment_shader = make_shader(
GL_FRAGMENT_SHADER,
"src/hello-gl.f.glsl"
);
if (fragment_shader == 0)
return 0;

program = make_program(vertex_shader, fragment_shader);
if (program == 0)
return 0;


//shader variables
position = glGetAttribLocation(program, "position");
//texCoord_buffer = glGetAttribLocation(program, "texCoord_buffer");
rotate_x = glGetUniformLocation(program, "rotate_x_theta");
camera_uniform = glGetUniformLocation(program, "camera");
GLint texSampler;
texSampler = glGetUniformLocation(program, "tex");
glUniform1i(texSampler, 1);
return 1;
}

That gets executed from the class constructor. Then the draw function is called 60 times per second in an SDL loop:



void Draw::draw() {
glUseProgram(program);

glUniform1f(rotate_x, rotate_x_theta++);


float * camera = Camera::getInstance().getCameraMat();
glUniformMatrix4fv(camera_uniform, 1, false, camera);

glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);

glVertexAttribPointer(
position, /* attribute */
3, /* size */
GL_FLOAT, /* type */
GL_FALSE, /* normalized? */
0, /* stride */
(void*)0 /* array buffer offset */
);

glEnableVertexAttribArray(position);

glBindBuffer(GL_ARRAY_BUFFER, texCoord_buffer);
glVertexAttribPointer(
texCoord_buffer, /* attribute */
2, /* size */
GL_FLOAT, /* type */
GL_FALSE, /* normalized? */
0, /* stride */
(void*)0 /* array buffer offset */
);
glEnableVertexAttribArray(texCoord_buffer);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);


glDrawElements(
GL_TRIANGLES,
3 * g_total_tris,
GL_UNSIGNED_SHORT,
(GLvoid*) 0
);

glDisableVertexAttribArray(position);
glDisableVertexAttribArray(texCoord_buffer);
}

Vertex shader:


#version 110

attribute vec3 position;
attribute vec2 texCoord_buffer;
uniform float rotate_x_theta;
uniform mat4 camera;
varying vec2 tex_coord;
varying vec4 frag_colour;

mat4 view_frustum(
float angle_of_view,
float aspect_ratio,
float z_near,
float z_far
) {
return mat4(
vec4(1.0/tan(angle_of_view), 0.0, 0.0, 0.0),
vec4(0.0, aspect_ratio/tan(angle_of_view), 0.0, 0.0),
vec4(0.0, 0.0, (z_far+z_near)/(z_far-z_near), 1.0),
vec4(0.0, 0.0, -2.0*z_far*z_near/(z_far-z_near), 0.0)
);
}

mat4 translate(float x, float y, float z)
{
return mat4(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(x, y, z, 1.0)
);
}

mat4 rotate_x(float theta)
{
return mat4(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, cos(theta), sin(theta), 0.0),
vec4(0.0, -sin(theta), cos(theta), 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
}

void main()
{
gl_Position = view_frustum(radians(45.0), 4.0/3.0, 0.5, 100.0)
* translate(0.0, 0.0, 50.0)
* rotate_x(radians(rotate_x_theta))
* vec4(position.x, position.y, position.z, 1.0);
tex_coord = texCoord_buffer;
}

Fragment shader:


#version 110

uniform sampler2D tex;
varying vec2 tex_coord;

void main()
{
vec2 st;

st[0] = 0.1674 * 0.33927;
st[1] = 0.1648 * 1.1;
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * texture2D(tex, tex_coord);
}

With this code, my model is drawn, but it is all the same colour - if I replace tex_coord with st in the gl_FragColor line of the fragment shader it changes the colour of the model. So it would appear that it is reading reading the texture, but applying the same texture coordinate to every vertex. Alternatively, if I uncomment the line
texCoord_buffer = glGetAttribLocation(program, "texCoord_buffer");
at the end of the make_resources function, the model is textured with a mixture of brown and orange (and maybe black?) as follows:

http://img691.imageshack.us/img691/1219/sarge.jpg

My (probably uneducated) guess is that I'm screwing something up with the buffer and it's only reading a single texture coordinate or some garbage memory.

Help still greatly appreciated.

JimJones
04-25-2011, 04:58 AM
Wow, didn't actually include what the make_buffer function does. Here it is:


GLuint Draw::make_buffer(
GLenum target,
const void *buffer_data,
GLsizei buffer_size
) {

cout << "make_buffer" << endl;
GLuint buffer;
glGenBuffers(1, &amp;buffer);
cout << "done make" << endl;
glBindBuffer(target, buffer);
glBufferData(target, buffer_size, buffer_data, GL_STATIC_DRAW);

return buffer;
}

Still struggling to figure this out. Don't know what else to try at this point. Please help.

JimJones
04-25-2011, 08:12 AM
Wrote some code to move and rotate the model around and came upon this:

http://img831.imageshack.us/img831/346/sargfe.jpg


You can see the entirety of the texture mapped to a small section of the model. This is only visible up close and is found in several parts of the model. The edges of the square texture also appear to be stretched out across the model in some parts. If I don't include the lines

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

Then the single texture is mapped repeatedly across the whole model.

ZbuffeR
04-25-2011, 08:53 AM
Your texture coordinates seem to be way off.
They should be within the [0;1] range.
Add some traces to verify how you specify texcoords.

JimJones
04-25-2011, 09:38 AM
The following loop flips the t value of each texcoord and prints out the contents of the entire array, which is then passed into the buffer.



//t coord must be flipped to get UV mapping, so flip every other value in the array
cout << "SIZE:" << g_texCoords_array_size << endl;
for (int i =1; i < g_texCoords_array_size; i += 2)
{
cout << "TC:" << g_texCoords_array[i-1] << "!";
g_texCoords_array[i] = 1.0 - (g_texCoords_array[i]);
cout <<" !" << g_texCoords_array[i] << endl;
}
texCoord_buffer = make_buffer(
GL_ARRAY_BUFFER,
g_texCoords_array,
sizeof(GLfloat) * g_texCoords_array_size
);


Which produces the following output

SIZE:1598
TC:0.2314! !0.2537
TC:0.3077! !0.3172
TC:0.3108! !0.2525
TC:0.3086! !0.3158
TC:0.2324! !0.3184
TC:0.3364! !0.1425
...
with lots more values all in the range of 0 to 1.

The only thing I can think of is that I'm making the buffer incorrectly and ending up with garbage memory in it instead of the contents of the array. It shouldn't be a problem that my texCoord array is defined as 'GLfloat *' and not 'const GLvoid *' right?

I did just try adding the line


g_tcs = &amp;g_texCoords_array;
where g_tcs is a globally defined constant GLvoid pointer and passing it into the glBufferdata function instead, with the same result.

JimJones
04-27-2011, 04:43 PM
Is it possible to bind the texCoord buffer attribute as follows:

after program is linked and texCoord buffer is made into a GL_ARRAY_BUFFER



texCoord_buffer = glGetAttribLocation(program, "texCoord_buffer");
glBindAttribLocation(program, texCoord_buffer, "texCoord_buffer");

Or do I need to use glVertexAttribPointer as follows:


glBindBuffer(GL_ARRAY_BUFFER, texCoord_buffer);

glVertexAttribPointer(
texCoord_buffer, /* attribute */
2, /* size */
GL_FLOAT, /* type */
GL_FALSE, /* normalized? */
0, /* stride */
(void*)0 /* array buffer offset */
);
glEnableVertexAttribArray(texCoord_buffer);


Neither method is working for me at the moment...


Just tried both of those code snippets together, and the output from this code:


GLint h;
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &amp;h);
cout << "G" << h << endl;
is still "G0", meaning no attributes are active in the vertex shader.

Alfonse Reinheart
04-27-2011, 04:57 PM
You're confusing buffer objects with attribute locations.

Attribute locations are assigned to program objects. This assignment happens at link time. You can affect it by using layout(location) in your shader or glBindAttribLocation in code before linking. Attribute locations are just numbers, between 0 and 15.

Buffer objects are memory buffers. They are OpenGL objects, stored in GLuints, but they should not be considered numbers. Think of them as pointers, not integers.

The first parameter of glVertexAttribPointer is an attribute location, not a buffer object. The second parameter to glBindBuffer is a buffer object, not an attribute location.

In short, there are two values you need: an attribute location and a buffer object. When you have both of these, you do this:



glBindBuffer(GL_ARRAY_BUFFER, bufferObject);
glEnableVertexAttribArray(attributeLocation);
glVertexAttribPointer(attributeLocation, ...);

Dan Bartlett
04-27-2011, 05:02 PM
You need to a do a (modified) mixture of both. You need to either get the attribute location, with:

texCoord_loc = glGetAttribLocation(program, "texCoord_buffer"); // "texCoord" might be better name
or explicitly set the location with:

glBindAttribLocation(program, texCoord_loc, "texCoord_buffer");

Then you need to use this location in the glVertexAttribPointer/glEnableVertexAttribArray calls:



glBindBuffer(GL_ARRAY_BUFFER, texCoord_buffer);

glVertexAttribPointer(
texCoord_loc, /* attribute */
2, /* size */
GL_FLOAT, /* type */
GL_FALSE, /* normalized? */
0, /* stride */
(void*)0 /* array buffer offset */
);
glEnableVertexAttribArray(texCoord_loc);



You are currently using the texCoord_buffer variable for two different conflicting purposes.

JimJones
04-29-2011, 06:14 AM
Okay, this helps a lot - I've managed to get the texture mapped, it's not quite right but it's better than I had previously. Thanks very much for your help, it's really cleared up some confusion for me.