Confused about the texture unit value of texture sampler - opengl

I'm learning openGL texture from leanrnOpenGL : Textures
I can distinguish between glGetUniformLocation and "texture unit.
glGetUniformLocation : an integer that represents the location of a specific uniform variable within a program object.
texture unit : the location of a texture is known as a texture unit.
The pages has the following sentences:
We also have to tell OpenGL to which texture unit each shader sampler belongs to
by setting each sampler using `glUniform1i`.
The corresponding code is as follows :
ourShader.use();
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // set it manually
ourShader.setInt("texture2", 1); // or with shader class
I made some wrong changes as follows :
assigned the texture unit value of texture1 to 3, and texture2 to 4.
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 3);
ourShader.setInt("texture2", 4);
The program can be executed, but show a COMPLETELY PURE black texture(or nothing?)
Does the value of texture unit have to start from 0 ? and why?
Why assigning 3 or 4 cause a wrong effect ?

Under the guidance of #Rabbid76, I got the answer.
In the original text of learnOpenGL : Texture, there is a sentence that caused my confusion:
This location of a texture is more commonly known as a texture unit.
#Rabbid76 correct me:
The binding point between the texture object and the sampler uniform is the `texture unit`.
Does the value of texture unit have to start from 0 ?
A: No.
We can specify it by 0~15 at least.
OpenGL should have a at least a minimum of 16 texture units for you to use
which you can activate using GL_TEXTURE0 to GL_TEXTURE15.
Corresponding, when drawing,
invoke glActiveTexture by specify GL_TEXTURE0 to GL_TEXTURE15.
The following is the correct code aftet my correction:
// set `3` `4` as texture unit value
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 3);
ourShader.setInt("texture2", 4);
// bind textures on corresponding texture units
glActiveTexture(GL_TEXTURE3); // replace GL_TEXTURE0 with GL_TEXTURE3
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE4); // replace GL_TEXTURE1 with GL_TEXTURE4
glBindTexture(GL_TEXTURE_2D, texture2);
The code run normally and show the correct overlay effect(no longer a black view).

Related

OpenGL4.5 - bind multiple textures and samplers

