PDA

View Full Version : How to load textures into the fragment shader?



danielr
07-03-2017, 11:57 PM
I'm trying to blend textures together in the fragment shader. I know how to do it but what I seem to be struggling on is getting these textures from my header file to the fragment shader. Here's how my shader is set up:

out vec3 color;

in vec2 uv;

uniform sampler2D grasstexture;
uniform sampler2D rocktexture;

void main() {

vec4 grass = texture(grasstexture, uv);
vec4 rock = texture(rocktexture, uv);
And then I do my blending down below. I'm using C++ for this so my images are loaded in a header file (called mesh.h). In mesh.h here's how I set up my images:

OpenGP::EigenImage<vec3> image;
OpenGP::imread("grass.tga", image);

glGenTextures(1, &_diffuse_tex);
glBindTexture(GL_TEXTURE_2D, _diffuse_tex);

check_error_gl();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
check_error_gl();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F,
image.cols(), image.rows(), 0,
GL_RGB, GL_FLOAT, image.data());
check_error_gl();
And that loads the image from the header file. Now I'm trying to put multiple images into the fragment shader and use them there. One thing I tried doing is adding the following:

tex_id = glGetUniformLocation(_pid, "grasstexture");
glUniform1i(tex_id, 0);
The problem is when I try to load multiple textures, it doesn't seem to work. I'm not sure if I have to copy that entire thing for each texture I load or if I just need to say
OpenGP::imread("rock.tga", image); for the rock texture. So how do I do this?

Silence
07-05-2017, 01:20 AM
Use a different texture unit for each of your textures you want to use at the same time. Shaders should have their sampler uniforms defined with the texture unit to use.
Since it seems hard to find any tutorials about it, I'll try to give you some hints.



// tells the shader that we will use the first texture unit for the first texture
// and the second texture unit for the second texture
shader.setUniform("firstSampler", 0);
shader.setUniform("secondSampler", 1);

[...]

// set the first texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE2D, textureOne);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE2D, textureTwo);

// now render


Hope that helps.

john_connor
07-05-2017, 06:20 AM
// tells the shader that we will use the first texture unit for the first texture
// and the second texture unit for the second texture
shader.setUniform("firstSampler", 0);
shader.setUniform("secondSampler", 1);

[...]

// set the first texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE2D, textureOne);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE2D, textureTwo);

// now render



its usually a good idea to "reset" the selector after the textures are bound, so that next time you know that the "selector" is at GL_TEXTURE0, just in case ..

// set the first texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE2D, textureOne);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE2D, textureTwo);

glActiveTexture(GL_TEXTURE0);

/* render here */

or avoid it completely by using the newer (GL 4.5) function:
void glBindTextureUnit(GLuint unit, GLuint texture); (https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindTextureUnit.xhtml)

Silence
07-05-2017, 06:57 AM
its usually a good idea to "reset" the selector after the textures are bound, so that next time you know that the "selector" is at GL_TEXTURE0, just in case

I guess this can depend on your rendering pipeline. It is common, to my opinion, to have a strong link between textures and units. Meaning that a texture will generally be used for one unit, but not another one. For example, the depth texture used for shadowing will be used for texture unit 3. And this will break things to use this unit for doing the 'basic' texture-mapping, or to use the depth texture on the texture unit 0, which might be used for the 'basic' texture-mapping.
So, when you bind a texture, you'll most probably set it to the unit in which it will make sense for the shader to use it. Which lead to what you wrote after:


or avoid it completely by using the newer (GL 4.5) function:
void glBindTextureUnit(GLuint unit, GLuint texture); (https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindTextureUnit.xhtml)

Interesting. ARB_direct_state_access (https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_direct_state_access.txt) is really something I should have a look into. Hoping that all my hardware/drivers support it.

danielr
07-05-2017, 12:08 PM
Use a different texture unit for each of your textures you want to use at the same time. Shaders should have their sampler uniforms defined with the texture unit to use.
Since it seems hard to find any tutorials about it, I'll try to give you some hints.



// tells the shader that we will use the first texture unit for the first texture
// and the second texture unit for the second texture
shader.setUniform("firstSampler", 0);
shader.setUniform("secondSampler", 1);

[...]

// set the first texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE2D, textureOne);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE2D, textureTwo);

// now render


Hope that helps.

I don't think I'm doing this right. I keep getting an error that says "shader" was not declared in this scope. What's going on with that?

GClements
07-05-2017, 01:25 PM
I don't think I'm doing this right. I keep getting an error that says "shader" was not declared in this scope. What's going on with that?
You're taking his pseudocode literally.

shader.setUniform() is a hypothetical method on a hypothetical shader object which would make the shader current, query the uniform location, and assign a value to it.

So just replace the shader.setUniform() calls with glUseProgram(), glGetUniformLocation() and glUniform1i().

danielr
07-05-2017, 02:56 PM
You're taking his pseudocode literally.

shader.setUniform() is a hypothetical method on a hypothetical shader object which would make the shader current, query the uniform location, and assign a value to it.

So just replace the shader.setUniform() calls with glUseProgram(), glGetUniformLocation() and glUniform1i().

Ok so here's what I did

OpenGP::EigenImage<vec3> image;
OpenGP::imread("grass.tga", image);
OpenGP::imread("rock.tga", image);
glGetUniformLocation(0, "grasstexture");
glGetUniformLocation(1, "rocktexture");

glGenTextures(0, &_diffuse_tex);
glBindTexture(GL_TEXTURE_2D, _diffuse_tex);

check_error_gl();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
check_error_gl();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F,
image.cols(), image.rows(), 0,
GL_RGB, GL_FLOAT, image.data());
check_error_gl();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _diffuse_tex);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _diffuse_tex);
The problem is that I'm only getting the rock texture with no grass. I've also had problems before where the second texture would be extremely bright. With the above code, the lighting for the textures look normal but of course there's no grass.

Silence
07-05-2017, 10:58 PM
Ok so here's what I did

OpenGP::EigenImage<vec3> image;
OpenGP::imread("grass.tga", image);
OpenGP::imread("rock.tga", image);
glGetUniformLocation(0, "grasstexture");
glGetUniformLocation(1, "rocktexture");

glGenTextures(0, &_diffuse_tex);
glBindTexture(GL_TEXTURE_2D, _diffuse_tex);

check_error_gl();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
check_error_gl();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F,
image.cols(), image.rows(), 0,
GL_RGB, GL_FLOAT, image.data());
check_error_gl();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _diffuse_tex);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _diffuse_tex);
The problem is that I'm only getting the rock texture with no grass. I've also had problems before where the second texture would be extremely bright. With the above code, the lighting for the textures look normal but of course there's no grass.


You seem to do things not the right way.
I don't know OpenGP, but it seems you give the last loaded one (rock.tga) to OpenGL. Try to call to OpenGP::imread with a different image. Then create two different OpenGL textures.
Plus, you use the same texture (diffuse_tex) on both texture units.

Finally, I would advice you to read an OpenGL tutorial from the beginning. This one (http://www.opengl-tutorial.org/) for example in order to have the good basics.