Is it possible to access pre-set textureunits within a shader?

﹥>﹥吖頭↗ 提交于 2021-02-11 15:31:10

问题


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 the decalTexture (an image of a F)

  • diffuse is set to 6 so it's referencing texture unit 6. texture unit 3 is bound to the diffuseTexture (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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!