I'm trying to understand Textures, Texture Units and Samplers in OpenGL 4.5. I'm attaching a picture of what I'm trying to figure out. I think in my example everything is correct, but I am not so sure about the 1D Sampler on the right side with the question mark.
So, I know OpenGL offers a number of texture units/binding points where textures and samplers can be bound so they work together.
Each of these binding points can support one of each texture targets (in my case, I'm binding targets GL_TEXTURE_2D and GL_TEXTURE_1D to binding point 0, and another GL_TEXTURE_2D to binding point 1).
Additionally, samplers can be bound to these binding points in much the same way (I have bound a 2D sampler to binding point 0 in the pic).
The functions to perform these operations are glBindTextureUnit and glBindSampler.
My initial thought was to bind the 1D sampler to binding point 0, too, and in shader land do the matching based on the binding point and the type of the sampler:
layout (binding = 0) uniform sampler1D tex1D;
layout (binding = 0) uniform sampler2D tex2D;
Quoting the source:
Each texture image unit supports bindings to all targets. So a 2D
texture and an array texture can be bound to the same image unit, or
different 2D textures can be bound in two different image units
without affecting each other. So which texture gets used when
rendering? In GLSL, this depends on the type of sampler that uses this
texture image unit.
but I found the following statement:
[..] sounds suspiciously like you can use the same texture image unit
for different samplers, as long as they have different texture types.
Do not do this. The spec explicitly disallows it; if two different
GLSL samplers have different texture types, but are associated with
the same texture image unit, then rendering will fail. Give each
sampler a different texture image unit.
So, my question is, what is the purpose of binding different texture targets to the same binding point at all, if ultimately a single sampler is going to be bound to that binding point, forcing you to choose?
The information I'm quoting: https://www.khronos.org/opengl/wiki/Texture#Texture_image_units
So why does this exist? Well...
Once upon a time, there were no texture units (this is why glActiveTexture is a separate function from glBindTexture). Indeed, there weren't even texture objects in OpenGL 1.0. But there still needed to be different kinds of textures. You still needed to be able to create data for a 2D texture and a 3D texture. So they came up with the texture target distinction, and they used glEnables to determine which target would be used in a rendering operation.
When texture objects came into being in GL 1.1, they had to decide on the relationship between a texture object and the target. They decided that once an object was bound to a target, it was permanently associated with that target. Because of the aforementioned need to have multiple textures of different types, with the old enable functionality, it was decided that each target represented a separate object binding point. And they made you repeat the binding point in glBindTexture, so that it would be clear to the reader of the code which binding point's data you were disturbing.
Cut to OpenGL 1.2, when multitexture came out. So now they need you to be able to bind multiple textures of the same target, but to different "units". But they couldn't change glBindTexture to specify a particular unit; that would be a backwards-incompatible change.
Now, they could have completely revamped how textures work, creating a new binding function specifically for multitexturing and the like. But the OpenGL ARB loves backwards compatibility; they like making the old API functions work, no matter what the resulting API looks like. So instead, they decided that a texture unit would be an entire set of bindings, with each set having an enable state saying which target was the one to be used. And you switch between units with glActiveTexture.
Of course, once shaders came about, you can see how this all changes. The enable state becomes the sampler type in the shader. So now there's no explicit code describing which texture target is enabled; it's just shader stuff. So they had to make a rule that says that two samplers cannot use the same unit if they're different types.
That's why each texture unit has multiple independent binding points: OpenGL's commitment to backwards compatibility.
It is best to ignore that this capability exists. Bind the right textures that your particular shader needs. So focus on using those functions, and don't worry about the fact that you could have two textures bound to the same target. If you want to make certain that you're not accidentally using the wrong texture, you can use glBindTextures or glBindTextureUnit with a texture name of 0, which will unbind all targets in the particular texture unit(s).
Let's say you have two GLSL programs:
in progA:
uniform sampler1D progA_sampler1D;
uniform sampler2D progA_sampler2D;
in progB:
uniform sampler1D progB_sampler1D;
uniform sampler2D progB_sampler2D;
And you have several textures with names text1D_1, text1D_2, text1D_3,... text2D_1, text2D_2, etc
Now let's suppose you want progA to sample from text1D_1 and text2D_1 and progB to sample from text1D_2 and text2D_2
You already know that each sampler must be associated with a texture unit, not with a texture name.
We can not use the same texture unit for both samplers progA_sampler1D and progA_sampler2D
FIRST OPTION: four texture units
glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1); // Not glUniform1i(locationProgA_forSampler1D, GL_TEXTURE0 + 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);
glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 3);
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 3);
glActiveTexture(GL_TEXTURE0 + 4);
glBindTexture(GL_TEXTURE_2D, text2D_2);
glUniform1i(locationProgA_forSampler2D, 4);
SECOND OPTION: two texture units
glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);
glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 2);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, text2D_2);
glUniform1i(locationProgA_forSampler2D, 1);
Note that unit GL_TEXTURE0 + 1 has bound two textures text1D_1 and text2D_2 with different types.
On the same way GL_TEXTURE0 + 2 has bound two textures, of types GL_TEXTURE_2D and GL_TEXTURE_1D
WRONG OPTION: two texture units
glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);
glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 1);
//Next is wrong: two textures (text1D_1 and text1D_2) of same type GL_TEXTURE_1D
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_2); //Wrong: two textures of same type GL_TEXTURE_2D
glUniform1i(locationProgA_forSampler2D, 2);

OpenGL ES 3 (iOS) texturing oddness - want to know why

