Как я могу добавить несколько текстур в свою программу OpenGL?

#c #opengl #glsl #shader

#c #opengl #glsl #шейдер

Вопрос:

Я пишу программу OpenGL и у меня есть несколько моделей, созданных в среде, но я не знаю, как применить разные текстуры к каждой из этих моделей, поэтому на данный момент все они имеют одинаковую текстуру. Я читал, что мне нужно добавить в программу несколько текстурных блоков или использовать текстурный атлас. Текстурный атлас кажется более сложным, поэтому я пытаюсь добавить текстурные единицы.

Я думал, что этот процесс работает следующим образом:

  1. Создайте два текстурных блока с помощью glGenTextures.
  2. Привяжите данные первого изображения к первому текстурному блоку с помощью glBindTexture и glTexImage2D.
  3. Проделайте то же самое со вторым изображением.

Отсюда я подумал, что смогу указать OpenGL, какой текстурный блок я хочу использовать, используя glActiveTexture. Кажется, это работает только с одной текстурой (т. Е. пропустить шаг 3), но не работает с двумя или более.

Я уверен, что чего-то не хватает, так может кто-нибудь указать мне правильное направление?

 //Generate textures
int texturec=2;
int w[texturec],h[texturec];
unsigned char *data[texturec];
data[0]=getImage(amp;w[0],amp;h[0],"resources/a.png");
data[1]=getImage(amp;w[1],amp;h[1],"resources/b.png");

//Apply textures
GLuint textures[texturec];
glGenTextures(texturec, textures);

//Bind a.png to the first texture unit
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w[0], h[0], 0, GL_RGB, GL_UNSIGNED_BYTE, data[0]);

//Bind b.png to the second texture unit
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w[1], h[1], 0, GL_RGB, GL_UNSIGNED_BYTE, data[1]);

glActiveTexture(GL_TEXTURE0);

//Not super clear on what this does, but it needs to be here.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  

А вот и мои шейдеры:

Фрагмент:

 #version 330 core

in vec2 UV;
out vec3 color;

uniform sampler2D textureSampler;

void main(){
    color=texture(textureSampler,UV).rgb;
}
  

Вершина:

 #version 330 core

layout(location=0) in vec3 vertexPos;
layout(location=1) in vec2 vertexUV;

out vec2 UV;

uniform mat4 MVP;

void main(){
    gl_Position=MVP*vec4(vertexPos,1);
    UV=vertexUV;
}
  

Редактировать:

Вот мой новый код после применения предложений Rabid76:

 //Generate textures
int texturec=2;
int w[texturec],h[texturec];
unsigned char *data[texturec];
data[0]=getImage(amp;w[0],amp;h[0],"resources/a.png");
data[1]=getImage(amp;w[1],amp;h[1],"resources/b.png");

//Apply textures
GLuint textures[texturec];
glGenTextures(texturec, textures);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w[0], h[0], 0, GL_RGB, GL_UNSIGNED_BYTE, data[0]);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w[1], h[1], 0, GL_RGB, GL_UNSIGNED_BYTE, data[1]);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

//Shaders
programID=loadShaders("shaders/vertex.shader","shaders/fragment.shader");
glUseProgram(programID);

//Use texture unit 1
glActiveTexture(GL_TEXTURE1);
GLint texLoc=glGetUniformLocation(programID, "textureSampler");
glUniform1i(texLoc, 1);
  

Ответ №1:

  1. Создайте два текстурных блока с помощью glGenTextures .

Нет. glGenTextures не генерирует текстурную единицу.

glGenTextures резервирует значения имен, которые могут использоваться для текстурных объектов.

glBindTexture привязывает именованную текстуру к целевому объекту текстурирования. Когда вызывается эта функция, текстурный объект привязывается к текущему текстурному блоку.
Текущая единица измерения текстуры может быть установлена с помощью glActiveTexture :

например

 GLuint textures[texturec];
glGenTextures(texturec, textures);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
// [...]

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
// [...]
  

Значение имени текстурных объектов и текстурная единица — это совершенно разные вещи.
Текстурный элемент является точкой привязки между объектом текстуры и программой шейдера.
С одной стороны, текстурный объект должен быть привязан к текстурному блоку, с другой стороны, текстурный блок должен быть настроен на единообразие текстурного сэмплера. Таким образом, текстурный блок является «связующим звеном» между ними.

Начиная с GLSL версии 4.2, текстурный блок может быть установлен с помощью классификатора макета (GLSL) внутри шейдера. Точка привязки соответствует текстурному блоку. например, binding = 1 означает текстурный блок 1:

 layout(binding = 1) uniform sampler2D textureSampler;
  

В качестве альтернативы индекс текстурной единицы может быть присвоен текстуре sampler uniform с помощью glUniform1i :

 GLint texLoc = glGetUniformLocation(programID, "textureSampler");
glUniform1i(texLoc, 1);
  

Если программа шейдера использует 1 образец текстуры, единый

 uniform sampler2D textureSampler;
  

тогда достаточно привязать соответствующий объект текстуры перед вызовом draw:

 glActiveTexture(GL_TEXTURE0); // this is default 
glBindTexture(GL_TEXTURE_2D, textures[0]);
  

Но если шейдерная программа использует несколько форм сэмплера текстур (одной и той же цели), тогда разные объекты текстуры должны быть привязаны к разным текстурным единицам:

например

 layout(binding = 3) uniform sampler2D textureSampler1;
layout(binding = 4) uniform sampler2D textureSampler2;
  
 glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, textures[0]);

glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, textures[1]);
  

соответственно

 layout(location = 7) uniform sampler2D textureSampler1;
layout(location = 8) uniform sampler2D textureSampler2;
  
 glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, textures[0]);

glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, textures[1]);

glUseProgram(programID);
glUniform1i(7, 3); // location = 7 <- texture unit 3
glUniform1i(8, 4); // location = 8 <- texture unit 4
  

Комментарии:

1. Поскольку OP не показал этого в предоставленном ими коде, возможно, стоит расширить это последнее предложение, чтобы показать вызов glUniform() для установки текстурного блока, используемого фрагментным шейдером.

2. Я сделал все возможное, чтобы изменить свой код на основе вашего ответа. Я вставил измененный код в конец моего первоначального вопроса. Эти изменения позволяют мне использовать текстурный модуль 1, но я не могу переключиться обратно на текстурный модуль 0. Если я изменю второй аргумент glUniform1i на 0, все текстуры по умолчанию будут черными.

3. @sam Как упоминалось в средней части вопроса, достаточно привязать соответствующую текстуру перед вызовом draw. Поскольку в программе шейдеров одновременно используется только одна текстура, достаточно использовать текстурный модуль 0. Это означает, что они вам не нужны glActiveTexture , но сделайте glBindTexture это раньше glDraw...

4. @sam В любом случае glActiveTexture полезно только перед glBindTexture . Любое другое изменение активного текстурного элемента не делает того, что вы ожидаете от него — что бы это ни было.

5. @sam Есть ли в вашей программе какой-либо другой вызов glBindTexture ? Программа должна быть установлена ( glUseProgram(programID) ), прежде чем можно будет установить форму ( glUniform1i(texLoc, 1) ). glTexParameteri не изменяет некоторые глобальные параметры magic. Он задает параметры текущего связанного текстурного объекта. Он должен быть вызван для каждого объекта.