What is the simplest way of applying a normal map to a textured four vertex polygon in SDL? Do you have to create a shader or is there a simpler way? I have here my code that creates a textured rotating cube, with a rotating satellite light.
What additions to my code do I need to do if I want to use the generated texture (tex_Norm = generateTexture()) as a normal map also?
#include <windows.h>
#include <SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <GL/glext.h>
#include <math.h>
void initAll();
void setupBox();
void mainloop();
unsigned int generateTexture();
void handle_inputs();
void updateScreen();
void clean_up();
int scrWidth, scrHeight, flags;
bool bQuit = false;
float angle = 0.0f;
GLuint tex_Box, tex_Norm;
struct sVert
{
float x;
float y;
float z;
};
class cPolygon
{
public:
int v[4];
void fillverts(int v1, int v2, int v3, int v4) {
v[0] = v1;
v[1] = v2;
v[2] = v3;
v[3] = v4;
}
} p[6];
sVert pv[8];
int main(int argc, char *argv[])
{
initAll();
mainloop();
clean_up();
return 0;
}
void initAll()
{
scrWidth = 800;
scrHeight = 600;
SDL_InitSubSystem(SDL_INIT_VIDEO);
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
flags = SDL_OPENGL | SDL_ANYFORMAT ;
SDL_SetVideoMode(scrWidth, scrHeight, 16, flags);
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
gluPerspective( 45.0f, (GLfloat)scrWidth/(GLfloat)scrHeight, 1.0f, 500.0f );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
glEnable (GL_DEPTH_TEST);
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
SDL_WM_SetCaption( "Normal Mapping", NULL );
tex_Box = generateTexture();
tex_Norm = generateTexture();
setupBox();
}
void setupBox()
{
for (int z=0;z<2;z++)
for (int y=0;y<2;y++)
for (int x=0;x<2;x++)
{
pv[x+y*2+z*4].x = -1.0+x;
pv[x+y*2+z*4].y = -1.0+y;
pv[x+y*2+z*4].z = -1.0+z;
}
// Box object
p[0].fillverts (0, 1, 3, 2); // above
p[1].fillverts (4, 5, 1, 0); // behind
p[2].fillverts (6, 7, 3, 2); // in front
p[3].fillverts (5, 7, 3, 1); // right
p[4].fillverts (0, 2, 6, 4); // left
p[5].fillverts (7, 6, 4, 5); // below
}
unsigned int generateTexture()
{
BYTE data[128*128*3];
unsigned int id;
for (int x=0;x<128;x++)
for (int y=0;y<128;y++)
{
data[y*128*3+x*3+0] = x; // Red
data[y*128*3+x*3+1] = y; // Green
data[y*128*3+x*3+2] = 128-(abs(64-x)+abs(64-y)); // Blue
}
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGB, GL_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
return id;
}
void mainloop()
{
while(bQuit == false)
{
handle_inputs();
updateScreen();
angle += 1.5f;
Sleep(50);
}
}
void handle_inputs()
{
SDL_PumpEvents();
Uint8 * keystate = SDL_GetKeyState(NULL);
if(keystate[SDLK_ESCAPE]) bQuit = true;
}
void updateScreen()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
glTranslatef(0.0f, 0.0f, -3.0f);
glRotatef(-angle*2.0, 1.0f, 1.0f, 1.0f);
// Setup a light source
float lpos[] = {0.0, 0.0, 2.0, 1.0}; // position
float ldif[] = {1.0, 1.0, 1.0, 1.0}; // diffuse
float lamb[] = {0.3, 0.3, 0.2, 1.0}; // ambient
glLightfv(GL_LIGHT0, GL_POSITION, lpos);
glLightfv(GL_LIGHT0, GL_DIFFUSE, ldif);
glLightfv(GL_LIGHT0, GL_AMBIENT, lamb);
glPopMatrix();
glPushMatrix();
glTranslatef(0.5f, 0.5f, -3.0f);
glRotatef(angle, 1.0f, 1.0f, 1.0f);
// Draw box object
glBindTexture(GL_TEXTURE_2D, tex_Box);
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
for(int pi=0; pi<6; pi++)
for(int vi=0; vi<4; vi++)
{
switch(vi) {
case 0: glTexCoord2d(0.0,2.0); break;
case 1: glTexCoord2d(0.0,0.0); break;
case 2: glTexCoord2d(2.0,0.0); break;
case 3: glTexCoord2d(2.0,2.0); break; };
glVertex3f( pv[ p[pi].v[vi] ].x,
pv[ p[pi].v[vi] ].y,
pv[ p[pi].v[vi] ].z );
};
glEnd();
glDisable(GL_TEXTURE_2D);
glPopMatrix();
glFinish();
SDL_GL_SwapBuffers();
}
void clean_up()
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
glDeleteTextures(1, &tex_Box);
glDeleteTextures(1, &tex_Norm);
SDL_Quit();
}
Sorry, but if you want to use modern OpenGL features, you need modern OpenGL, which means shaders. :( That also means all your lighting needs to be done manually in the shader, so your program is going to get quite a bit more complicated, but that's the price you pay for cool images.
Explaining in detail how to do normal mapping wouldn't fit here, but there are very nice tutorials out there, for example http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/.
Good luck!
After some hours adventure into the world of GLSL and some help from the answer in this question: SDL OpenGL in C++, Texture Shader missing the texture
... I finally made a Normal map out of my generated texture. Here is the result:
#include <windows.h>
#include <SDL.h>
#include <GL/glew.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdio.h>
#include <math.h>
#include <string>
using namespace std;
#define LOG_SIZE 10000
void initAll();
void setupBox();
void mainloop();
unsigned int generateTexture();
void handle_inputs();
void updateScreen();
void clean_up();
void dbpf(int, const char *, ...);
int scrWidth, scrHeight, flags;
bool bQuit = false;
float angle = 0.0f;
va_list m;
int db_threashold = 0;
GLint status;
GLchar elog[LOG_SIZE];
GLint rLength = 0;
GLuint tex_Box, tex_Norm;
std::string vertex_source, fragment_source;
GLuint shader_program, vertex_shader, fragment_shader;
GLuint vao, vbo;
const char *source;
int length;
struct sVert
{
float x;
float y;
float z;
};
class cPolygon
{
public:
int v[4];
void fillverts(int v1, int v2, int v3, int v4) {
v[0] = v1;
v[1] = v2;
v[2] = v3;
v[3] = v4;
}
} p[6];
sVert pv[8];
int main(int argc, char *argv[])
{
initAll();
mainloop();
clean_up();
return 0;
}
void initAll()
{
scrWidth = 800;
scrHeight = 600;
vertex_source =
"#version 330\n"
"in vec3 Position;\n"
"in vec2 TexCoord;\n"
"out vec3 oColor;\n"
"out vec2 oTexcoord;\n"
"void main() {\n"
" oTexcoord = TexCoord;\n"
" gl_Position = gl_ModelViewProjectionMatrix*vec4(Position, 1.0);\n"
"}\n";
fragment_source =
"#version 330\n"
"in vec2 oTexcoord;\n"
"out vec4 oColor;\n"
"uniform sampler2D tex;\n"
"uniform sampler2D tex_norm;\n"
"void main() {\n"
" vec4 lightpos = normalize(-gl_ModelViewProjectionMatrix*vec4(1.0, -1.0, -1.5, 1.0));\n"
" vec3 tmpNorm = normalize(texture2D(tex_norm, oTexcoord).rgb * 2.0 - 1.0);\n"
" float difuse = max(dot(tmpNorm, lightpos.xyz), 0.0);\n"
" vec3 tmpcolor = difuse * texture2D(tex, oTexcoord).rgb;\n"
" oColor = vec4(tmpcolor, 1.0);\n"
"}\n";
SDL_InitSubSystem(SDL_INIT_VIDEO);
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
flags = SDL_OPENGL | SDL_ANYFORMAT ;
SDL_SetVideoMode(scrWidth, scrHeight, 16, flags);
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
gluPerspective( 45.0f, (GLfloat)scrWidth/(GLfloat)scrHeight, 1.0f, 500.0f );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
glEnable (GL_DEPTH_TEST);
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
SDL_WM_SetCaption( "Normal map", NULL );
glewInit();
// vertex shader
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
source = vertex_source.c_str();
length = vertex_source.size();
glShaderSource(vertex_shader, 1, &source, &length);
glCompileShader(vertex_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &status);
glGetShaderInfoLog(vertex_shader, LOG_SIZE, &rLength, elog);
dbpf(10, "Compile vertex log: \n %s \n", elog);
// fragment shader
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
source = fragment_source.c_str();
length = fragment_source.size();
glShaderSource(fragment_shader, 1, &source, &length);
glCompileShader(fragment_shader);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &status);
glGetShaderInfoLog(fragment_shader, LOG_SIZE, &rLength, elog);
dbpf(10, "Compile fragment log: \n %s \n", elog);
// create program
shader_program = glCreateProgram();
// attach shaders
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
// link the program and check for errors
glLinkProgram(shader_program);
glGetProgramiv(shader_program, GL_LINK_STATUS, &status);
glGetProgramInfoLog(shader_program, LOG_SIZE, &rLength, elog);
dbpf(10, "Link log: \n %s \n", elog);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
setupBox();
GLfloat vd[6*5*6];
for(int pi=0; pi<6; pi++)
{
vd[pi*30+ 0] = pv[ p[pi].v[0] ].x;
vd[pi*30+ 1] = pv[ p[pi].v[0] ].y;
vd[pi*30+ 2] = pv[ p[pi].v[0] ].z;
vd[pi*30+ 3] = 0.0;
vd[pi*30+ 4] = 1.0;
vd[pi*30+ 5] = pv[ p[pi].v[1] ].x;
vd[pi*30+ 6] = pv[ p[pi].v[1] ].y;
vd[pi*30+ 7] = pv[ p[pi].v[1] ].z;
vd[pi*30+ 8] = 0.0;
vd[pi*30+ 9] = 0.0;
vd[pi*30+10] = pv[ p[pi].v[2] ].x;
vd[pi*30+11] = pv[ p[pi].v[2] ].y;
vd[pi*30+12] = pv[ p[pi].v[2] ].z;
vd[pi*30+13] = 1.0;
vd[pi*30+14] = 0.0;
vd[pi*30+15] = pv[ p[pi].v[0] ].x;
vd[pi*30+16] = pv[ p[pi].v[0] ].y;
vd[pi*30+17] = pv[ p[pi].v[0] ].z;
vd[pi*30+18] = 0.0;
vd[pi*30+19] = 1.0;
vd[pi*30+20] = pv[ p[pi].v[2] ].x;
vd[pi*30+21] = pv[ p[pi].v[2] ].y;
vd[pi*30+22] = pv[ p[pi].v[2] ].z;
vd[pi*30+23] = 1.0;
vd[pi*30+24] = 0.0;
vd[pi*30+25] = pv[ p[pi].v[3] ].x;
vd[pi*30+26] = pv[ p[pi].v[3] ].y;
vd[pi*30+27] = pv[ p[pi].v[3] ].z;
vd[pi*30+28] = 1.0;
vd[pi*30+29] = 1.0;
}
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*6*5*6, vd, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (char*)0 + 0*sizeof(GLfloat));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (char*)0 + 3*sizeof(GLfloat));
tex_Box = generateTexture();
tex_Norm = generateTexture();
}
void setupBox()
{
for (int z=0;z<2;z++)
for (int y=0;y<2;y++)
for (int x=0;x<2;x++)
{
pv[x+y*2+z*4].x = -1.0+x;
pv[x+y*2+z*4].y = -1.0+y;
pv[x+y*2+z*4].z = -1.0+z;
}
p[0].fillverts (0, 1, 3, 2); // above
p[1].fillverts (4, 5, 1, 0); // behind
p[2].fillverts (6, 7, 3, 2); // in front
p[3].fillverts (5, 7, 3, 1); // right
p[4].fillverts (0, 2, 6, 4); // left
p[5].fillverts (7, 6, 4, 5); // below
}
unsigned int generateTexture()
{
BYTE data[128*128*3];
unsigned int id;
for (int x=0;x<128;x++)
for (int y=0;y<128;y++)
{
data[y*128*3+x*3+0] = x; // Red
data[y*128*3+x*3+1] = y; // Green
data[y*128*3+x*3+2] = 128-(abs(64-x)+abs(64-y)); // Blue
}
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
return id;
}
void mainloop()
{
while(bQuit == false)
{
handle_inputs();
updateScreen();
angle += 1.5f;
Sleep(50);
}
}
void handle_inputs()
{
SDL_PumpEvents();
Uint8 * keystate = SDL_GetKeyState(NULL);
if(keystate[SDLK_ESCAPE]) bQuit = true;
}
void updateScreen()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt (2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// use the shader program
glUseProgram(shader_program);
// bind the vao
glBindVertexArray(vao);
// rotation
glRotatef(angle, 1.0, 0.0, 0.0); //rotate on the x axis
glRotatef(angle, 0.0, 1.0, 0.0); //rotate on the y axis
glRotatef(angle, 0.0, 0.0, 1.0); //rotate on the z axis
// bind texture
glActiveTexture(GL_TEXTURE0);
int loc = glGetUniformLocation(shader_program, "tex");
glUniform1i(loc, 0);
glBindTexture(GL_TEXTURE_2D, tex_Box);
// bind normal texture
glActiveTexture(GL_TEXTURE1);
loc = glGetUniformLocation(shader_program, "tex_norm");
glUniform1i(loc, 1);
glBindTexture(GL_TEXTURE_2D, tex_Norm);
// draw
glDrawArrays(GL_TRIANGLES, 0, 6*6);
// cleanup
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glUseProgram(0);
SDL_GL_SwapBuffers();
}
void clean_up()
{
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDetachShader(shader_program, vertex_shader);
glDetachShader(shader_program, fragment_shader);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
glDeleteProgram(shader_program);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
glDeleteTextures(1, &tex_Box);
glDeleteTextures(1, &tex_Norm);
SDL_Quit();
}
void dbpf(int t, const char * msg, ...)
{
va_start(m, msg);
if (t >= db_threashold) vfprintf(stderr, msg, m);
va_end(m);
}
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 simply trying to create a triangle in the center and rotate the camera around the center. (0,0,0). the code is whipped from multiple tutorial sources and i suspect the problem could be somewhere from the glm::perspective/lookat or gl:projection/model matrix before triangle draw calls.
because that's where my general confusion is at right now.
any help is greatly appreciated so i can move on with my life. thank you in advance.
//sudo g++ -o sdl main.cpp -lSDL2_image -lGL -lGLU -lglut -lX11 -lGLEW `sdl2-config --cflags --libs`
#include <iostream>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include "glm/gtc/matrix_transform.hpp"
#include <glm/gtc/type_ptr.hpp>
#include <SDL2/SDL.h>
#include <string>
#include <GL/gl.h>
std::string programName = "SDL2/OpenGL";
SDL_Window *mainWindow;
SDL_GLContext mainContext;
void Calculate()
{
float radius = 2.0f;
float camX = sin(SDL_GetTicks()*0.001) * radius;
float camZ = cos(SDL_GetTicks()*0.001) * radius;
glm::mat4 perspecive_mat = glm::perspective(
45.0f, 1.0f / 1.0f, 0.1f, 100.0f );
glm::mat4 view_mat = glm::lookAt(
glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0) );
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(glm::value_ptr(perspecive_mat));
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(glm::value_ptr(view_mat));
}
void Render()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float vertexCoords[24] = { // Coordinates for the vertices of a cube.
1,1,1, 1,1,-1, 1,-1,-1, 1,-1,1,
-1,1,1, -1,1,-1, -1,-1,-1, -1,-1,1 };
float vertexColors[24] = { // An RGB color value for each vertex
1,1,1, 1,0,0, 1,1,0, 0,1,0,
0,0,1, 1,0,1, 0,0,0, 0,1,1 };
int elementArray[24] = { // Vertex numbers for the six faces.
0,1,2,3, 0,3,7,4, 0,4,5,1,
6,2,1,5, 6,5,4,7, 6,7,3,2 };
glVertexPointer( 3, GL_FLOAT, 0, vertexCoords );
glColorPointer( 3, GL_FLOAT, 0, vertexColors );
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glDrawElements( GL_QUADS, 24, GL_UNSIGNED_INT, elementArray );
SDL_GL_SwapWindow(mainWindow);
}
bool Loop()
{
while (true ){
SDL_Event event;
while ( SDL_PollEvent( &event ) ){
switch ( event.type ){
case SDL_QUIT :
SDL_Quit();
return 0;
case SDL_KEYDOWN :
std::cout<<"Key Down"<<std::endl;
break;
case SDL_KEYUP :
std::cout<<"Key Up"<<std::endl;
break;
case SDL_MOUSEBUTTONDOWN :
case SDL_MOUSEBUTTONUP :
case SDL_MOUSEMOTION :
default :
break;
}
}
Calculate();
Render();
}
}
void CheckSDLError(int line = -1){
std::string error = SDL_GetError();
if (error != "")
{
std::cout << "SLD Error : " << error << std::endl;
if (line != -1)
std::cout << "\nLine : " << line << std::endl;
SDL_ClearError();
}
}
void Cleanup(){
SDL_GL_DeleteContext(mainContext);
SDL_DestroyWindow(mainWindow );
SDL_Quit();
}
int main(int argc, char *argv[]){
if (SDL_Init(SDL_INIT_VIDEO) < 0){
std::cout << "Failed to init SDL\n";
return false;
}
mainWindow = SDL_CreateWindow(programName.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,512, 512, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
if (!mainWindow )
{
std::cout << "Unable to create window\n"<< std::endl;;
CheckSDLError(__LINE__);
return false;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
mainContext = SDL_GL_CreateContext(mainWindow );
// This makes our buffer swap syncronized with the monitor's vertical refresh
// ( which means it enables v-sync)
// Setting this to 0 will disable V-sync
// Which means our application could run at unlimited fps
SDL_GL_SetSwapInterval(1);
// Init GLEW
glewExperimental = GL_TRUE;
glewInit();
// Enable blending so that we can have transparanet object
glEnable(GL_BLEND ) ;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Enable depth testing so that closer triangles will hide the triangles farther away
glEnable( GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
Loop();
Cleanup();
return 0;
}
Note, that drawing by glBegin/glEnd sequences and the fixed function pipeline matrix stack is deprecated since decades.
Read about Fixed Function Pipeline and see Vertex Specification and Shader for a state of the art way of rendering.
If you want to use the deprecated way of drawing, then you have to use a compatibility profile context instead of a core profile context (see OpenGL Context):
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
Further note, that glm::lookAt and glm::perspective don't set any OpenGL states or even fixed function pipline matrices. glm - OpenGL Mathematics is a math library, to do calculations related to OpenGL. Both function return a 4*4 matrix.
You can use glLoadMatrixf to load a matrix return by a glm function to the OpenGL fixed function pipeline matrix stack:
#include <glm/glm.hpp>
#include "glm/gtc/matrix_transform.hpp"
#include <glm/gtc/type_ptr.hpp>
void Calculate()
{
float radius = 2.0f;
float camX = sin(SDL_GetTicks()*0.001) * radius;
float camZ = cos(SDL_GetTicks()*0.001) * radius;
glm::mat4 perspecive_mat = glm::perspective(
45.0f, 1.0f / 1.0f, 0.1f, 100.0f );
glm::mat4 view_mat = glm::lookAt(
glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0) );
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(glm::value_ptr(perspecive_mat));
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(glm::value_ptr(view_mat));
}
If you want to use a core profile, then you have to create a simple Shader program:
#include <string>
std::string sh_vert = R"(
#version 150 core
in vec3 v_pos;
in vec4 v_col;
out vec4 color;
uniform mat4 projection;
uniform mat4 view;
void main()
{
color = v_col;
gl_Position = projection * view * vec4(v_pos, 1.0);
}
)";
std::string sh_frag = R"(
#version 150 core
in vec4 color;
void main()
{
gl_FragColor = color;
}
)";
Compile and link the program:
#include <vector>
GLuint CreateShader(GLenum type, const char *code)
{
GLuint shaderObj = glCreateShader(type);
glShaderSource(shaderObj, 1, &code, nullptr);
glCompileShader(shaderObj);
GLint status = GL_TRUE;
glGetShaderiv(shaderObj, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint logLen;
glGetShaderiv(shaderObj, GL_INFO_LOG_LENGTH, &logLen);
std::vector< char >log(logLen);
GLsizei written;
glGetShaderInfoLog(shaderObj, logLen, &written, log.data());
std::cout << "compile error:" << std::endl << log.data() << std::endl;
}
return shaderObj;
}
GLuint CreateProgram()
{
GLuint vShObj = CreateShader(GL_VERTEX_SHADER, sh_vert.c_str());
GLuint fShObj = CreateShader(GL_FRAGMENT_SHADER, sh_frag.c_str());
GLuint progObj = glCreateProgram();
glAttachShader(progObj, vShObj);
glAttachShader(progObj, fShObj);
glLinkProgram(progObj);
GLint status = GL_TRUE;
glGetProgramiv(progObj, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint logLen;
glGetProgramiv(progObj, GL_INFO_LOG_LENGTH, &logLen);
std::vector< char >log(logLen);
GLsizei written;
glGetProgramInfoLog(progObj, logLen, &written, log.data());
std::cout << "link error:" << std::endl << log.data() << std::endl;
}
return progObj;
}
Get the attribute and uniform locations:
GLuint prog;
GLint pos_attr, col_attr, proj_loc, view_loc;
void Init()
{
prog = CreateProgram();
pos_attr = glGetAttribLocation(prog, "v_pos");
col_attr = glGetAttribLocation(prog, "v_col");
proj_loc = glGetUniformLocation(prog, "projection");
view_loc = glGetUniformLocation(prog, "view");
// ....
}
Create a Vertex Array Object:
GLuint vao;
void Init()
{
// ....
const std::vector<float> varray
{
// x y z red green blue
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f
};
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, varray.size()*sizeof(float), varray.data(), GL_STATIC_DRAW);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glVertexAttribPointer(pos_attr, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), 0);
glEnableVertexAttribArray(pos_attr);
glVertexAttribPointer(col_attr, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));
glEnableVertexAttribArray(col_attr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
Installs the program object as part of current rendering state and set the uniform variables:
void Calculate()
{
float radius = 2.0f;
float camX = sin(SDL_GetTicks()*0.001) * radius;
float camZ = cos(SDL_GetTicks()*0.001) * radius;
glm::mat4 perspecive_mat = glm::perspective(
45.0f, 1.0f / 1.0f, 0.1f, 100.0f );
glm::mat4 view_mat = glm::lookAt(
glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0) );
glUseProgram(prog);
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, glm::value_ptr(perspecive_mat));
glUniformMatrix4fv(view_loc, 1, GL_FALSE, glm::value_ptr(view_mat));
}
Finally draw the triangle:
void Render()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
SDL_GL_SwapWindow(mainWindow);
}
bool Loop()
{
Init();
while (true) {
// ....
Calculate();
Render();
}
}
I have written application that runs on an embedded device, but all I see on display is the surface area filled with color as specified by me in glViewPort.
Text does not appear.
I have glError prints to see if some gl API call has failed, but nothing is failing.
I also do not have FT library error.
Though my prints show face->glyph->bitmap.width face->glyph->bitmap_left as zero for any character that I load.
// ******** CODE BELOW ******//
int OpenGlInit()
{
/* Here is the Init code for embedded graphics
This works fine.*/
glViewport(0, 0, 800, 600);
PrintGlError();
char vShaderStr[] =
"attribute vec4 vertex;\n"
"varying vec2 TexCoords;\n"
"uniform vec4 projection;\n"
"void main()\n"
"{\n"
"gl_Position = projection * vec4(vertex.xy, 0, 1);\n"
"TexCoords = vertex.zw;\n"
"}\n";
char fShaderStr[]=
"varying vec2 TexCoords;\n"
"uniform sampler2D text;\n"
"uniform vec4 color;\n"
"void main(void) {\n"
"gl_FragColor = vec4(1, 1, 1, texture2D(text, TexCoords).r) * color;\n"
"}\n";
GLuint vertexShader;
GLuint fragmentShader;
GLint linked;
vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
ProgramObject = glCreateProgram();
PrintGlError();
if (ProgramObject == 0) {
printf("DEBUG- glCreateProgram() returned 0\n");
return 0;
}
glAttachShader( ProgramObject, vertexShader );
PrintGlError();
glAttachShader( ProgramObject, fragmentShader );
PrintGlError();
glLinkProgram( ProgramObject );
PrintGlError();
glGetProgramiv( ProgramObject, GL_LINK_STATUS, &linked );
PrintGlError();
if (!linked)
{
printf("failed to link program\n");
GLint infoLen = 0;
glGetProgramiv( ProgramObject, GL_INFO_LOG_LENGTH, &infoLen);
PrintGlError();
if (infoLen > 1)
{
char infoLog[infoLen];
glGetProgramInfoLog( ProgramObject, infoLen, NULL, infoLog );
PrintGlError();
printf("Error linking program:\n%s\n", infoLog);
}
glDeleteProgram( ProgramObject );
PrintGlError();
return GL_FALSE;
}
printf("\nAfter Linked ProgramObject and compiled shaders");
printf("\nBefore Gen Buffers");
glGenBuffers(1, &VBO);
PrintGlError();
printf("\nAfter Gen Buffers");
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
PrintGlError();
glClear(GL_COLOR_BUFFER_BIT);
PrintGlError();
glEnable(GL_BLEND);
PrintGlError();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
PrintGlError();
if(glGetUniformLocation(ProgramObject, "color") == -1)
printf("\n ERROR uniform color is -1");
GLfloat black[4] = {1, 0, 0, 1};
glUniform4fv(glGetUniformLocation(ProgramObject, "color"), 1, black);
PrintGlError();
printf("\nAfter Matrix4fv");
float sx = 2.0 / 800;
float sy = 2.0 / 600;
RenderText("This is sample text", -1 + 8 * sx, 1 - 50 * sy, sx, sy);
printf("DEBUG\n\n\n\n\n\n\n\n\n\n\n");
RenderText("(C) LearnOpenGL.com", -1 + 8.5 * sx, 1 - 100.5 * sy, sx, sy);
eglSwapBuffers(egldisplay, eglsurface);
PrintGlError();
sleep(30);
glDeleteProgram(ProgramObject);
}
void RenderText(std::string message, GLfloat x, GLfloat y, GLfloat sx, GLfloat sy)
{
FT_Library ft;
FT_Face face;
if(FT_Init_FreeType(&ft))
std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;
if(FT_New_Face(ft, "/usr/share/fonts/tt7268mi_.ttf", 0, &face))
std::cout << "ERROR::FREETYPE: Failed to load calibri display" << std::endl;
if(FT_Set_Char_Size(face, 0, 16*64, 300, 300))
std::cout << "ERROR::FREETYPE: Failed to set char size" << std::endl;
printf("\nDEBUG after freetype\n");
if(FT_Set_Pixel_Sizes(face, 0, 48))
std::cout << "ERROR::FREETYPE: Failed to set pixel size" << std::endl;
printf("DEBUG after pixel sizes\n");
glActiveTexture(GL_TEXTURE0);
PrintGlError();
GLuint text;
glGenTextures(1, &text);
PrintGlError();
glBindTexture(GL_TEXTURE_2D, text);
PrintGlError();
if (glGetUniformLocation(ProgramObject, "text") == -1)
printf("\n ERROR uniform text is -1");
glUniform1i(glGetUniformLocation(ProgramObject, "text"), 0);
PrintGlError();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
PrintGlError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
PrintGlError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
PrintGlError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
PrintGlError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
PrintGlError();
int vPosition = glGetAttribLocation(ProgramObject, "vertex");
if (vPosition == -1)
printf("\nERROR vPosition is -1");
PrintGlError();
glEnableVertexAttribArray(vPosition);
PrintGlError();
glBindBuffer(GL_ARRAY_BUFFER, VBO);
PrintGlError();
//glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
PrintGlError();
std::string::const_iterator ch;
for (ch = message.begin(); ch != message.end(); ch++)
{
const char c = *ch;
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
continue;
}
FT_UInt glyph_index = FT_Get_Char_Index(face, c);
printf("\nglyphindex is: %u",glyph_index);
FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER);
printf("\nChar: %c, facewidth:%f, faceheight:%f", c, (face->glyph->bitmap.width), (face->glyph->bitmap.rows));
printf("\nfaceleft:%f, facetop:%f ", (face->glyph->bitmap_left), (face->glyph->bitmap_top));
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
PrintGlError();
GLfloat xpos = x + (face->glyph->bitmap_left) * sx;
GLfloat ypos = -y - (face->glyph->bitmap_top) * sy;
GLfloat w = (face->glyph->bitmap.width) * sx;
GLfloat h = (face->glyph->bitmap.rows) * sy;
printf("\n xpos:%f ypos:%f w:%f h:%f", xpos, ypos, w, h);
/*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 }
};*/
point vertices[4] = {
{xpos, -ypos , 0, 0},
{xpos + w, -ypos , 1, 0},
{xpos, -ypos - h, 0, 1},
{xpos + w, -ypos - h, 1, 1},
};
glBufferData(GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_DYNAMIC_DRAW);
PrintGlError();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
PrintGlError();
x += (face->glyph->advance.x/64) * sx;
y += (face->glyph->advance.y/64) * sy;
}
glDeleteTextures(1, &text);
glDisableVertexAttribArray(vPosition);
FT_Done_Face(face);
FT_Done_FreeType(ft);
}
void PrintGlError()
{
GLenum ErrorCheckValue = glGetError();
if (ErrorCheckValue != GL_NO_ERROR)
{
fprintf(stderr,"\nGL ERROR:%d", ErrorCheckValue);
}
}
GLuint LoadShader(GLenum type, const char *shaderSrc)
{
GLuint shader;
GLint compiled;
shader = glCreateShader( type );
if (shader == 0) {
return 0;
}
glShaderSource( shader, 1, &shaderSrc, NULL);
PrintGlError();
glCompileShader(shader);
PrintGlError();
glGetShaderiv( shader, GL_COMPILE_STATUS, &compiled);
PrintGlError();
if (!compiled)
{
printf("failed to compile shader\n");
GLint infoLen = 0;
glGetShaderiv(shader,GL_INFO_LOG_LENGTH, &infoLen);
PrintGlError();
if (infoLen > 1) {
char infoLog[infoLen];
glGetProgramInfoLog( ProgramObject, infoLen, NULL, infoLog );
PrintGlError();
printf("Error compiling shader:\n%s\n", infoLog);
}
glDeleteShader(shader);
PrintGlError();
return 0;
}
return shader;
}
There are some issues in your program.
The bitmap which is generated by FT_Load_Glyph has only one color channel. This means that the 7th parameter of glTexImage2D has to be GL_RED, when you define the texture image:
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_RED, // <--------------- GL_RED instead of GL_RGBA
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
You have missed to install the shader program object as part of current rendering state by glUseProgram. This has to be done after the program is linked ([glLinkProgram])(https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glLinkProgram.xhtml) and before any value of a uniform is specified (glUniform*):
glLinkProgram( ProgramObject );
GLint color_location = glGetUniformLocation(ProgramObject, "color");
....
glUseProgram( ProgramObject );
GLfloat black[4] = {1, 0, 0, 1};
glUniform4fv(color_location, 1, black);
the GLSL data type of the projection matrix has to be mat4. Note, a multiplication of 2 variables of type vec4, would lead to a component-wise multiplication to all components of the vector. See GLSL Programming/Vector and Matrix Operations.
But since you have initialized the vertex coordinates in normalized device space, you don't need any projection:
attribute vec4 vertex;
varying vec2 TexCoords;
void main()
{
gl_Position = vec4(vertex.xy, 0, 1);
TexCoords = vertex.zw;
}
Preview:
But I am using gl2 library and (gl2.h) does not have GL_RED defined.
Since GL_RED is not supported in OpenGL ES 2.0, you have to do a workaround. GL_ALPHA is supported in OpneGL ES 2.0, see glTexImage2D.
Use the GL_ALPHA format specifier:
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA,
face->glyph->bitmap.width, face->glyph->bitmap.rows, 0,
GL_ALPHA,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
And read the alpha channel form the a component, when looking up the texture in the fragment shader:
float alpha = texture2D(text, TexCoords).a;
gl_FragColor = vec4(1.0, 1.0, 1.0, alpha) * color;
So Im having a bit of trouble loading texture images. I am not exactly sure what I could be doing wrong. It seems to only be reading the last pixel and I am not sure why?
Any help is appreciated. Here is my code:
#include "Angel.h"
#include <glew.h>
#include <glut.h>
#include <typeinfo>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
using namespace std;
vec4 gridLines[] = {
vec4(-0.5, -0.5, 0.0, 1.0), //v1
vec4(0.5, -0.5, 0.0, 1.0), //v2
vec4(-0.5, 0.5, 0.0, 1.0), //v3
vec4(0.5, 0.5, 0.0, 1.0), //v4
};
GLuint vertexID[3];
GLuint bufferID2;
GLuint ProjectionLocation, ModelViewLocation;
mat4 instance;
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
float
roundTo(float value, int digits)//Ensures rounding is done correct
{
//check to see if the value is very close to zero
if ((int)(value*10.0) == 0){
return 0.0;
}
double factor = pow(10.0, digits - ceil(log10(fabs(value))));
return round(value * factor) / factor;
}
void
init(void) //Initialize Buffers for all objects
{
//1. Load shaders and use the resulting shader program
GLuint program = InitShader("vshader81.glsl", "fshader81.glsl");
glUseProgram(program);
GLuint vPosition = glGetAttribLocation(program, "vPosition");
GLuint vColor = glGetAttribLocation(program, "vColor");
glGenVertexArrays(1, &vertexID[1]);
vec4 colors2[] = {
vec4(1.0, 1.0, 1.0, 1.0), //1
vec4(1.0, 1.0, 1.0, 1.0), //2
vec4(1.0, 1.0, 1.0, 1.0), //3
vec4(1.0, 1.0, 1.0, 1.0), //4
};
// Create and initialize a buffer object
glBindVertexArray(vertexID[1]);
glGenBuffers(1, &bufferID2);
glBindBuffer(GL_ARRAY_BUFFER, bufferID2);
glBufferData(GL_ARRAY_BUFFER, sizeof(gridLines)+sizeof(colors2), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(gridLines), gridLines);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(gridLines), sizeof(colors2), colors2);
glEnableVertexAttribArray(vPosition);
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
GLuint textureid;
// Texture coordinates
vec4 tex_coords[] = {
vec4(-0.5, -0.5, 0.0, 1.0), //v1
vec4(0.5, -0.5, 0.0, 1.0), //v2
vec4(-0.5, 0.5, 0.0, 1.0), //v3
vec4(0.5, 0.5, 0.0, 1.0), //v4
};
glGenTextures(1, &textureid);
glBindTexture(GL_TEXTURE_2D, textureid);
/***********************************************/
const char * imagepath = "checkboard2.bmp";
// Data read from the header of the BMP file
unsigned char header[54]; // Each BMP file begins by a 54-bytes header
unsigned int dataPos; // Position in the file where the actual data begins
unsigned int width, height;
unsigned int imageSize; // = width*height*3
// Actual RGB data
unsigned char * data;
// Open the file
FILE * file = fopen(imagepath, "rb");
if (!file) { printf("Image could not be opened\n"); }
if (fread(header, 1, 54, file) != 54){ // If not 54 bytes read : problem
printf("Not a correct BMP file\n");
}
if (header[0] != 'B' || header[1] != 'M'){
printf("Not a correct BMP file\n");
}
cout << "great.." << endl;
// Read ints from the byte array
dataPos = *(int*)&(header[0x0A]);
imageSize = *(int*)&(header[0x22]);
cout << "Image Size: " << imageSize << endl;
width = *(int*)&(header[0x12]);
cout << "width: " << width << endl;
height = *(int*)&(header[0x16]);
// Some BMP files are misformatted, guess missing information
if (imageSize == 0) imageSize = width*height * 3; // 3 : one byte for each Red, Green and Blue component
if (dataPos == 0) dataPos = 54; // The BMP header is done that way
// Create a buffer
data = new unsigned char[imageSize];
// Read the actual data from the file into the buffer
fread(data, 1, imageSize, file);
//Everything is in memory now, the file can be closed
fclose(file);
//GLuint Texture = loadBMP_custom("brick_converted.bmp");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//glActiveTexture(GL_TEXTURE0);
GLuint vTexCoord = glGetAttribLocation(program, "vTexCoord");
glEnableVertexAttribArray(vTexCoord);
glVertexAttribPointer(vTexCoord, 2, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(sizeof(gridLines)));
glUniform1i(glGetUniformLocation(program, "texture"), 0);
//4. Bind all Buffers
glBindVertexArray(0);
//5.Set Background Color
glClearColor(0.5, 0.5, 0.5, 0.0); // background
}
//----------------------------------------------------------------------------
// Draw on Screen
//----------------------------------------------------------------------------
void
paintOnScreen()
{
glBindVertexArray(vertexID[1]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void
display(void) //Display to screen
{
//1.Clear the window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_CULL_FACE);
//2.Translations/Rotations/Projections etc..
glViewport(0, 0, 300, 300);
//3.Draw Objects
paintOnScreen();
glBindVertexArray(0);
//3.Force OpenGL to render
glFlush();
}
void
reshape(int width, int height)
{
glViewport(0, 0, width, height);
//aspect = GLfloat(width)/height;
}
void
keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 033: //ESC
exit(EXIT_SUCCESS);
break;
}
}
//----------------------------------------------------------------------------
int
main(int argc, char **argv)
{
// Initialize glut library
glutInit(&argc, argv);
// Create the window
glutInitDisplayMode(GLUT_RGBA);
glutInitWindowSize(300, 300);
glutInitWindowPosition(300, 200);
glutCreateWindow("Test");
// Initialize glew library
glewInit();
// Your own initialization
init();
// Set callback functions
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
// Start the main loop
glutMainLoop();
return 0;
}
and my fragment shader:
#version 150
in vec2 texCoord;
out vec4 fColor;
uniform sampler2D texture;
void main()
{
fColor = texture2D( texture, texCoord );
}
and my vertex shader:
#version 150
in vec4 vPosition;
in vec2 vTexCoord;
out vec2 texCoord;
void main()
{
texCoord = vTexCoord;
gl_Position = vPosition;
}
You do not specify the texture coords correctly. You define an tex_coords array, but this data is never copied to a VBO (or referenced in the code at all). You set up the vTexCoord attribute pointer to point to the place in the VBO where you copied your colors2 data array, which is all 1.0, so you are constantly accessing the very last texel.
I have the sample code of drawing a cube. How can I draw other cubes at different positions (using translation matrices) to be able to draw a tetris of which's engine I have.
#include <glf.hpp>
#include <GL/freeglut.h>
#include <camera.h>
#define kEpsilon 1.0e-6f
#define kPI 3.1415926535897932384626433832795f
#define kHalfPI 1.5707963267948966192313216916398f
#define kTwoPI 2.0f*kPI
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
namespace
{
//path to shader files
std::string const SHADER_VERT_SOURCE("../GL3_init/Basic.vsh"); //// relative to
std::string const SHADER_FRAG_SOURCE("../GL3_init/Basic.fsh"); //// the executable path
//cube data
namespace cube {
// (6 faces)(2 triangles/face)(3 vertices/triangle)
const int NumVertices = 36;
glm::vec4 points[NumVertices];
glm::vec4 colors[NumVertices];
// Vertices positions of a unit cube centered at origin
glm::vec4 vertex_positions[8] = {
glm::vec4( -0.5, -0.5, 0.5, 1.0),
glm::vec4( -0.5, 0.5, 0.5, 1.0),
glm::vec4( 0.5, 0.5, 0.5, 1.0),
glm::vec4( 0.5, -0.5, 0.5, 1.0),
glm::vec4( -0.5, -0.5, -0.5, 1.0),
glm::vec4( -0.5, 0.5, -0.5, 1.0),
glm::vec4( 0.5, 0.5, -0.5, 1.0),
glm::vec4( 0.5, -0.5, -0.5, 1.0)
};
// RGBA colors
glm::vec4 vertex_colors[8] = {
glm::vec4( 0.0, 0.0, 0.0, 1.0),//black
glm::vec4( 1.0, 0.0, 0.0, 1.0),//red
glm::vec4( 1.0, 1.0, 0.0, 1.0),//yellow
glm::vec4( 0.0, 1.0, 0.0, 1.0),//green
glm::vec4( 0.0, 0.0, 1.0, 1.0),//blue
glm::vec4( 1.0, 0.0, 1.0, 1.0),//magenta
glm::vec4( 1.0, 1.0, 1.0, 1.0),//white
glm::vec4( 0.0, 1.0, 1.0, 1.0) //cyan
};
// quad() generates two triangles for each face and assigns colors to the vertices
int Index = 0;
void quad( int a, int b, int c, int d )
{
colors[Index] = vertex_colors[a]; points[Index] = vertex_positions[a]; Index++;
colors[Index] = vertex_colors[b]; points[Index] = vertex_positions[b]; Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertex_positions[c]; Index++;
colors[Index] = vertex_colors[a]; points[Index] = vertex_positions[a]; Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertex_positions[c]; Index++;
colors[Index] = vertex_colors[d]; points[Index] = vertex_positions[d]; Index++;
}
// generate 12 triangles: 36 vertices and 36 colors
void colorcube()
{
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
}
//OpenGL Objects
GLuint vao;
GLuint buffer;
GLuint program;
//window
int width = 800;
int height = 600;
//MVP
glm::mat4 mProjection = glm::perspective(60.0f, float(width) / height, 0.1f, 1000.0f);
Camera camera(glm::vec3(0.0f, 1.0f,-3.0f), glm::vec3(0.0f,0.0f,0.0f), glm::vec3(0.0f,1.0f,0.0f), mProjection);
//uniform variables
GLuint uMVP;
}
//create, compile and link shaders
bool initProgram()
{
bool Validated(true);
GLuint VertShaderName = glf::createShader(GL_VERTEX_SHADER, SHADER_VERT_SOURCE);
GLuint FragShaderName = glf::createShader(GL_FRAGMENT_SHADER, SHADER_FRAG_SOURCE);
program = glCreateProgram();
glAttachShader(program, VertShaderName);
glAttachShader(program, FragShaderName);
glLinkProgram(program);
glDeleteShader(VertShaderName);
glDeleteShader(FragShaderName);
Validated = Validated && glf::checkProgram(program);
if(Validated)
{
glUseProgram(program);
//Associate uniforms
uMVP = glGetUniformLocation( program, "MVP");
Validated = Validated && glf::checkError("initProgram - stage");
}
}
// Create and initialize buffer object
bool initBuffer()
{
bool Validated(true);
glGenBuffers(1,&buffer);
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(cube::points) + sizeof(cube::colors),NULL,GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(cube::points), cube::points);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(cube::points), sizeof(cube::colors), cube::colors);
return Validated;
}
// Create and bind vertex array object. Set pointer to vertex attributes
bool initVertexArray()
{
bool Validated(true);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
GLuint vPosition = glGetAttribLocation( program,"vPosition" );
glEnableVertexAttribArray(vPosition);
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
GLuint vColor = glGetAttribLocation( program, "vColor" );
glEnableVertexAttribArray( vColor );
glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(cube::points)) );
return Validated;
}
// Setup GL state
bool beginGL()
{
bool Validated(true);
//setup the cube
cube::colorcube();
Validated = initProgram();
if(Validated)
Validated = initBuffer();
if(Validated)
Validated = initVertexArray();
return Validated;
}
// Free memory
void endGL()
{
glDeleteBuffers(1,&buffer);
glDeleteProgram(program);
glDeleteVertexArrays(1, &vao);
}
///////////////////////////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClearBufferfv(GL_COLOR, 0, &glm::vec4(0.4f, 0.4f, 0.4f, 1.0f)[0]);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glEnable(GL_MULTISAMPLE);
//glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
//setup Model View Projection
glm::mat4 mMVP = camera.getprojection()*camera.ViewMatrix();
glProgramUniformMatrix4fv(program, uMVP, 1, GL_FALSE, glm::value_ptr(mMVP));
//Draw Cube
glDrawArrays(GL_TRIANGLES,0,cube::NumVertices);
// Flush drawing commands
glutSwapBuffers();
// Refresh the Window
glutPostRedisplay();
glDisable(GL_MULTISAMPLE);
}
///////////////////////////////////////////////////////////////////////////////
// A normal ASCII key has been pressed.
// In this case, space bar is pressed
void KeyPressFunc(unsigned char key, int x, int y)
{
switch(key)
{
// controls for camera
// add up/down and left/right controls
case 'L': camera.slide(.2, 0, 0); break;// slide camera right
case 'L' + 32: camera.slide(-0.2, 0, 0); break; // slide camera left
case 'U': camera.slide(0, -0.2, 0); break;// slide camera down
case 'U' + 32: camera.slide(0, 0.2, 0); break; // slide camera up
case 'F': camera.slide(0,0, 0.2); break; // slide camera forward
case 'F' + 32: camera.slide(0,0,-0.2); break; //slide camera back
// add pitch controls
case 'P': camera.pitch(-1.0); break;
case 'P' + 32: camera.pitch( 1.0); break;
// add yaw controls
case 'Y': camera.yaw(-1.0); break;
case 'Y' + 32: camera.yaw( 1.0); break;
// add roll controls
case 'R': camera.roll(1.0); break;
case 'R' + 32: camera.roll(-1.0); break;
// close with Esc
case 27: exit(0); break;
}
}
//////////////////////////////////////////////////////////////////////////////
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
width = w; height = h;
glViewport(0, 0, w, h);
mProjection = glm::perspective(60.0f, float(width) / height, 0.1f, 1000.0f);
camera.setprojection(mProjection);
}
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(width, height);
glutCreateWindow("3D Demo");
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
return 1;
}
if(beginGL()){
glutReshapeFunc(ChangeSize);
glutKeyboardFunc(KeyPressFunc);
glutDisplayFunc(RenderScene);
glutCloseFunc(endGL);
glutMainLoop();
return 0;
}
return 1;
}
In RenderScene you can play with the following lines in order to draw multiple cubes :
glm::mat4 mMVP = camera.getprojection()*camera.ViewMatrix();
for(int i = 0; i < 10; i++)
{
glm::mat4 finalM = mMVP * a_function_that_creates_a_translation_matrix(x, y, z);
glProgramUniformMatrix4fv(program, uMVP, 1, GL_FALSE, glm::value_ptr(finalM));
glDrawArrays(GL_TRIANGLES,0,cube::NumVertices);
}