I have a functioning OpenGL ES 3 program (iOS), but I've having a difficult time understanding OpenGL textures. I'm trying to render several quads to the screen, all with different textures. The textures are all 256 color images with a sperate palette.
This is C++ code that sends the textures to the shaders
// THIS CODE WORKS, BUT I'M NOT SURE WHY
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _renderQueue[idx]->TextureId);
glUniform1i(_glShaderTexture, 1); // what does the 1 mean here
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _renderQueue[idx]->PaletteId);
glUniform1i(_glShaderPalette, 2); // what does the 2 mean here?
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
This is the fragment shader
uniform sampler2D texture; // New
uniform sampler2D palette; // A palette of 256 colors
varying highp vec2 texCoordOut;
void main()
{
highp vec4 palIndex = texture2D(texture, texCoordOut);
gl_FragColor = texture2D(palette, palIndex.xy);
}
As I said, the code works, but I'm unsure WHY it works. Several seemingly minor changes break it. For example, using GL_TEXTURE0, and GL_TEXTURE1 in the C++ code breaks it. Changing the numbers in glUniform1i to 0, and 1 break it. I'm guessing I do not understand something about texturing in OpenGL 3+ (maybe Texture Units???), but need some guidance to figure out what.
Since it's often confusing to newer OpenGL programmers, I'll try to explain the concept of texture units on a very basic level. It's not a complex concept once you pick up on the terminology.
The whole thing is motivated by offering the possibility of sampling multiple textures in shaders. Since OpenGL traditionally operates on objects that are bound with glBind*() calls, this means that an option to bind multiple textures is needed. Therefore, the concept of having one bound texture was extended to having a table of bound textures. What OpenGL calls a texture unit is an entry in this table, designated by an index.
If you wanted to describe this state in a C/C++ style notation, you could define the table of bound texture as an array of texture ids, where the size is the maximum number of bound textures supported by the implementation (queried with glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, ...)):
GLuint BoundTextureIds[MAX_TEXTURE_UNITS];
If you bind a texture, it gets bound to the currently active texture unit. This means that the last call to glActiveTexture() determines which entry in the table of bound textures is modified. In a typical call sequence, which binds a texture to texture unit i:
glActiveTexture(GL_TEXTUREi);
glBindTexture(GL_TEXTURE_2D, texId);
this would correspond to modifying our imaginary data structure by:
BoundTextureIds[i] = texId;
That covers the setup. Now, the shaders can access all the textures in this table. Variables of type sampler2D are used to access textures in the GLSL code. To determine which texture each sampler2D variable accesses, we need to specify which table entry each one uses. This is done by setting the uniform value to the table index:
glUniform1i(samplerLoc, i);
specifies that the sampler uniform at location samplerLoc reads from table entry i, meaning that it samples the texture with id BoundTextureIds[i].
In the specific case of the question, the first texture was bound to texture unit 1 because glActiveTexture(GL_TEXTURE1) was called before glBindTexture(). To access this texture from the shader, the shader uniform needs to be set to 1 as well. Same thing for the second texture, with texture unit 2.
(The description above was slightly simplified because it did not take into account different texture targets. In reality, textures with different targets, e.g. GL_TEXTURE_2D and GL_TEXTURE_3D, can be bound to the same texture unit.)
GL_TEXTURE1 and GL_TEXTURE2 refer to texture units. glUniform1i takes a texture unit id for the second argument for samplers. This is why they are 1 and 2.
From the OpenGL website:
The value of a sampler uniform in a program is not a texture object,
but a texture image unit index. So you set the texture unit index for
each sampler in a program.

Access depth-stencil texture in a shader program

It seems to be difficult to find information about how to access depth and stencil buffers in shaders of successive render passes.
In a first render pass, I do not only render color and depth information but also make use of stencil operations to count objects. I use a multi render target FBO for this, with color buffers and a combined depth stencil buffer attached. All of them are in the form of textures (no render buffer objects involved).
In a second render pass (when rendering to the screen), I want to access the previously computed stencil index on a per-pixel basis (but not necessarily the same pixel I'm drawing then), similar like you would like to access the previously rendered color buffer to apply some post processing effect.
But I fail to bind the depth stencil texture in the second pass to my shader program as a uniform. At least only black values are read from it, so I guess it's not bound correctly.
Is it possible to bind a depth stencil texture to a texture unit for use in a shader program? Is it impossible to access depth and stencil textures using "normal" samplers? Is it possible with some "special" sampler? Does it depend on the interpolation mode set on the texture or a similar setting?
If not, what is the best (fastest) way to copy the stencil information into a separate color texture between these two render passes? Maybe involving a third render pass which draws a single color using stencil test (I only need a binary version of the stencil buffer in the final render pass, to be precise I need to test if the value is zero).
The setup for the textures being used by the intermediate FBO is as follows:
// The textures for color information (GL_COLOR_ATTACHMENT*):
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
// The texture for depth and stencil information (GL_DEPTH_STENCIL_ATTACHMENT*):
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);
In the second render pass, I currently only try to "debug" the contents of all textures. Therefore I setup the shader with these values:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, <texture>);
glUniform1i(texLocation, 0);
and let the shader program simply copy the texture to the screen:
uniform sampler2D tex;
in vec2 texCoord;
out vec4 fragColor;
void main() {
fragColor = texture2D(tex, texCoord);
}
The results are as followed:
When <texture> above refers to one of my color textures, I see the color output rendered in the first render pass, which is what I expect.
When <texture> above refers to the depth stencil texture, the shader doesn't do anything (I see the color with which I clear the screen).
When copying the depth stencil texture to the CPU and examine it, I see both the depth and stencil information in the packed 24 + 8 bit data as expected.
I have no experience with using stencil as a texture, but you may want to take a look at the following extension :
http://www.opengl.org/registry/specs/ARB/stencil_texturing.txt
Another option could be to create a view of the texture using
http://www.opengl.org/registry/specs/ARB/texture_view.txt
Or you could count objects without the stencil buffer, perhaps using MRT and additive blending on second render target using :
http://www.opengl.org/registry/specs/EXT/draw_buffers2.txt
But I'm afraid those options are not included in pure GL3.3...

Can I use one Texture unit for 5 different 2D textures

