использование массивов текстур со свободным типом в opengl

После прочтения и запуска следующего руководства (http://learnopengl.com/code_viewer.php?code=in-practice/text_rendering), я научился визуализировать текст с помощью FreeType в OpenGL. Теперь мне интересно, можно ли избежать вызова glDrawArrays для каждого глифа. Поэтому я внес некоторые изменения в VBO, чтобы использовать его для всей строки вместо одного глифа. В качестве первого шага я использовал строку «AA», и, поскольку оба глифа идентичны, они также имеют одну и ту же текстуру. Таким образом, не было проблемой запустить следующий код:

glGenVertexArrays(1, & textVAO);
glBindVertexArray(textVAO);
glGenBuffers(1, &textVBO);
glBindBuffer(GL_ARRAY_BUFFER,textVBO);
glBufferData(GL_ARRAY_BUFFER, 24* 2* sizeof(float), NULL, GL_DYNAMIC_DRAW);
glVertexPointer( 4, GL_FLOAT, 0, NULL);

с последующим:

textShader.Use();
glm::vec3 color;
color = glm::vec3(1.0, 0.7f, 0.9f);
glUniform3f(glGetUniformLocation(textShader.Program, "textColor"), color.x, color.y, color.z);
glBindVertexArray(textVAO);
glActiveTexture(GL_TEXTURE0);
int k=0;
for(c = text.begin(); c != text.end(); c++){
    Character ch = Characters[*c];
    GLfloat xpos = x + ch.Bearing.x * scale;
    GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;
    GLfloat w = ch.Size.x * scale;
    GLfloat h = ch.Size.y * scale;
    V.block(0,k,24,1) << xpos, ypos + h,0.0,0.0,xpos, ypos,0.0,1.0,xpos + w, ypos,1.0,1.0,xpos, ypos + h,0.0,0.0,xpos + w,ypos,1.0,1.0,xpos + w, ypos+h,1.0,0.0;
    k++;
    x += ( (ch.Advance >> 6) * scale);
}
glBindTexture(GL_TEXTURE_2D,66);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glBindBuffer(GL_ARRAY_BUFFER,textVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 24* 2* sizeof(float), V.data());
glDrawArrays(GL_TRIANGLES,0,4*3);
glBindTexture(GL_TEXTURE_2D,0);
glDisableClientState(GL_VERTEX_ARRAY);
glUseProgramObjectARB(0);

Я хотел бы иметь возможность отображать «AB» или «ZW» на экране, поэтому сейчас я пытаюсь использовать GL_TEXTURE_2D_ARRAY вместе с glTexImage3D и glTexSubImage3D. Опять же, для упрощения я использую «AA», чтобы глифы имели одинаковую ширину и высоту. Поэтому я добавил

GLuint textureArray;
glEnable(GL_TEXTURE_2D_ARRAY);
glGenTextures(1, &textureArray);
glBindTexture(GL_TEXTURE_2D_ARRAY, textureArray);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, 4, 30, 35, 2, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);

к первой части прецедентного кода, а вторая часть стала:

textShader.Use();
glm::vec3 color;
color = glm::vec3(1.0, 0.7f, 0.9f);
glUniform3f(glGetUniformLocation(textShader.Program, "textColor"), color.x, color.y, color.z);
glBindVertexArray(textVAO);
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D_ARRAY);
for(c = text.begin(); c != text.end(); c++) {
    Character ch = Characters[*c];
    GLfloat xpos = x + ch.Bearing.x * scale;
    GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;
    GLfloat w = ch.Size.x * scale;
    GLfloat h = ch.Size.y * scale;
    V.block(0,k,24,1) << xpos, ypos + h,0.0,0.0,xpos, ypos,0.0,1.0,xpos + w, ypos,1.0,1.0,xpos, ypos + h,0.0,0.0,xpos + w,ypos,1.0,1.0,xpos + w, ypos+h,1.0,0.0;
    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, k, 30,35, 1, GL_RED, GL_UNSIGNED_BYTE, ch.pointeur);
    k++;
    x += ( (ch.Advance >> 6) * scale);
    }
glBindTexture(GL_TEXTURE_2D_ARRAY,textureArray);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER,textVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 24* 2* sizeof(float), V.data());
glDrawArrays(GL_TRIANGLES,0,4*3);
glBindTexture(GL_TEXTURE_2D,0);
glDisableClientState(GL_VERTEX_ARRAY);
glUseProgramObjectARB(0);

