How can I create multiple samplers for one texture array?
So far I have relied upon OpenGL figuring out that the declared uniform sampler2Darray txa sampler refers to the texture array I bound with glBindTexture.
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, width, height,
layerCount, 0 GL_RGBA, GL_UNSIGNED_BYTE, texture_array);
...
glGenTextures(1,&texture_ID);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_ID);
...
//fragment shader
uniform sampler2Darray txa
...
vec2 tc;
tc.x = (1.0f - tex_coord.x) * tex_quad[0] + tex_coord.x * tex_quad[1];
tc.y = (1.0f - tex_coord.y) * tex_quad[2] + tex_coord.y * tex_quad[3];
vec4 sampled_color = texture(txa, vec3(tc, tex_id));
I tried specifying two samplers in the fragment shader but I get a compilation error for the fragment shader:
uniform sampler2DArray txa;
uniform sampler2DArray txa2;
...
vec4 texture = texture(txa, vec3(tc, tex_id));
vec4 texture2 = texture(txa2, vec3(tc2, tex_id));
I didn't expect this to work, however, I am not sure that the fragment shader compilator checks whether samplers are assigned textures, so maybe something else is wrong.
I tried generating and binding the sampler objects but I still get a fragment shader error:
GLuint sampler_IDs[2];
glGenSamplers(2,sampler_IDs);
glBindSampler(texture_ID, sampler_IDs[0]);
glBindSampler(texture_ID, sampler_IDs[1]);
I would like to stick to lower versions of OpenGL, is it possible? Any help is appreciated, thank you!
The error is cause by the line
vec4 texture = texture(txa, vec3(tc, tex_id));
Then name of the variable texture is equal the name of the built-in function texture. The variable is declared in local scope, so in this scope texture is a variable and calling the function texture causes an error.
Rename the variable to solve the issue. e.g.:
vec4 texture1 = texture(txa, vec3(tc, tex_id));
Related
I am trying to understand this code:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);
This is the related shader code:
#version 330 core
...
uniform sampler2D ourTexture1;
uniform sampler2D ourTexture2;
void main()
{
color = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2);
}
So, as far as I understand, after activating GL_TEXTURE0 we bind texture1 to it. My understanding is that this binds texture1 to first sampler2d.The part I dont understand is, why do we need to use glUniform call.
It's an indirection. You choose the texture that is input at location GL_TEXTURE0 then you tell the uniform in your shader to fetch its texture from that same location. It's kind of like this (apologies for the diagram).
The first row is texture unit locations and the second row is shader uniform locations. You may want to bind texture unit 4 to shader sampler 2, for example.
(DatenWolf will be along in a moment to correct me :).
I am using freeglut, GLEW and DevIL to render a textured teapot using a vertex and fragment shader. This is all working fine in OpenGL 2.0 and GLSL 1.2 on Ubuntu 14.04.
Now, I want to apply a bump map to the teapot. My lecturer evidently doesn't brew his own tea, and so doesn't know they're supposed to be smooth. Anyway, I found a nice-looking tutorial on old-school bump mapping that includes a fragment shader that begins:
uniform sampler2D DecalTex; //The texture
uniform sampler2D BumpTex; //The bump-map
What they don't mention is how to pass two textures to the shader in the first place.
Previously I
//OpenGL cpp file
glBindTexture(GL_TEXTURE_2D, textureHandle);
//Vertex shader
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
//Fragment shader
gl_FragColor = color * texture2D(DecalTex,gl_TexCoord[0].xy);
so now I
//OpenGL cpp file
glBindTexture(GL_TEXTURE_2D, textureHandle);
glBindTexture(GL_TEXTURE_2D, bumpHandle);
//Vertex shader
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
gl_TexCoord[1] = gl_TextureMatrix[1] * gl_MultiTexCoord1;
//Fragment shader
gl_FragColor = color * texture2D(BumpTex,gl_TexCoord[0].xy);
//no bump logic yet, just testing I can use texture 1 instead of texture 0
but this doesn't work. The texture disappears completely (effectively the teapot is white). I've tried GL_TEXTURE_2D_ARRAY, glActiveTexture and few other likely-seeming but fruitless options.
After sifting through the usual mixed bag of references to OpenGL and GLSL new and old, I've come to the conclusion that I probably need glGetUniformLocation. How exactly do I use this in the OpenGL cpp file to pass the already-populated texture handles to the fragment shader?
How to pass an array of textures with different sizes to GLSL?
Passing Multiple Textures from OpenGL to GLSL shader
Multiple textures in GLSL - only one works
(This is homework so please answer with minimal code fragments (if at all). Thanks!)
Failing that, does anyone have a tea cosy mesh?
It is very simple, really. All you need is to bind the sampler to some texture unit with glUniform1i. So for your code sample, assuming the two uniform samplers:
uniform sampler2D DecalTex; // The texture (we'll bind to texture unit 0)
uniform sampler2D BumpTex; // The bump-map (we'll bind to texture unit 1)
In your initialization code:
// Get the uniform variables location. You've probably already done that before...
decalTexLocation = glGetUniformLocation(shader_program, "DecalTex");
bumpTexLocation = glGetUniformLocation(shader_program, "BumpTex");
// Then bind the uniform samplers to texture units:
glUseProgram(shader_program);
glUniform1i(decalTexLocation, 0);
glUniform1i(bumpTexLocation, 1);
OK, shader uniforms set, now we render. To do so, you will need the usual glBindTexture plus glActiveTexture:
glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0
glBindTexture(GL_TEXTURE_2D, decalTexHandle);
glActiveTexture(GL_TEXTURE0 + 1); // Texture unit 1
glBindTexture(GL_TEXTURE_2D, bumpHandle);
// Done! Now you render normally.
And in the shader, you will use the textures samplers just like you already do:
vec4 a = texture2D(DecalTex, tc);
vec4 b = texture2D(BumpTex, tc);
Note: For techniques like bump-mapping, you only need one set of texture coordinates, since the textures are the same, only containing different data. So you should probably pass texture coordinates as a vertex attribute.
instead of using:
glUniform1i(decalTexLocation, 0);
glUniform1i(bumpTexLocation, 1);
in your code,
you can have:
layout(binding=0) uniform sampler2D DecalTex;
// The texture (we'll bind to texture unit 0)
layout(binding=1)uniform sampler2D BumpTex;
// The bump-map (we'll bind to texture unit 1)
in your shader. That also mean you don't have to query for the location.
Currently i'm rewriting my rendering to use defered shading in any case, for this i also rewrite the 3D shaders and i'm aiming for Physical based shading.
Now something strange happens, while it worked all the time before and is still working with all other shaders, the uniform variable is not working anymore like expected. If i assign 2 textures two the different texture slots it shows always only one of them.
Basically i'm calling the following to bind my texture:
glActiveTexture(slot); // slot is GL_TEXTURE0 = 0x84C0 + _TextureCount
glBindTexture(type, handle);
glUniform1i(uniform.Location, _TextureCount);
// Always passing, so the uniform is assigned to the right shader
Debug.Assert(_TextureCount == glGetUniformiv(_ShaderHandle, uniform.Location));
_TextureCount++;
No error is generated by OpenGL and all uniform locations are correct ( Checked each of them! ). If i insert a third texture into the shader the first is still not visible, but the second and the third are. Of course if i use only one texture, the texture is visible. Also it seems like its not the texture itself, all are RGB565 and if i switch the order in which they are loaded always the last one is working.
This looks like a error somehow, but this error is completly new, before switching to MRT the shaders worked.
The Shader
varying vec3 vNormal;
varying vec2 vUV0;
varying mat3 vTangentToWorld;
uniform float uRoughness;
uniform float uMetallic;
uniform sampler2D uDiffuse;
uniform sampler2D uDiffuse2;
uniform sampler2D uNormals;
void PS_SHADER() // PS_SHADER is defined as main
{
vec4 diffuse = texture2D(uDiffuse2, vUV0) * uRoughness + (1 - uRoughness) * texture2D(uDiffuse, vUV0);
vec3 specular = (diffuse.xyz * uMetallic) + (uSpecular * (1 - uMetallic));
vec3 normalTexture = texture2D(uNormals, vUV0).rgb * 2.0 - 1.0;
vec3 normal = normalTexture * vTangentToWorld;
// Output the data into the deferred pipeline
// Color component
gl_FragData[0] = vec4((diffuse * (1 - uMetallic)).xyz, diffuse.a);
// Normal and roughness
gl_FragData[1] = vec4((normalize(normal) + 1) / 2, uRoughness);
// Specular component
gl_FragData[2] = vec4(specular, UNUSED);
}
MRT setup
The MRT is working. I generate a framebuffer and attach 4 textures for rendering to it bound to GL_COLOR_ATTACHMENT0 to GL_COLOR_ATTACHMENT2 and of course i have a GL_DEPTH_ATTACHMENT.
Before rendering i bind the target framebuffer ( glBindFramebuffer(GL_FRAMEBUFFER, _FramebufferHandle) ), and call glDrawBuffers with the color attachments only.
I know how to load the texture
std::unique_ptr<glimg::ImageSet> pImgSet(glimg::loaders::dds::LoadFromFile("test.dds"));
GLuint tex = glimg::CreateTexture(pImgSet.get(), 0);
But how do I get this texture into my shader?
GL Image - Unoffcial OpenGL SDK
Bind the texture to a texture unit, e.g. unit 0:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
Add a sampler2D uniform to your shader:
uniform sampler2D myTexture;
Set the uniform to the number of the texture unit, as an integer:
glUseProgram(program);
GLint location = glGetUniformLocation(program, "myTexture");
glUniform1i(location, 0);
In the shader, use texture2D to sample it, e.g.:
gl_FragColor = texture2D(myTexture, texCoords);
The key thing to know is that sampler2D uniforms can be set as integers; setting it to 1 means to use the texture bound to GL_TEXTURE1, and so on. The uniform's value defaults to 0, and the active texture unit defaults to GL_TEXTURE0, so if you use only one texture unit, you don't even need to set the uniform.
I'm trying to write a simple shader which would add textures attached to FBOs. There is no problem with FBO initialization and such (I've tested it). The problem is I believe with
glActiveTexture(GL_TEXTURE0). It doesn't seem to be doing anything- here is my frag shader:
(but generally shader is called - I've tested that by putting gl_FragColor = vec4(0,1,0,1);
uniform sampler2D Texture0;
uniform sampler2D Texture1;
varying vec2 vTexCoord;
void main()
{
vec4 texel0 = texture2D(Texture0, gl_TexCoord[0].st);
vec4 vec = texel0;
gl_FragColor = texel0;
}
And in C++ code i have:
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, iFrameBufferAccumulation);
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
( Render something - it works fine to iTextureImgAccumulation texture attached to GL_COLOR_ATTACHMENT0_EXT )
glClear (GL_COLOR_BUFFER_BIT );
glEnable(GL_TEXTURE_RECTANGLE_NV);
glActiveTexture(GL_TEXTURE0);
glBindTexture( GL_TEXTURE_RECTANGLE_NV, iTextureImgAccumulation ); // Bind our frame buffer texture
xShader.setUniform1i("Texture0", 0);
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations
glTranslatef(0.0f, 0.0f, -2.0f);
xShader.bind();
glBegin(GL_QUADS);
glTexCoord2f(0,OPT.m_nHeight);
glVertex3f(-1,-1,0);
glTexCoord2f(OPT.m_nWidth,OPT.m_nHeight);
glVertex3f(1,-1,0);
glTexCoord2f(OPT.m_nWidth,0);
glVertex3f(1,1,0);
glTexCoord2f(0,0);
glVertex3f(-1,1,0);
glEnd();
glBindTexture( GL_TEXTURE_RECTANGLE_NV, NULL );
xShader.unbind();
Result: black screen (when displaying second texture and using shader (without using shader its fine). I'm aware that this shader shouldn't do much but he doesn't even display
the first texture.
I'm in the middle of testing things but idea is that after rendering to first texture
I would add first texture to the second one. To do this I imagine that this fragment shader
would work :
uniform sampler2D Texture0;
uniform sampler2D Texture1;
varying vec2 vTexCoord;
void main()
{
vec4 texel0 = texture2D(Texture0, gl_TexCoord[0].st);
vec4 texel1 = texture2D(Texture1, gl_TexCoord[0].st);
vec4 vec = texel0 + texel1;
vec.w = 1.0;
gl_FragColor = vec;
}
And whole idea is that in a loop tex2 = tex2 + tex1 ( would it be possible that i use tex2 in this shader to render to GL_COLOR_ATTACHMENT1_EXT which is attached to tex2 ?)
I've tested both xShader.bind(); before initializing uniform variables and after. Both cases - black screen.
Anyway for a moment, I'm pretty sure that there is some problem with initialization of sampler for textures (maybe cos they are attached to FBO)?
I've checked the rest and it works fine.
Also another stupid problem:
How can i render texture on whole screen ?
I've tried something like that but it doesn't work ( i have to translate a bit this quad )
glViewport(0,0 , OPT.m_nWidth, OPT.m_nHeight);
glBindTexture( GL_TEXTURE_RECTANGLE_NV, iTextureImg/*iTextureImgAccumulation*/ ); // Bind our frame buffer texture
glBegin(GL_QUADS);
glTexCoord2f(0,OPT.m_nHeight);
glVertex3f(-1,-1,0);
glTexCoord2f(OPT.m_nWidth,OPT.m_nHeight);
glVertex3f(1,-1,0);
glTexCoord2f(OPT.m_nWidth,0);
glVertex3f(1,1,0);
glTexCoord2f(0,0);
glVertex3f(-1,1,0);
glEnd();
Doesnt work with glVertex2f also..
Edit: I've checked out and I can initialise some uniform variables only textures are problematic.
I've changed order but it still doesn't work.:( Btw other uniforms values are working well. I've displayed texture I want to pass to shader too. It works fine. But for unknown reason texture sampler isn't initialized in fragment shader. Maybe it has something to do that this texture is glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_RGB16F /GL_FLOAT_R32_NV/, OPT.m_nWidth, OPT.m_nHeight, 0, GL_RED, GL_FLOAT, NULL); (its not GL_TEXTURE_2D)?
It's not clear what does your xShader.bind(), I can gues you do glUseProgram(...) there. But uniform variables (sampler index in your case) should be set up after the glUseProgram(...) is called. In this order:
glUseProgram(your_shaders); //probably your xShader.bind() does it.
GLuint sampler_idx = 0;
GLint location = glGetUniformLocation(your_shaders, "Texture0");
if(location != -1) glUniform1i(location, sampler_idx);
else error("cant, get uniform location");
glActiveTexture(GL_TEXTURE0 + sampler_idx);
glBindTexture(GL_TEXTURE_2D, iTextureImg);
and 'yes' you can render FBO texture and use it in shader in another context
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, your_fbo_id);
// render to FBO there
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
then use your FBO texture the same way as you use regular textures.
glActiveTexture(GL_TEXTURE0);
glBindTexture( GL_TEXTURE_RECTANGLE_NV, iTextureImgAccumulation ); // Bind our frame buffer texture
xShader.setUniform1i("Texture0", 0);
This is a rectangle texture.
uniform sampler2D Texture0;
This is a 2D texture. They are not the same thing. The sampler type must match the texture type. You need to use a samplerRect, assuming your version of GLSL supports that.