Related
I'm trying to add border/outline to text's drawn using freetype. So far I've found freetype's docs about it but they are a bit inconsistent. Some functions/structs are visible on my project but some are not. For example I can create an outline object like this FT_Outline border; but I can't call the function I need to call in order to apply that outline like the docs says me to do like this: FT_Outline_New( FT_Library library, FT_UInt numPoints, FT_Int numContours, FT_Outline *anoutline );. This question is of no help either, almost all of the functions/structs are not present on my setup.
I'm a bit lost, how am I supposed to implement outlines to my text? Are these methods outdated if so what is the modern method? Am I linking/importing/compiling freetype wrong? I have no errors with freetype whatsoever so I don't think there are any errors with my freetype implementation.
Here is how my text-rendering process looks like:
Text.cpp
Text::Text(text_attributes&& atrib, int gl_width, int gl_height)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shader = Shader("./src/opengl/shaders/text.vs", "./src/opengl/shaders/text.fs");
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(SCR_WIDTH), 0.0f, static_cast<float>(SCR_HEIGHT));
shader.use();
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
if (FT_Init_FreeType(&ft))
{
exit(0);
}
std::string font_name = "./src/opengl/fonts/" + *atrib.__font_name + ".ttf";
FT_Face face;
if (FT_New_Face(ft, font_name.c_str(), 0, &face)) {
VI_ERROR("ERROR::FREETYPE: Failed to load font");
exit(0) ;
} else {
FT_Outline border;
FT_Set_Pixel_Sizes(face, 0, atrib.__font_size);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (unsigned char c = 0; c < 128; c++)
{
// Load character glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
VI_ERROR("ERROR::FREETYTPE: Failed to load Glyph");
continue;
}
// generate texture
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
// set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// now store character for later use
Character character = {
texture,
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
static_cast<unsigned int>(face->glyph->advance.x)
};
Characters.insert(std::pair<char, Character>(c, character));
}
glBindTexture(GL_TEXTURE_2D, 0);
}
FT_Done_Face(face);
FT_Done_FreeType(ft);
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Text::render_text(std::string text, float x, float y, float z, std::string hex_color, float angle_rad, bool bg)
{
static const int scale = 1;
shader.use();
glUniform3f(glGetUniformLocation(shader.ID, "textColor"), 0.1, 0.1, 0.1);//color.get_color_float(Utils::RED), color.get_color_float(Utils::GREEN), color.get_color_float(Utils::BLUE));
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
GLfloat vertices[6][4] = {
{ 0.0, 1.0, 0.0, 0.0 },
{ 0.0, 0.0, 0.0, 1.0 },
{ 1.0, 0.0, 1.0, 1.0 },
{ 0.0, 1.0, 0.0, 0.0 },
{ 1.0, 0.0, 1.0, 1.0 },
{ 1.0, 1.0, 1.0, 0.0 }
};
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData
glBindBuffer(GL_ARRAY_BUFFER, 0);
glm::mat4 rotateM = glm::rotate(glm::mat4(1.0f), glm::radians(angle_rad), glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 transOriginM = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, z));
std::string::const_iterator c;
GLfloat char_x = 0.0f;
for (c = text.begin(); c != text.end(); c++)
{
Character ch = Characters[*c];
GLfloat w = ch.Size.x * scale;
GLfloat h = ch.Size.y * scale;
GLfloat xrel = char_x + ch.Bearing.x * scale;
GLfloat yrel = y - (ch.Size.y - ch.Bearing.y) * scale;
char_x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
glm::mat4 scaleM = glm::scale(glm::mat4(1.0f), glm::vec3(w, h, 1.0f));
glm::mat4 transRelM = glm::translate(glm::mat4(1.0f), glm::vec3(xrel, yrel, z));
glm::mat4 modelM = transOriginM * rotateM * transRelM * scaleM;
GLint model_loc = glGetUniformLocation(shader.ID, "model");
glUniformMatrix4fv(model_loc, 1, GL_FALSE, glm::value_ptr(modelM));
glBindTexture(GL_TEXTURE_2D, ch.TextureID);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
Text.h
extern "C"{
#include <ft2build.h>
#include FT_FREETYPE_H
}
class Text{
Text(text_attributes&& atrib, int w, int h);
render_text(std::string text, float x, float y, float z, std::string hex_color, float angle_rad, bool bg);
};
You should also include:
#include FT_GLYPH_H //optional glyph management component
#include FT_OUTLINE_H //scalable outline management
#include FT_STROKER_H //functions to stroke outline paths
See also:
https://freetype.org/freetype2/docs/reference/ft2-header_file_macros.html
If you want to render an outline (in the specified order):
FT_Stroker_New
FT_Stroker_Set
FT_Get_Glyph
FT_Glyph_Stroke
if glyph->format == FT_GLYPH_FORMAT_OUTLINE
FT_Raster_Params params
params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT
params.gray_spans = outlineRenderCallback
params.user = user_data
FT_Outline_Render
I am working on a 2D game engine and I want to implement text rendering. I want to use freetype. I have the following code:
GLuint va;
glGenVertexArrays(1, &va);
GLuint vb;
glGenBuffers(1, &vb);
glBindVertexArray(va);
glBindBuffer(GL_ARRAY_BUFFER, vb);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
FT_Face testface;
FT_Error error;
error = FT_New_Face(ftlib, "arial.ttf", 0, &testface);
if (error == FT_Err_Unknown_File_Format)
{
std::cout << "Font format not supported" << std::endl;
}
else if (error)
{
std::cout << "Something else" << "\n";
}
error = FT_Set_Pixel_Sizes(testface, 0, 48);
if (error)
{
std::cout << "Problem with set px szes occ\n";
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (unsigned char c = 0; c < 128; c++)
{
if (FT_Load_Char(testface, c, FT_LOAD_RENDER))
{
std::cout << "Error. Failed to load glyph "<<c<<"\n";
continue;
}
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
testface->glyph->bitmap.width,
testface->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
testface->glyph->bitmap.buffer
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Character newchar = {
texture,
glm::ivec2(testface->glyph->bitmap.width, testface->glyph->bitmap.rows),
glm::ivec2(testface->glyph->bitmap_left, testface->glyph->bitmap_top),
testface->glyph->advance.x
};
characters.insert(std::pair<char, Character>{c, newchar});
}
FT_Done_Face(testface);
//glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glUseProgram(text_shader_program);
glUniform4f(glGetUniformLocation(text_shader_program, "textColor"), 1.f, 1.f, 1.f, 1.f);
glActiveTexture(GL_TEXTURE0);
std::string::const_iterator c;
std::string text = "TEST";
glBindVertexArray(va);
float x = 0.f;
for (c = text.begin(); c != text.end(); c++)
{
Character ch = characters[*c];
float xpos = x + ch.bearing.x;
float ypos = 0.f - (ch.size.y - ch.bearing.y);
float w = ch.size.x;
float h = ch.size.y;
float vertices[6][4] = {
{xpos, ypos + h, 0.f, 0.f},
{xpos, ypos, 0.f, 1.f},
{xpos + w, ypos, 1.f, 1.f},
{xpos, ypos + h, 0.f, 0.f},
{xpos + w, ypos, 1.f, 1.f},
{xpos + w, ypos + h, 1.f, 0.f}
};
glBindTexture(GL_TEXTURE_2D, ch.textureID);
glBindBuffer(GL_ARRAY_BUFFER, vb);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
x += (ch.advance >> 6);
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
I initialize the library (before the above code happens) with
FT_Error error = FT_Init_FreeType(&ftlib);
if (error)
{
std::cout << "????" << std::endl;
}
The face and library initializes properly, glyphs are loaded, but nothing is drawn. I don't use camera, so all the coordinates are in space -1 to 1 if that matters.
I have the following shaders for text rendering:
vertexshader
#version 330 core
layout(location=0) in vec4 vertex;
out vec2 TextCoords;
void main() {
gl_Position = vec4(vertex.xy, 0.0, 1.0);
TextCoords = vertex.zw;
}
fragmentshader
#version 330 core
in vec3 TextCoords;
out vec4 color;
uniform sampler2D text;
uniform vec4 textColor;
void main(){
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TextCoords).r);
color = textColor * sampled;
}
Update:
I changed the line FT_Set_Pixel_Sizes(testface, 0, 48) to FT_Set_Pixel_Sizes(testface, 0, 1) and I got a huge gray rectangle where the text should be.
Has anyone got any ideas?
The problem seems to be the result of not using z-coordinate. The problem is, when using coordinate space -1 to 1, the text is rendering in too big scale to be seen or recognized. The solution is to use the z-coordinate as a static camera view, so for 500x500 window the borders are -250.f and 250.f
I'm trying to implement a background-blur effect with OpenGL.
Here is my thought:
drawing all background element to colorFBO
draw colorFBO into pingpongFBO[0] and pingpongFBO1
use pingpongFBO[0] as texture, draw hori-blur rect to pingpongFBO1
use pingpongFBO1 as texture, draw vert-blur rect to pingpongFBO[0]
Here is the result with blur radius of 200:
Result
As you can see, the hori-blur effect disappeared almost.
The edge of blue rectangle is still sharp.
If I only draw hori-blur part, it looks correct, the edge is now blurry.
Hori Only
Here is my blur frag shader code
#version 330 core
#define pow2(x)(x*x)
#define PI 3.14159265
uniform sampler2D screenTexture;
uniform bool horizontal;
uniform float radius;
out vec4 FragColor;
float gaussian(float x){
float sigma2=2.*pow2(radius/3.);
return(1./(sqrt(PI*sigma2)))*exp(-pow2(x)/sigma2);
}
void main(){
vec2 resolution=vec2(600,600);
vec2 uv=vec2(gl_FragCoord.xy/resolution);
vec4 color=vec4(0.);
float weight=gaussian(0);
color+=texture2D(screenTexture,uv)*weight;
float accum=weight;
if(horizontal){
for(int i=1;i<radius+1;i++){
vec2 off=vec2(i,0)/resolution;
weight=gaussian(i);
color+=texture2D(screenTexture,uv+off)*weight;
color+=texture2D(screenTexture,uv-off)*weight;
accum+=weight*2;
}
}else{
for(int i=1;i<radius+1;i++){
vec2 off=vec2(0,i)/resolution;
weight=gaussian(i);
color+=texture2D(screenTexture,uv+off)*weight;
color+=texture2D(screenTexture,uv-off)*weight;
accum+=weight*2;
}
}
FragColor=vec4((color/accum).xyz,1.);
}
Here is main CPP :
#include "Common.hh"
const unsigned int SCR_WIDTH = 600;
const unsigned int SCR_HEIGHT = 600;
float left = 150;
float top = 200;
float radius = 200;
void processInput(GLFWwindow *window);
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
float *quad1Vertices = rectWithSize(SCR_WIDTH, 200);
float *quad2Vertices = rectWithSize(200, SCR_WIDTH);
float *blurQuadVertices = rectWithSize(200.0, 200.0);
float backgroundVertices[] = {
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f};
unsigned int quad1VAO, quad1VBO;
glGenVertexArrays(1, &quad1VAO);
glGenBuffers(1, &quad1VBO);
glBindVertexArray(quad1VAO);
glBindBuffer(GL_ARRAY_BUFFER, quad1VBO);
glBufferData(GL_ARRAY_BUFFER, RECT_SIZE, quad1Vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
unsigned int quad2VAO, quad2VBO;
glGenVertexArrays(1, &quad2VAO);
glGenBuffers(1, &quad2VBO);
glBindVertexArray(quad2VAO);
glBindBuffer(GL_ARRAY_BUFFER, quad2VBO);
glBufferData(GL_ARRAY_BUFFER, RECT_SIZE, quad2Vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
unsigned int quad3VAO, quad3VBO;
glGenVertexArrays(1, &quad3VAO);
glGenBuffers(1, &quad3VBO);
glBindVertexArray(quad3VAO);
glBindBuffer(GL_ARRAY_BUFFER, quad3VBO);
glBufferData(GL_ARRAY_BUFFER, RECT_SIZE, blurQuadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
unsigned int backgroundVAO, backgroundVBO;
glGenVertexArrays(1, &backgroundVAO);
glGenBuffers(1, &backgroundVBO);
glBindVertexArray(backgroundVAO);
glBindBuffer(GL_ARRAY_BUFFER, backgroundVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(backgroundVertices), &backgroundVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
Shader shader("simple_rect.vs", "simple_rect.fs");
Shader screenShader("screen.vs", "screen.fs");
Shader blurShader("blur_rect.vs", "blur_rect.fs");
glm::mat4 projection = glm::ortho(0.0f, (float)SCR_WIDTH, 0.0f, (float)SCR_HEIGHT, -1.0f, 1.0f);
glm::mat4 model = glm::mat4(1.0);
shader.use();
shader.setMat4("projection", projection);
blurShader.use();
blurShader.setMat4("projection", projection);
blurShader.setInt("screenTexture", 0);
screenShader.use();
screenShader.setMat4("projection", glm::mat4(1.0));
screenShader.setMat4("model", glm::mat4(1.0));
screenShader.setInt("screenTexture", 0);
GLuint colorFBO;
GLuint colorBuffer;
glGenFramebuffers(1, &colorFBO);
glGenTextures(1, &colorBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, colorFBO);
glBindTexture(GL_TEXTURE_2D, colorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBuffer, 0);
GLuint pingPongFBO[2];
GLuint pingPongColorBuffer[2];
glGenFramebuffers(2, pingPongFBO);
glGenTextures(2, pingPongColorBuffer);
for (GLuint i = 0; i < 2; i++)
{
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[i]);
glBindTexture(GL_TEXTURE_2D, pingPongColorBuffer[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingPongColorBuffer[i], 0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
while (!glfwWindowShouldClose(window))
{
processInput(window);
glBindFramebuffer(GL_FRAMEBUFFER, colorFBO);
glClearColor(229.0 / 255.0, 229.0 / 255.0, 229.0 / 255.0, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
shader.use();
shader.setMat4("model", glm::translate(model, glm::vec3(0.0f, 100.0f, 0.0f)));
shader.setVec4("uColor", glm::vec4(0.3451, 0.7333, 0.2, 1.0));
glBindVertexArray(quad1VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
shader.setVec4("uColor", glm::vec4(0, 178.0 / 255.0, 1, 1.0));
shader.setMat4("model", glm::translate(model, glm::vec3(50.0f, 0.0f, 0.0f)));
glBindVertexArray(quad2VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[0]);
glBindTexture(GL_TEXTURE_2D, colorBuffer);
glClearColor(0.0f, 1.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(backgroundVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[1]);
glBindTexture(GL_TEXTURE_2D, colorBuffer);
glClearColor(0.0f, 1.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(backgroundVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[1]);
glBindTexture(GL_TEXTURE_2D, pingPongColorBuffer[0]);
blurShader.use();
blurShader.setMat4("model", glm::translate(model, glm::vec3(left, top, 0)));
blurShader.setInt("screenTexture", 0);
blurShader.setBool("horizontal", true);
blurShader.setFloat("radius", radius);
glBindVertexArray(quad3VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[0]);
glBindTexture(GL_TEXTURE_2D, pingPongColorBuffer[1]);
blurShader.setBool("horizontal", false);
glBindVertexArray(quad3VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 1.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(backgroundVAO);
glBindTexture(GL_TEXTURE_2D, pingPongColorBuffer[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &quad1VAO);
glDeleteBuffers(1, &quad1VBO);
glDeleteVertexArrays(1, &quad2VAO);
glDeleteBuffers(1, &quad2VBO);
glDeleteVertexArrays(1, &quad3VAO);
glDeleteBuffers(1, &quad3VBO);
glDeleteVertexArrays(1, &backgroundVAO);
glDeleteBuffers(1, &backgroundVBO);
glfwTerminate();
return 0;
}
The full source code is here
The vertical edge is blurry. But the effect is reduced and covered by the vertical blur in the 2nd pass. Note, the vertical blur reinforced the vertical edge, because it blurs along this edge.
After the 1st pass (horizontal blur), the main color on the left side is still blue and the main color on the right side is still green and white. The vertical blur, mix the colors along the columns of the image. That causes that the transition along the columns between the left (blue) and right (green/white) becomes a noticeable edge.
If you change the order of the passed (1st vertical blur, 2nd horizontal blur), then the horizontal edge becomes visible again:
In general the algorithm works. Compare the result, when a diagonal blur is used:
void main(){
// [...]
if(horizontal){
for(int i=1;i<radius+1;i++){
vec2 off=vec2(i,i)/resolution;
// [...]
}
}else{
for(int i=1;i<radius+1;i++){
vec2 off=vec2(-i,i)/resolution;
// [...]
}
}
// [...]
}
I am facing an issue with font rendering. I am using the tutorial https://learnopengl.com/In-Practice/Text-Rendering for this.
I am developing the application in OpenGLESV3.
I have made different shader programs for 3D object drawing, for loading textures and lastly for the font rendering.
I am drawing the fonts after the 3D objects and texture drawing is done.
However the issue is that the font is not seen when i draw using orthographic projection. I am able to only see the 3D objects and texture. (Even though the shaders are different)
when I only draw font only with orthographic projection it is drawing perfectly.
I am sure it is the issue with the settings in projection, but i am unable to figure out the issue.
Can anybody help please ?
Here is the code inside my render loop
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, (GLsizei)SCR_WIDTH, (GLsizei)SCR_HEIGHT);
/********************************* 3D objects Rendering ********************************************************/
glUseProgram(shaderpgmPoints);
projection = glm::perspective(glm::radians(camspec.fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(shaderpgmPoints, "projection"), 1, false, &projection[0][0]);
view = glm::lookAt(camspec.cameraPos, camspec.cameraPos + camspec.cameraFront, camspec.cameraUp);
glUniformMatrix4fv(glGetUniformLocation(shaderpgmPoints, "view"), 1, false, &view[0][0]);
model = glm::mat4(1.0f);
glUniformMatrix4fv(glGetUniformLocation(shaderpgmPoints, "model"), 1, false, &model[0][0]);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, quadBufferObject);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)64);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindBuffer(GL_ARRAY_BUFFER, leftlaneBufferObject);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)192);
;
glEnable(GL_PROGRAM_POINT_SIZE);
glDrawArrays(GL_LINES, 0, 12);
glDisable(GL_BLEND);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindBuffer(GL_ARRAY_BUFFER, rightlaneBufferObject);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)32);
glLineWidth(10.0f);
glDrawArrays(GL_LINES, 0, 2);
glDisable(GL_BLEND);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glUseProgram(0);
/********************************* 3D objects Rendering -End ********************************************************/
/********************************* Texture Rendering ********************************************************/
glUseProgram(shaderpgmTexture);
glm::mat4 transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(0.0f, 0.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(shaderpgmTexture, "transform"), 1, false, &transform[0][0]);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUniform1i(glGetUniformLocation(shaderpgmTexture, "outputTexture"), 1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, camTexture);
glBindBuffer(GL_ARRAY_BUFFER, camTextureBufferObject);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glDrawArrays(GL_QUADS, 0, 4);
glDisable(GL_BLEND);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glUseProgram(0);
/********************************* Texture Rendering - End ********************************************************/
/********************************* Font Rendering ********************************************************/
glDisable(GL_DEPTH_TEST);
glUseProgram(shaderpgmFont);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glm::mat4 project = glm::ortho(0.0f, static_cast<GLfloat>(SCR_WIDTH), 0.0f, static_cast<GLfloat>(SCR_HEIGHT));
glUniformMatrix4fv(glGetUniformLocation(shaderpgmFont, "projection"), 1, false, &project[0][0]);
glUniform3f(glGetUniformLocation(shaderpgmFont, "textColor"), 1.0f, 0.0f, 0.0f);
glActiveTexture(GL_TEXTURE0);
GLfloat scale = 1.0f;
GLfloat x = 10.0f;
GLfloat y = 10.0f;
std::string text = "THE";
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 - (ch.Size.y - ch.Bearing.y) * scale;
GLfloat w = ch.Size.x * scale;
GLfloat h = ch.Size.y * scale;
GLfloat vertices[6][4] = {
{ 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 }
};
// Render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.TextureID);
// Update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, FontVertexBufferObject);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
}
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glUseProgram(0);
glEnable(GL_DEPTH_TEST);
/********************************* Font Rendering - End********************************************************/
I am Initializing the font buffer like this
glDisable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(shaderpgmFont);
// FreeType
FT_Library ft;
// All functions return a value different than 0 whenever an error occurred
if (FT_Init_FreeType(&ft))
std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;
// Load font as face
FT_Face face;
if (FT_New_Face(ft, "fonts/arial.ttf", 0, &face))
{
std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;
}
else
{
// Set size to load glyphs as
FT_Set_Pixel_Sizes(face, 0, 48);
// Disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Load first 128 characters of ASCII set
for (GLubyte c = 0; c < 128; c++)
{
// Load character glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
continue;
}
// Generate texture
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_ALPHA,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_ALPHA,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
// Set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Now store character for later use
Character character = {
texture,
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
face->glyph->advance.x
};
Characters.insert(std::pair<GLchar, Character>(c, character));
}
glBindTexture(GL_TEXTURE_2D, 0);
// Destroy FreeType once we're finished
FT_Done_Face(face);
FT_Done_FreeType(ft);
// Configure VAO/VBO for texture quads
glGenBuffers(1, &FontVertexBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, FontVertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glUseProgram(0);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
In your initialization function, you've set up some OpenGL state via glVertexAttribPointer, which later gets clobbered.
To fix this, modify your text-rendering code so that it makes two calls glVertexAttribPointer after glBindBuffer.
Could some one explain for me how can I render multiple objects with different textures in OpenGL?
I think that I'm nearly to the final result but at the moment I got stuck here and I don't know what I need to do next. Really need some helps!
At the moment this is what I have:
drawSphere(): draw an UV sphere based on number of longs and lats
int numberOfVerices = 0;
for(int i = 0; i <= lats; i++) {
double lat0 = M_PI * (-0.5 + (double) (i - 1) / lats);
double z0 = sin(lat0);
double zr0 = cos(lat0);
double lat1 = M_PI * (-0.5 + (double) i / lats);
double z1 = sin(lat1);
double zr1 = cos(lat1);
for(int j = 0; j <= longs; j++) {
double lng = 2 * M_PI * (double) (j - 1) / longs;
double x = cos(lng);
double y = sin(lng);
glNormal3f(x * zr0, y * zr0, z0);
vertices.push_back(x * zr0);
vertices.push_back(y * zr0);
vertices.push_back(z0);
indices.push_back(numberOfVerices);
numberOfVerices++;
vertices.push_back(x * zr1);
vertices.push_back(y * zr1);
vertices.push_back(z1);
indices.push_back(numberOfVerices);
numberOfVerices++;
}
indices.push_back(GL_PRIMITIVE_RESTART_FIXED_INDEX);
}
SetupGeometry(): this method is used to bind vertices and texture coordinates.
drawSphere(300, 300);
glGenBuffers(1, &vboVertex);
glBindBuffer(GL_ARRAY_BUFFER, vboVertex);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), &vertices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
glGenBuffers(1, &vboTexture);
glBindBuffer(GL_ARRAY_BUFFER, vboTexture);
glBufferData(GL_ARRAY_BUFFER, texture.size() * sizeof(GLfloat), &texture[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glGenBuffers(1, &vboIndex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndex);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
numsToDraw = indices.size();
nums = indices.size();
SetupShader(): create shader and compile shader
char text[1000];
int length;
vertexSource = filetobuf("space/sphere.vert");
fragmentSource = filetobuf("space/sphere.frag");
vertexShader = glCreateShader(GL_VERTEX_SHADER);
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertexShader, 1, (const GLchar**) &vertexSource, 0);
glShaderSource(fragmentShader, 1, (const GLchar**) &fragmentSource, 0);
fprintf(stderr, "Compiling vertex shader....\n");
glCompileShader(vertexShader);
fprintf(stderr, "Compiling fragment shader....\n");
glCompileShader(fragmentShader);
fprintf(stderr, "Done....\n");
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindAttribLocation(shaderProgram, 0, "in_Position");
glBindAttribLocation(shaderProgram, 1, "Texture_Coord");
printf("Linking program ... \n");
glLinkProgram(shaderProgram);
glGetProgramInfoLog(shaderProgram, 1000, &length, text);
if(length > 0){
fprintf(stderr, "Validate Shader Program\n%s\n", text );
}
glUseProgram(shaderProgram);
SetupTexture(char * filename): loading image, generate 2 arrays of textures, one to GL_TEXTURE0 and other one to GL_TEXTURE1
GLuint texture[2];
glGenTextures(2, texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
data = stbi_load(fileName, &w, &h, &n, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glUniform1i(glGetUniformLocation(shaderProgram, "texture_Sun"), 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_image_free(data);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture[1]);
data = stbi_load(fileName1, &w, &h, &n, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glUniform1i(glGetUniformLocation(shaderProgram, "texture_Earth"), 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_image_free(data);
Render(int i): rendering 2 objects, which is sphere.
glClearColor(0.0, 0.0, 0.0, 1.0);/* Make our background black */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndex);
glPushMatrix();
glLoadIdentity();
glm::mat4 Projection = glm::perspective(50.0f, 5.0f / 3.0f, 1.0f, 100.0f);
glm::mat4 View = glm::lookAt(
glm::vec3(0, 5, 2),
glm::vec3(0, 0, 0),
glm::vec3(0, 1, 0)
);
/* Animations */
GLfloat angle = (GLfloat) (i);
View = glm::translate(View, glm::vec3(2.0f, 0.0f, 0.0f));
View = glm::rotate(View, angle * 0.5f, glm::vec3(0.0f, 0.0f, 1.0f));
/* ******* */
glm::mat4 Model = glm::mat4(1.0f);
glm::mat4 MVP = Projection * View * Model;
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "mvpMatrix"), 1, GL_FALSE, glm::value_ptr(MVP));
glDrawElements(GL_QUAD_STRIP, numsToDraw, GL_UNSIGNED_INT, NULL);
glPopMatrix();
glPushMatrix();
glLoadIdentity();
glm::mat4 Projection1 = glm::perspective(50.0f, 5.0f / 3.0f, 1.0f, 100.0f);
glm::mat4 View1 = glm::lookAt(
glm::vec3(0, 5, 2),
glm::vec3(0, 0, 0),
glm::vec3(0, 1, 0)
);
/* Animations */
GLfloat angle1 = (GLfloat) (i);
View1 = glm::translate(View1, glm::vec3(-2.0f, 0.0f, 0.0f));
View1 = glm::rotate(View1, angle1 * -0.5f, glm::vec3(0.0f, 0.0f, 1.0f));
/* ******* */
glm::mat4 Model1 = glm::mat4(1.0f);
MVP = Projection1 * View1 * Model1;
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "mvpMatrix"), 1, GL_FALSE, glm::value_ptr(MVP));
glDrawElements(GL_QUAD_STRIP, nums, GL_UNSIGNED_INT, NULL);
glPopMatrix();
VertexShader:
#version 330 core
precision highp float;
attribute vec3 in_Position;
attribute vec3 in_Position1;
varying vec4 Texture_Coord;
uniform mat4 mvpMatrix;
void main(void){
gl_Position = mvpMatrix * vec4(in_Position, 1.0);
Texture_Coord = vec4(in_Position, 1.0);
}
FragmentShader:
#version 330 core
precision highp float;
varying vec4 Texture_Coord;
uniform sampler2D texture_Sun;
uniform sampler2D texture_Earth;
out vec4 FragColor;
void main(void){
vec2 longLat = vec2((atan(Texture_Coord.y, Texture_Coord.x)/3.1415926 + 1) * 0.5, (asin(Texture_Coord.z) / 3.1415926 + 0.5));
FragColor = texture2D(texture_Sun, longLat);
FragColor = texture2D(texture_Earth, longLat);
}
Main codes in main:
SetupGeomtry();
SetupShader();
SetupTexture("images/Earth.jpg", "images/sun.jpg");
/*
* Main loop
*/
int i = 0;
while(!glfwWindowShouldClose(window)){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Render(i+=1);
glfwSwapBuffers(window);
glfwPollEvents();
Sleep(10);
}
Normally you would load your textures (i.e. buffer them to GPU):
GLuint texture[2];
glGenTextures(2, texture);
glBindTexture(GL_TEXTURE_2D, texture[0]);
data = stbi_load(fileName, &w, &h, &n, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_image_free(data);
glBindTexture(GL_TEXTURE_2D, texture[1]);
data = stbi_load(fileName1, &w, &h, &n, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_image_free(data);
then before drawing each of your objects bind the relevant texture for said object:
// already selected the shader programme for both Sun and Earth
GLint textureLocation = glGetUniformLocation(shaderProgram, "texture_CelestialBody");
// optionally remember more locations for multi texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glUniform1i(textureLocation, 0);
// optionally bind more textures for multi texture shader...
// draw Sun now
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[1]);
glUniform1i(textureLocation, 0);
// optionally bind more textures for multi texture shader...
// draw Earth now
then the texture_CelestialBody identifies the texture for both the Sun and the Earth - i.e. the fragment shader would not discriminate between the two.