问题
TL;DR: I've activated and bound textures to textureunits 1-4 and now I wish to use them within shaders without using a program specific uniform position. Is it possible to read data from textures using the "global" texture units?
I'm having a bit of a hard time understanding how to access textures in webgl. Here in this tutorial on textures it says "Texture units are a global array of references to textures.", and this sounds great. I've bound my textures using
gl.activeTexture(gl.TEXTURE1 + i);
gl.bindTexture(gl.TEXTURE_2D, tex);
where i
is the index of the newly created texture and tex
is the texture itself.
But it doesn't give an example of how to access the texture within a shader using this global reference. In this tutorial from the same page it does give an example of how to access the textures from within a shader, but only when using an uniform position (much like how an attribute is accessed), which requires a program (and thus, removing the global aspect, in a way, since it's dependent on the program to be accessed). In this preceding tutorial any details of how to actually set up the texture "u_texture" is skipped altogether.
In short, I have two programs, and I wish to use textures globally in both of them without having to getUniformLocation on each program, if it's possible to do so. Seeing the promises of texture units, I thought I could use them, but I don't know how to actually do something like
sampler2D textureUnit2;
within the shaders. Is this possible? Am i confusing the implementation of the textureUnits with the practicality of using them?
Many thanks!
回答1:
I wish to use textures globally in both of them without having to getUniformLocation on each program, if it's possible to do so
It is not possible to do so.
The texture units are global. You can think of it just like a global array in JavaScript
const textureUnits = [
{ TEXTURE_2D: someTexture, }, // unit 0
{ TEXTURE_2D: someOtherTexture, }, // unit 1
{ TEXTURE_2D: yetAnotherTexture, }, // unit 2
...
];
You can bind a texture to a texture unit by first calling gl.activeTexture
and then calling gl.bindTexture
. Example:
const unit = 2;
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE_2D, yetAnotherTexture);
yetAnotherTexture
is now bound to texture unit 2
Separately you have shader programs. To access a texture you declare a uniform sampler2d someName;
in your shader.
uniform sampler2D foobar;
Think of that as an index into the texture units. You then need to set the index (tell the shader program which global texture unit to use)
const foobarLocation = gl.getUniformLocation(someProgram, "foobar");
...
// tell the program to use the texture on unit 2
const indexOfTextureUnit = 2;
gl.uniform1i(foobarLocation, indexOfTextureUnit);
The texture units themselves are global. The uniforms of a program are unique to each program. You need to set them to tell them which texture unit you want that uniform to reference. Uniforms default to 0 so if you want them to reference texture unit 0 then you don't need to set them but for any other texture unit you do need to set them.
Imagine GLSL's texture2D
function was written in JavaScript. It would work something like
function texture2d(sampler, texcoord) {
const texture = textureUnits[sampler].TEXTURE_2D;
return getColorFromTextureAtTexcoord(texture, texcoord);
}
So if I did this
uniform sampler2D foobar;
varying vec2 v_texcoord;
void main() {
gl_FragColor = texture2D(foobar, v_texcoord);
}
Then I set the foobar
uniform to 2
const foobarLocation = gl.getUniformLocation(someProgram, "foobar");
...
// tell the program to use the texture on unit 2
const indexOfTextureUnit = 2;
gl.uniform1i(foobarLocation, indexOfTextureUnit);
It's going to reference the global texture unit at index 2.
Note: This code in your question
gl.activeTexture(gl.TEXTURE1 + i);
gl.bindTexture(gl.TEXTURE_2D, tex);
seems wrong. It should use TEXTURE0
as a base, not TEXTURE1
gl.activeTexture(gl.TEXTURE0 + i);
gl.bindTexture(gl.TEXTURE_2D, tex);
This diagram might help visualize how it works
Above you can see shader program (gold) has two uniforms decal
and diffuse
.
decal
is set to 3 so it's referencing texture unit 3. texture unit 3 is bound to thedecalTexture
(an image of a F)diffuse
is set to 6 so it's referencing texture unit 6. texture unit 3 is bound to thediffuseTexture
(a 4x4 checkerboard)
Note that texture unit 0 is also bound to the decalTexture
but the shader program is not referencing texture unit 0 so at least for the purpose of drawing pixels, in other words executing the shader program, texture unit 0 is not being used.
来源:https://stackoverflow.com/questions/62836064/is-it-possible-to-access-pre-set-textureunits-within-a-shader