I know there can be GL_MAX_TEXTURE_IMAGE_UNITS texture units in an OpenGL Program. But lets assume I am using only one.
Suppose I have 5 2D textures. Can I "map" them to a single texture unit (TEXTURE0) to render all of them.
I mean I will do this:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
//specify tex data and tex params for tex1
glBindTexture(GL_TEXTURE_2D, tex2);
//specify tex data and tex params for tex1
.. and so on for all 5 textures
Render Loop:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
//draw
glBindTexture(GL_TEXTURE_2D, tex2);
//draw
..and so on for all 5 textures
In the fragment shader I am using only 1 sampler2d variable which is initialized to zero (for texture unit 0)
If this will not work correctly, then should I use 5 different texture units(TEXTURE0, TEXTURE0+1, ....TEXTURE0+5) and "map" each texture to a different texture unit.
Most important question: If my first approach is correct, how many 2D textures(lets just only talk about 2D textures for now) can I "map" to a Texture unit?
As long as you don't want to use several textures on a single piece of geometry simultanously, this is perfectly possible. Actually it's the usual thing to do, because the purpose of multitexturing and texture units is not to hold different textures for different objects, but different layers of textures used on the same object.
Think of texture units as something like layers in Photoshop.
Well, as I understand OpenGL texture units:
Text units are a way for shaders to point texture for sampling. If your shader must access more than one texture, you must separate them into differents texture units.
In your case, as you seem to need only 1 texture at the same time, glActiveTexture is useless, just let's Opengl use the default GL_TEXTURE0 for all.
So, yes, your render loop is correct.
How many is the limit of texture units? 48+, more than enough!
Note:
Remember that OpenGL is a state machine, so to configure more than 1 texture to work at the same time, you need a way to select them, that is exactly what glActiveTexture do.
I hope it will help you.

OpenGl Texture unit

’m observing a strange behavior from my application which I hope you can explain to me.
You see, I have these two 3D textures that are being sent to the fragment shader and are rendered perfectly OK. But there is a problem, As soon as I even create another texture (it’s a 1D texture), a black screen is being rendered instead of the correct former result.
About this 1D texture, I’m not even sending it to the fragment shader. The minute I call glTexImage1D(…) the black screen shows up. I comment this line and it goes away!!And the two textures are rendered.
I figured there has to be some kind of problem with texture units. Because when I run the application with gDebugger, the texture unit of one of 3D textures is THE SAME as the one assigned to the 1D texture.
I did not assign any texture unit to the 1D texture, I just created it. Apparently texture unit GL_TEXTURE0 is being automatically assigned to the 1D texture. The weird part is that although I use GL_TEXTURE2 and GL_TEXTURE3 for the 3D texture, one of them is being bound to GL_TEXTURE0 as a result of calling glTexImage1D!
Here is a snapshot from the textures list window of gDebugger:
Texture 1 (unit 2 ,bound3d)-enabled
Texture 2 (unit 0 ,bound1d)
Texture 3 (unit 0 ,bound3d)-Enabled (unit 3,bound3D)-enabled
Why is this happening?
the problem is not why texture 1D is being bound to GL_TEXTURE0, it is why it can affect the state of another already bound texture. the code is like this :
.
...
// generating the first texture_3d
glTexImage(GL_TEXTURE_3D,....);
glBindTexture(GL_TEXTURE_3D,id1);
//render loop for the first texture_3d
GLuint glEnum = GL_TEXTURE2;
vtkgl::ActiveTexture(glEnum);
glBindTexture(vtkgl::TEXTURE_3D,id1);
program->setUniform("TEX1",2);
// generating the second texture_3d
glTexImage(GL_TEXTURE_3D,....);
glBindTexture(GL_TEXTURE_3D,id2);
//render loop for the first texture_3d
GLuint glEnum = GL_TEXTURE3;
vtkgl::ActiveTexture(glEnum);
glBindTexture(vtkgl::TEXTURE_3D,id2);
program->setUniform("TEX2",3);
// generating texture 1D
glTexImage(GL_TEXTURE_1D,....);
glBindTexture(GL_TEXTURE_1D,id3);
we expect GL_TEXTURE2 and GL_TEXTURE3 to be active but gDebugger indicates that GL_TEXTURE0 and GL_TEXTURE2 are active.
Call glActiveTexture BEFORE glBindTexture
glBindsTexture binds to CURRENT active texture, so in your current code you first you bind id1 to texture 0, then bind it again to texture2, then bind id2 to texture2 (replacing id1), etc.
You should not have both 1D and 3D textures bound to the same texture unit at the same time. To avoid this, either unbind the 3D texture: glBindTexture() or switch to a new texture unit: vtkgl::ActiveTexture() before binding the 1D texture.
As the above poster has stated, the call to vtkgl::ActiveTexture() must come before the corresponding call to glBindTexture().