Код компилируется, но на экране ничего не отображается. Поэтому мне интересно, правильно ли я использую GL_TEXTURE_2D_ARRAY, а также нужно ли мне делать «массивную версию» шейдеров. Я использую OpenGL 4.5, спасибо.


person Jonathan Berrebi    schedule 19.05.2016    source источник


Ответы (2)


Есть (как минимум) две проблемы.

Во-первых, вы используете конвейер с фиксированной функцией, который нельзя использовать с массивами текстур. Среда текстур, обращающаяся к вещам, не знает, как с ними обращаться. Если вы хотите использовать текстуры массива, вы должны использовать шейдеры.

Во-вторых, даже если вы использовали шейдеры, вы все делаете неправильно. Опять же, вас неправильно учили в учебнике (который в данном случае учит настолько плохой практике, что это активно вредит пользователям OpenGL). Вы помещаете каждый глиф в свой собственный слой массива. Ну, многие реализации OpenGL поддерживают только 256 слоев массива, что немного глифы, если вы хотите включить неанглийский текст.

Правильный способ рендеринга глифов — создать текстурный атлас глифов, а не использовать глиф на текстуру (как это делает дерьмовый туториал) или глиф на слой-массив (как делаете вы). Вы помещаете несколько глифов в разные места одной 2D-текстуры, а затем используете координаты текстуры, чтобы выбрать, какой глиф использовать. Это позволит вам отправлять целые блоки текста с помощью одного вызова отрисовки.

В наши дни размер 2D-текстуры может превышать 16К в пикселях. Даже с текстурами размером всего 4096 x 4096 вы можете вместить в них более 16 тысяч глифов 32 x 32.

person Nicol Bolas    schedule 19.05.2016
comment
Как автор туториала, я хотел бы поделиться своим мнением: я согласен с тем, что упаковка всех глифов в одну текстуру, безусловно, лучше всего влияет на производительность. Причина, по которой я выбрал подход для каждого глифа в этом руководстве, заключается в том, что его легче усвоить читателям, плохо знакомым с рендерингом глифа. Если бы я также потратил время на описание того, как упаковывать глифы и отображать каждый глиф по-разному с разными координатами UV, учебник был бы объемным и трудным для понимания. Учебник имеет примечание об этом в конце и предлагает читателям решить это самостоятельно. - person Joey Dewd; 19.05.2016
comment
@JoeyDewd: я понимаю и ценю трудный баланс между решением чему учить, а чему нет. Тем не менее, атласы текстур — это базовый и жизненно важный навык разработчика графики. Они используются во всей графике для разных целей, но для начинающих программистов графики большинство применений - это хитрости. Вы не визуализируете сцену достаточно сложно, чтобы разница в производительности была очевидна. Напротив, рендеринг текста предлагает прекрасную возможность представить концепцию атласа текстур. Я по-прежнему говорю, что не представить его здесь — плохая услуга для ваших читателей. - person Nicol Bolas; 20.05.2016
comment
Я согласен, в идеале у меня было бы 3 отдельных учебника: один для атласов текстур, один для FreeType и один для создания атласов текстур с помощью FreeType в этом порядке (и, возможно, один для рендеринга поля расстояния со знаком). Я записал все это в свой список дел. - person Joey Dewd; 20.05.2016
comment
Спасибо! Joey за то, что познакомил меня с фритайпом в opengl, и Nicol за то, что дал мне путь. Я хотел бы избежать обучения на практике, но учебные пособия доступны так легко ... и нелегко разобраться, какая теоретическая база является наиболее подходящей. Теперь я использовал атлас текстур, и он работает, по крайней мере, для себя. - person Jonathan Berrebi; 20.05.2016

Я нашел еще одно руководство, состоящее из двух частей: первая часть (очень похожая с педагогической точки зрения на руководство Джоуи) вызывает gldraw для каждого глифа, а вторая часть объясняет, как нарисовать целую строку за один вызов. Вот ссылка на вторую часть. Полный код можно скачать (прокрутите страницу вниз). Спасибо!

https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02

person Jonathan Berrebi    schedule 03.06.2016
comment
Просто чтобы прояснить для тех, кто собирается это сделать сейчас, это руководство, в котором рассказывается об атласах текстур. Это тот, чтобы следовать. Спасибо Джонатан. - person PhilT; 02.12.2019