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
Related
I've made an OpenGL c++ program that can display some text into the screen. I've followed
some tutorials and got help from this question. With that, I've managed to write a function called add_border but I didn't quite understand how this all works and couldn't manage to display border on the screen.
Here is what I have so far:
text.fs
#version 300 es
precision mediump float;
in vec2 vUV;
uniform sampler2D u_texture;
uniform vec3 textColor;
out vec4 fragColor;
void main()
{
vec2 uv = vUV.xy;
float text = texture(u_texture, uv).r;
fragColor = vec4(textColor.rgb*text, text);
}
text.vs
#version 300 es
precision mediump float;
layout (location = 0) in vec4 in_attr;
out vec2 vUV;
uniform mat4 projection;
uniform mat4 model;
void main()
{
vUV = in_attr.zw;
gl_Position = projection * model * vec4(in_attr.xy, 1.0f, 1.0f);
}
Text.cpp
void Text::render_text(std::string text, float x, float y, float z, std::string hex_color, float angle_rad, bool has_bg)
{
static const int scale = 1;
y /= 2;
shader.use();
glUniform3f(glGetUniformLocation(shader.ID, "textColor"), 0.9, 0.9, 0.9);
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; )
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);
}
void Text::add_border(){
if(FT_Outline_New(ft_lib, 10000, 1000, &border) != 0){
VI_ERROR("Error");
}
FT_Stroker_New(ft_lib, &stroker);
FT_Stroker_Set(stroker, 2 * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
FT_Get_Glyph(face->glyph, &glyph);
FT_Glyph_Stroke(&glyph, stroker, false);
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(ft_lib, &border, ¶ms);
}
}
Text::Text(text_attributes&& atrib, int gl_width, int gl_height)
:SCR_WIDTH(gl_width), SCR_HEIGHT(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_lib))
{
VI_ERROR("ERROR::FREETYPE: Could not init FreeType Library");
exit(0);
}
std::string font_name = "./src/opengl/fonts/" + *atrib.__font_name + ".ttf";
if (FT_New_Face(ft_lib, font_name.c_str(), 0, &face)) {
VI_ERROR("ERROR::FREETYPE: Failed to load font");
exit(0) ;
} else {
FT_Set_Pixel_Sizes(face, 0, atrib.__font_size);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (unsigned char c = 0; c < 128; c++)
{
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
VI_ERROR("ERROR::FREETYTPE: Failed to load Glyph");
continue;
}
add_border();
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
);
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 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_Outline_Done(ft_lib, &border);
FT_Done_FreeType(ft_lib);
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);
}
I've read somewhere that changing FT_LOAD_RENDER to FT_LOAD_DEFAULT in FT_Load_Char() causes the text to disappear completely
Right now this code compiles and runs just fine, however there is no border around the text. I want to add some parameters to the add_border function to have it display different border's with the given parameters such as border thickness and border color.
What am I doing wrong? How can I have it display border like I want it to?
Based on the comments above:
The below example is based on my own lib (although very shortened), where i am collecting the coverage into an span array and render it later. It is also possible to render the coverage(s)/scanline directly onto a bitmap.
//structure to set the bounds
//and to hold a list of the coverage(s)
//basically the 'raw' data to produce a single glyph image
struct my_spans {
int x_min, y_min;
int x_max, y_max;
FT_Span *span_array; //needed later to reproduce the (glyph) image
};
struct my_spans spans; //initialize properly
//set the raster params accordingly
FT_Raster_Params params;
memset(¶ms, 0, sizeof(params));
params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT; //direct rendering
params.gray_spans = outlineRenderCallback; //our callback below
params.user = &spans; //userdata will be our struct
//will be called for each scanline, thus the single y coordinate
void outlineRenderCallback(
int y,
int count,
const FT_Span *ft_spans,
void *user
){
struct my_spans *spans = (struct my_spans*) user;
FT_Span span;
//increase array capacity by count
//foreach span
for (int i=0; i < count; ++i, ++ft_spans) {
//horizontal boundary
spans->x_min = min(spans->x_min, ft_spans->x);
spans->x_max = max(spans->x_max, ft_spans->x + ft_spans->len - 1);
//span copy, although possible to write into array directly
span.x = ft_spans->x;
span.y = y;
span.len = ft_spans->len;
span.coverage = ft_spans->coverage;
//push span to array, if not written directly
}
//vertical boundary
spans->y_min = min(spans->y_min, y);
spans->y_max = max(spans->y_max, y);
}
Reference:
FT_Outline_Render
FT_Raster_Params
FT_SpanFunc
FT_Span
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 place a square as background for my text, which is rendered by FreeType in OpenGL. But the text somehow cuts through my square like this:
See, the arc behind my square is ok. Arc is on 0.0, square is on 1.0, and the text is on 2.0 on Z axis.
This is how I create them:
auto arc2x = new OpenGL::Rendering::Models::Arc(
Container::Position(pos_x, pos_y, 0), Container::Color::GREEN, 50,
radius, 90, arc_degree);
arc2x->create();
arc2x->set_rotation(76, 0, 0, 1);
auto text1_back = new OpenGL::Rendering::Models::Quad(
Container::Position(pos_x, pos_y - radius, -0.1),
Container::Color::CYAN, 25, 25);
text1_back->create();
auto text1 = new OpenGL::Rendering::Models::Text(
"8", Container::Position(pos_x - 5, pos_y - radius / 1.5, 2), 22,
Container::Color::PINK);
text1->create();
My Text class:
// Text.cpp
#include "Text.h"
using namespace OpenGL::Rendering::Models;
Text::Text(const std::string& text, OpenGL::Container::Position position,
int font_size, OpenGL::Container::Color color) {
m_font_size = font_size;
m_scale = 1.0;
m_text = text;
float angle = 0;
this->color.r = color.r;
this->color.g = color.g;
this->color.b = color.b;
this->color.a = color.a;
matrix.xx = (FT_Fixed)(cos(angle) * 0x10000L);
matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L);
matrix.yx = (FT_Fixed)(sin(angle) * 0x10000L);
matrix.yy = (FT_Fixed)(cos(angle) * 0x10000L);
this->position.x = position.x;
this->position.y = position.y;
this->position.z = position.z;
if (FT_Init_FreeType(&font)) {
Log()->critical("Could not initalize Freetype library for fonts.");
}
if (FT_New_Face(font, "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf", 0,
&face)) {
Log()->critical("Could not load font. File is missing maybe?");
}
FT_Set_Char_Size(face, 0, m_font_size * 64, 300, 300);
FT_Set_Pixel_Sizes(face, 0, m_font_size);
if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) {
Log()->critical(
"Could not load a test glyph. The font is corrupted maybe?");
}
for (GLubyte c = 0; c < 128; ++c) {
FT_Set_Transform(face, &matrix, 0);
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
Log()->critical("Could not load glyph \"{}\"", c);
continue;
}
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);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
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 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));
}
FT_Done_Face(face);
FT_Done_FreeType(font);
}
void Text::create() {
GLuint vao;
GLuint vbo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL,
GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
(void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
this->vao = vao;
this->vbos.push_back(vbo);
this->set_program(OpenGL::Managers::ShaderManager::get_program("text"));
this->set_position(position.x, position.y, position.z);
}
void Text::draw() {
glUseProgram(this->program);
glUniformMatrix4fv(glGetUniformLocation(this->program, "model_matrix"), 1,
false, &model_matrix[0][0]);
glUniform4f(glGetUniformLocation(this->program, "text_color"), color.r,
color.g, color.b, color.a);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(this->vao);
GLfloat temp_x = 0;
GLfloat temp_y = 0;
std::string::const_iterator c;
for (c = m_text.begin(); c != m_text.end(); c++) {
Character ch = characters[*c];
GLfloat xpos = temp_x + ch.bearing.x * m_scale;
GLfloat ypos = temp_y - (ch.size.y - ch.bearing.y) * m_scale;
GLfloat w = ch.size.x * m_scale;
GLfloat h = ch.size.y * m_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} /**/
};
glBindTexture(GL_TEXTURE_2D, ch.texture_id);
glBindBuffer(GL_ARRAY_BUFFER, this->vbos[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
temp_x += (ch.advance >> 6) * m_scale;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Text::set_text(const std::string& a_text) {
if (!a_text.empty()) {
m_text = a_text;
} else {
Log()->info("Cannot set the text. Input seems to be empty.");
}
}
std::string Text::get_text() { return m_text; }
void Text::set_color(const Container::Color a_color) {
color.r = a_color.r;
color.g = a_color.g;
color.b = a_color.b;
color.a = a_color.a;
}
And my Quad class:
// Quad.cpp
#include "Quad.h"
using namespace OpenGL;
using namespace Rendering::Models;
Quad::Quad(Container::Position pos, Container::Color color, float width,
float height) {
position.x = pos.x;
position.y = pos.y;
position.z = pos.z;
this->color.x = color.r;
this->color.y = color.g;
this->color.z = color.b;
this->color.w = color.a;
this->width = width;
this->height = height;
}
Quad::~Quad() {}
void Quad::create() {
GLuint vao;
GLuint vbo;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
std::vector<Container::Vertex> vertices;
vertices.push_back(
Container::Vertex(glm::vec3(-1, -1, 0.0), color));
vertices.push_back(
Container::Vertex(glm::vec3(-1, 1, 0.0), color));
vertices.push_back(
Container::Vertex(glm::vec3(1, -1, 0.0), color));
vertices.push_back(
Container::Vertex(glm::vec3(1, 1, 0.0), color));
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Container::Vertex) * 4, &vertices[0],
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Container::Vertex),
(void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(
1, 4, GL_FLOAT, GL_FALSE, sizeof(Container::Vertex),
(void*)(offsetof(Container::Vertex, Container::Vertex::m_color)));
glBindVertexArray(0);
this->vao = vao;
this->vbos.push_back(vbo);
this->set_program(OpenGL::Managers::ShaderManager::get_program("shape"));
this->set_scale(width / 2, height / 2, 1.0);
this->set_position(position.x, position.y, position.z);
}
void Quad::draw() {
glUseProgram(this->program);
glUniformMatrix4fv(glGetUniformLocation(this->program, "model_matrix"), 1,
false, &model_matrix[0][0]);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
Any ideas why this is happening? If you need more code for diagnosis, I'll add it right away. Thank you so much.
You are drawing quads with alpha blending. The alpha blending allows the areas around the 8 to be transparent. However, as far as OpenGL is concerned this is just a modification of the fragment colour. The transparent fragments still get written to the depth buffer.
Your draw order appears to be unspecified. Judging by the comments below the question you are using std::map to hold your models. The order you get when iterating over that container will not necessarily be the same as the insertion order. This means you are potentially drawing a 'closer' element first, causing its depth values to get written to the depth buffer and therefore preventing any subsequent 'further away' elements from contributing to the fragment colour.
Unordered drawing is fine (and actually encouraged for performance reasons,) in the vast majority of cases. However, once you start using alpha blended transparency, order becomes important.
For alpha blended 2D elements the general approach to drawing is:
Turn off depth testing.
Order the elements in your cpu code based on depth.
Draw them from back to front (furthest away to closest).
Working with OpenGL, GLM, GLAD, GLFW and FreeType attempting to get fonts to render. I've stepped through to make sure no OpenGL errors had occured while rendering, nothing posts an error code, but nothing renders to the screen. I'm also not sure if I'm using glm's ortho function correctly, because i'm not sure where to actually use the variable:
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f);
This is my Draw Loop, it gets called after the scene Initializes:
void GameLoop::Run()
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
while (!glfwWindowShouldClose(m_game_window))
{
// Do That thing where you draw everything
}
}
The Font constructor which loads the font into textures:
Salem::Graphics::Font::Font(std::string p_font_path)
{
FT_Library ft;
if (FT_Init_FreeType(&ft))
{
std::cout << "Error loading FreeType library" << std::endl;
}
FT_Face face;
if (FT_New_Face(ft, p_font_path.c_str(), 0, &face))
{
std::cout << "Error loading font;\n" << p_font_path << std::endl;
}
FT_Set_Pixel_Sizes(face, 0, 28);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Disable byte-alignment restriction
for (GLubyte c = 0; c < 128; c++)
{
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
std::cout << "Failed to load a character glyph." << std::endl;
continue;
}
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);
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 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
};
m_characters.insert(std::make_pair(c, character));
}
FT_Done_Face(face);
FT_Done_FreeType(ft);
std::cout << "Font loaded. " << std::endl;
}
My text's Initialize function:
void Text::Initialize()
{
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f);
glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vbo);
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, nullptr, m_draw_method);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
And my text's draw function:
void Text::Draw()
{
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f);
shader()->Use();
glUniform3f(glGetUniformLocation(shader()->id(), "textColor"), m_color.x, m_color.y, m_color.z);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(m_vao);
for (std::string::const_iterator c = m_text.begin(); c != m_text.end(); ++c)
{
Character ch = m_font.characters()[*c];
GLfloat xPosition = m_position.x + ch.Bearing.x * m_scale;
GLfloat yPosition = m_position.y - (ch.Size.y - ch.Bearing.y) * m_scale;
GLfloat width = ch.Size.x * m_scale;
GLfloat height = ch.Size.y * m_scale;
GLfloat vertices[6][4] = {
{xPosition, yPosition + height, 0.0, 0.0},
{xPosition, yPosition, 0.0, 1.0},
{xPosition + width, yPosition, 1.0, 1.0},
{xPosition, yPosition + height, 0.0, 0.0},
{xPosition + width, yPosition, 1.0, 1.0},
{xPosition + width, yPosition + height, 1.0, 0.0}
};
glBindTexture(GL_TEXTURE_2D, ch.TextureID);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
//glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
m_position.x += (ch.Advance >> 6) * m_scale;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
Didn't want the post to be too long, if my shaders are needed, let me know.
Upon breaking up the code, i found a helpful resource which lead me to my problem. From https://learnopengl.com/#!In-Practice/Text-Rendering Project source, I found that my orthographic projection variable had to be passed to my shader program when I was Constructing the font. I did this by add the following code to my Font Constructor:
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f);
p_shader->Use();
glUniformMatrix4fv(glGetUniformLocation(p_shader->id(), "projection"), 1, GL_FALSE, glm::value_ptr(projection));
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.