This shows you the differences between two versions of the page.
egc:laboratoare:bonusrendertext [2019/11/25 16:50] victor.asavei [Utilizare FreeType] |
egc:laboratoare:bonusrendertext [2019/12/03 07:56] (current) alexandru.gradinaru |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laborator bonus ===== | + | ===== Resurse bonus ===== |
==== Redare text în OpenGL ==== | ==== Redare text în OpenGL ==== | ||
Line 18: | Line 18: | ||
Cea de-a doua variantă (și cea care este folosită și pentru implementare în continuarea laboratorului) este ca pentru fiecare caracter/simbol să se creeze câte o textură individuală ce va avea dimensiunea (lătime/înălțime) caracterului/simbolului. Această metodă permite o mai bună flexibilitate în manipularea fiecărui caracter/simbol în parte pentru poziționarea/scalarea acestuia. | Cea de-a doua variantă (și cea care este folosită și pentru implementare în continuarea laboratorului) este ca pentru fiecare caracter/simbol să se creeze câte o textură individuală ce va avea dimensiunea (lătime/înălțime) caracterului/simbolului. Această metodă permite o mai bună flexibilitate în manipularea fiecărui caracter/simbol în parte pentru poziționarea/scalarea acestuia. | ||
- | Pentru a obtine textura pentru fiecare caracter/simbol necesar dintr-un font dat, a fost folosită biblioteca //FreeType//. | + | Pentru a obtine imaginea (bitmapul) pentru fiecare caracter/simbol necesar dintr-un font dat, a fost folosită biblioteca //FreeType//. |
==== Utilizare FreeType ==== | ==== Utilizare FreeType ==== | ||
Line 84: | Line 84: | ||
==== Shadere ==== | ==== Shadere ==== | ||
- | ==== Redare text ==== | + | Pentru redarea fiecărui caracter se folosesc următoarele shadere: |
+ | * Vertex shader | ||
+ | <code glsl> | ||
+ | layout(location = 0) in vec4 vertex; // <x,y,u,v> | ||
+ | out vec2 TexCoords; | ||
+ | uniform mat4 projection; | ||
+ | void main() | ||
+ | { | ||
+ | gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); | ||
+ | TexCoords = vertex.zw; | ||
+ | } | ||
+ | </code> | ||
+ | Pe coordonatele .xy se transmit coordonatele x,y din cadrul ferestrei de afișare care reprezintă poziția unde va fi afișat caracterul. | ||
+ | |||
+ | Coordonata z va fi zero deoarece afișarea se va face direct pe fața cubului corespunzător volumului vizual canonic în planul Z = 0. | ||
+ | |||
+ | Astfel acestea vor fi transformate in vertex shader doar cu matricea de proiecție. | ||
+ | |||
+ | Se va folosi o proiecție ortografică ce va avea //near// = //far// = 0 și pentru a considera colțul stânga sus ca fiind 0,0 va avea //bottom// inversat cu //top// astfel: | ||
+ | <code glsl> | ||
+ | glm::value_ptr(glm::ortho(0.0f, static_cast<GLfloat>(width), static_cast<GLfloat>(height), 0.0f)) | ||
+ | </code> | ||
+ | unde **width** și **height** sunt rezoluția ferestrei de afișare | ||
+ | |||
+ | Pe coordonatele .zw vor veni coordonatele de textură care vor fi trimise mai departe în //TexCoords// | ||
+ | |||
+ | * Fragment shader | ||
+ | <code glsl> | ||
+ | in vec2 TexCoords; | ||
+ | out vec4 color; | ||
+ | |||
+ | uniform sampler2D text; | ||
+ | uniform vec3 textColor; | ||
+ | |||
+ | void main() | ||
+ | { | ||
+ | vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r); | ||
+ | color = vec4(textColor, 1.0) * sampled; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Fragment shader-ul primește ca uniforme id-ul texturii unde a fost încărcat bitmap-ul monocolor al caracterului și culoarea cu care se dorește a fi afișat textul. | ||
+ | Astfel, deoarece am folosit doar canalul roșu pentru textură (GL_RED) se face eșantionarea din textură doar de pe acesta (//texture(text, TexCoords).r//) și se stochează în culoarea finală pe canalul de alpha (componenta a 4-a). În acest fel, considerând quad-ul pe care se afisează ca fiind transparent (in bitmap acolo unde nu există pixeli pentru caracter, va fi intors 0 iar un alpha de 0 înseamnă perfect transparent) textul va fi afișat iar restul conținutului quad-ului pe care a fost mapată textură va fi ignorat permițând astfel combinarea naturală cu ce deja a fost afișat anterior în frame buffer. | ||
+ | Ca ultim pas, se înmulțește culoarea obținută cu culoarea //textColor// pentru a desena textul cu o culoare dorită. | ||
+ | |||
+ | Pentru a putea fi folosit acest mecanism de transparență trebuie activat și folosit mecanismul de blending din OpenGL astfel: | ||
+ | <code glsl> | ||
+ | glEnable(GL_BLEND); | ||
+ | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
+ | </code> | ||
+ | |||
+ | ==== Redarea textului ==== | ||
+ | |||
+ | Pentru redarea unei linii de text se va folosi functia | ||
+ | <code glsl> | ||
+ | void RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) | ||
+ | </code> | ||
+ | unde: | ||
+ | * x,y: coordonatele (x,y) din fereastra de afișare de unde va fi afișată linia de text ( 0,0 corespunde colțului stânga sus al ecranului) | ||
+ | * scale: factor de scalare pentru text (dimensiunea font-ului ales la încărcare va fi înmulțită cu acest factor) | ||
+ | * color: culoarea textului | ||
+ | |||
+ | Întreaga funcție: | ||
+ | |||
+ | <code glsl> | ||
+ | void TextRenderer::RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) | ||
+ | { | ||
+ | // Activate corresponding render state | ||
+ | //this->TextShader.Use(); | ||
+ | if (this->TextShader) | ||
+ | { | ||
+ | glUseProgram(this->TextShader->program); | ||
+ | CheckOpenGLError(); | ||
+ | } | ||
+ | |||
+ | int loc_text_color = glGetUniformLocation(this->TextShader->program, "textColor"); | ||
+ | glUniform3f(loc_text_color, color.r, color.g, color.b); | ||
+ | //this->TextShader.SetVector3f("textColor", color); | ||
+ | glActiveTexture(GL_TEXTURE0); | ||
+ | glBindVertexArray(this->VAO); | ||
+ | |||
+ | // Iterate through all characters | ||
+ | std::string::const_iterator c; | ||
+ | for (c = text.begin(); c != text.end(); c++) | ||
+ | { | ||
+ | Character ch = Characters[*c]; | ||
+ | |||
+ | GLfloat xpos = x + ch.Bearing.x * scale; | ||
+ | GLfloat ypos = y + (this->Characters['H'].Bearing.y - ch.Bearing.y) * scale; | ||
+ | |||
+ | GLfloat w = ch.Size.x * scale; | ||
+ | GLfloat h = ch.Size.y * scale; | ||
+ | // Update VBO for each character | ||
+ | GLfloat vertices[6][4] = { | ||
+ | { xpos, ypos + h, 0.0, 1.0 }, | ||
+ | { xpos + w, ypos, 1.0, 0.0 }, | ||
+ | { xpos, ypos, 0.0, 0.0 }, | ||
+ | |||
+ | { xpos, ypos + h, 0.0, 1.0 }, | ||
+ | { xpos + w, ypos + h, 1.0, 1.0 }, | ||
+ | { xpos + w, ypos, 1.0, 0.0 } | ||
+ | }; | ||
+ | // Render glyph texture over quad | ||
+ | glBindTexture(GL_TEXTURE_2D, ch.TextureID); | ||
+ | // Update content of VBO memory | ||
+ | glBindBuffer(GL_ARRAY_BUFFER, this->VBO); | ||
+ | glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData | ||
+ | |||
+ | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||
+ | // Render quad | ||
+ | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | ||
+ | glEnable(GL_BLEND); | ||
+ | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
+ | glDrawArrays(GL_TRIANGLES, 0, 6); | ||
+ | glDisable(GL_BLEND); | ||
+ | // Now advance cursors for next glyph | ||
+ | x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (1/64th times 2^6 = 64) | ||
+ | } | ||
+ | glBindVertexArray(0); | ||
+ | glBindTexture(GL_TEXTURE_2D, 0); | ||
+ | } | ||
+ | </code> | ||
+ | Astfel se procesează fiecare caracter din care este alcătuit quad-ul după cum urmează: | ||
+ | * Pentru fiecare caracter se avansează pe ecran pe linia orizontală (variabila //x//) la poziția de început de unde urmează a fi desenat (//xpos, ypos//) | ||
+ | * Se crează quad-ul de dimensiunea lățimea și înălțimea texturii asociate caracterului | ||
+ | * Quad-ul va fi desenat folosind două triunghiuri: 6 vertecși (câte 3 pentru fiecare triunghi) fiecare cu 4 valori: | ||
+ | * x,y: coodonatele vârfului (după cum s-a explicat mai sus z va fi zero deoarece se desenează folosind o proiecție ortografică în planul Z = 0 al volumului vizual canonic) | ||
+ | * z,w: coordonatele de textură asociate vârfului | ||
+ | * Se desenează quad-ul format din cele două triunghiuri cu textura corespunzătoare caracterului (//glBindTexture(GL_TEXTURE_2D, ch.TextureID)//) și cu mecanismul de blending activat pentru a nu desena decât pixelii caracterului și a ignora restul quad-ului care este transparent | ||
+ | |||
+ | ==== Utilizare ==== | ||
+ | |||
+ | - Descărcați [[https://github.com/UPB-Graphics/Framework-EGC/archive/withrendertext.zip|framework-ul de laborator]] ce are implementat redarea textului | ||
+ | - Rulați exemplul de redare a textului: clasa ''Laborator_BonusTextRenderer'' | ||
+ | - Dacă doriți să vedeți în detaliu implementarea redării textului examinați: | ||
+ | * Clasa ''TextRenderer'' din /TextRenderer | ||
+ | * Shaderele ''VertexShaderText.glsl'' și ''FragmentShaderText.glsl'' din /TextRenderer/Shaders | ||