opengl+glfw+glew+freetype书写汉字
FreeType
http://gnuwin32.sourceforge.net/packages/freetype.htm
下载Developer files(下载后是freetype-2.3.5-1-lib.zip)
Dependencies(下载后是freetype-2.3.5-1-dep.zip)
Binaries(下载的是freetype-2.3.5-1-bin.zip)
头文件:(freetype-2.3.5-1-lib.zip或freetype-2.3.5-1-bin.zip)解压后freetype在inlude/freetype2目录里面,应该把freetype目录拖动freetype2同级目录,然后把freetype2目录删掉
lib库:(freetype-2.3.5-1-lib.zip或freetype-2.3.5-1-bin.zip)解压后的lib目录,freetype.lib,不用freetype-bcc.lib
dll库:Dependencies(下载后是freetype-2.3.5-1-dep.zip)中的zlib1.dll、Binaries(下载的是freetype-2.3.5-1-bin.zip)中的freeType6.dll
沿用了上篇博文的绘制立方体代码
#include <iostream>
#include <Windows.h>
#define _USE_MATH_DEFINES
#include <math.h>
// GLEW
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// SOIL
#include <SOIL.h>
// FreeType
// 把FreeType2中的freetype目录拖到FreeType2同级目录
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
GLuint WIDTH = 400, HEIGHT = 400;
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
#define TEXTURE_CNT 2
GLuint textures[TEXTURE_CNT];//存储2个纹理
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void windowResize_callback(GLFWwindow* window, int width, int height);
FT_Face g_ftFace;
int fontWidth = 14, fontHeight = 16;
#define MAX_NO_TEXTURES 1
#define CUBE_TEXTURE 0
GLuint texture_id[MAX_NO_TEXTURES];
struct xCharTexture
{
GLuint m_texID;
wchar_t m_chaID;
int m_Width;
int m_Height;
int m_adv_x;
int m_adv_y;
int m_delta_x;
int m_delta_y;
public:
xCharTexture()
{
m_texID = 0;
m_chaID = 0;
m_Width = 0;
m_Height = 0;
}
}g_TexID[65536];
//unicode,"C:\Windows\Fonts\"
void initFreeType(const char* fontFilePath)
{
FT_Library library;
FT_Error error;
error = FT_Init_FreeType(&library);
if (FT_Err_Ok != error)
{
OutputDebugString(L"FT_Init_FreeType failed");
return;
}
error = FT_New_Face(library, fontFilePath, 0, &g_ftFace);
if (FT_Err_Ok != error)
{
OutputDebugString(L"FT_New_Face failed");
return;
}
FT_Select_Charmap(g_ftFace, FT_ENCODING_UNICODE);
FT_Set_Char_Size(g_ftFace, fontWidth, fontHeight, 0, 0);
FT_Set_Pixel_Sizes(g_ftFace, fontWidth, fontHeight);
}
GLuint loadChar(wchar_t ch)
{
if (g_TexID[ch].m_texID)
{
return g_TexID[ch].m_texID;
}
/* 装载字形图像到字形槽(将会抹掉先前的字形图像) */
if (FT_Load_Char(g_ftFace, ch, /*FT_LOAD_RENDER|*/FT_LOAD_FORCE_AUTOHINT |
(TRUE ? FT_LOAD_TARGET_NORMAL : FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO)))
{
return 0;
}
/*if(FT_Load_Glyph( m_FT_Face, FT_Get_Char_Index( m_FT_Face, ch ), FT_LOAD_FORCE_AUTOHINT ))
throw std::runtime_error("FT_Load_Glyph failed");*/
xCharTexture& charTex = g_TexID[ch];
//得到字模
FT_Glyph glyph;
//把字形图像从字形槽复制到新的FT_Glyph对象glyph中。这个函数返回一个错误码并且设置glyph。
if (FT_Get_Glyph(g_ftFace->glyph, &glyph))
{
return 0;
}
//转化成位图
FT_Render_Glyph(g_ftFace->glyph, FT_RENDER_MODE_LCD);//FT_RENDER_MODE_NORMAL );
FT_Glyph_To_Bitmap(&glyph, ft_render_mode_normal, 0, 1);
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;//首部是FT_GlyphRec,也就是FT_Glyph
//取道位图数据
FT_Bitmap& bitmap = bitmap_glyph->bitmap;
//把位图数据拷贝自己定义的数据区里.这样旧可以画到需要的东西上面了。
int width = bitmap.width;
int height = bitmap.rows;
g_ftFace->size->metrics.y_ppem; //伸缩距离到设备空间
g_ftFace->glyph->metrics.horiAdvance; //水平文本排列
charTex.m_Width = width;
charTex.m_Height = height;
charTex.m_adv_x = g_ftFace->glyph->advance.x / 64.0f; //步进宽度
charTex.m_adv_y = g_ftFace->size->metrics.y_ppem; //m_FT_Face->glyph->metrics.horiBearingY / 64.0f;
charTex.m_delta_x = (float)bitmap_glyph->left; //left:字形原点(0,0)到字形位图最左边象素的水平距离.它以整数象素的形式表示。
charTex.m_delta_y = (float)bitmap_glyph->top - height; //Top: 类似于字形槽的bitmap_top字段。
glGenTextures(1, &charTex.m_texID);
glBindTexture(GL_TEXTURE_2D, charTex.m_texID);
char* pBuf = new char[width * height * 4];
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
unsigned char _vl = (i >= bitmap.width || j >= bitmap.rows) ? 0 : bitmap.buffer[i + bitmap.width*j];
pBuf[(4 * i + (height - j - 1) * width * 4)] = 0xFF;
pBuf[(4 * i + (height - j - 1) * width * 4) + 1] = 0xFF;
pBuf[(4 * i + (height - j - 1) * width * 4) + 2] = 0xFF;
pBuf[(4 * i + (height - j - 1) * width * 4) + 3] = _vl;
}
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBuf); //指定一个二维的纹理图片
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); //glTexParameteri():纹理过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE); //纹理进行混合
/*gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pBuf);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexEnvi(GL_TEXTURE_2D,GL_TEXTURE_ENV_MODE,GL_REPLACE);*/
delete[] pBuf;
return charTex.m_chaID;
}
xCharTexture* getTextChar(wchar_t ch)
{
loadChar(ch);
return &g_TexID[ch];
}
void drawText(const wchar_t* _strText, GLfloat x, GLfloat y, GLfloat maxW, GLfloat h)
{
GLfloat sx = x;
GLfloat sy = y;
GLfloat maxH = h;
size_t nLen = wcslen(_strText);
glEnable(GL_TEXTURE_2D);
for (int i = 0; i < nLen; i++)
{
if (_strText[i] == '\n')
{
sx = x; sy -= maxH + 0.02f;
continue;
}
xCharTexture* pCharTex = getTextChar(_strText[i]);
glBindTexture(GL_TEXTURE_2D, pCharTex->m_texID); //绑定到目标纹理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //特殊的像素算法
GLfloat w = (GLfloat)pCharTex->m_Width / WIDTH * 2;
GLfloat h = (GLfloat)pCharTex->m_Height / HEIGHT * 2;
GLfloat ch_x = sx + (GLfloat)pCharTex->m_delta_x / WIDTH * 2;
GLfloat ch_y = sy - h - (GLfloat)pCharTex->m_delta_y / HEIGHT * 2;
WCHAR str[100];
swprintf(str, 200, L"%c w %lf h %lf ch_x %lf ch_y %lf textureid %d advance %d\r\n",
_strText[i], w, h, ch_x, ch_y, pCharTex->m_texID, pCharTex->m_adv_x);
OutputDebugString(str);
if (maxH < h) maxH = h;
glBegin(GL_QUADS); // 定义一个或一组原始的顶点
{
glTexCoord2f(0.0f, 0.0f); glVertex3f(ch_x, ch_y, 0.2f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(ch_x + w, ch_y, 0.2f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(ch_x + w, ch_y + h, 0.2f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(ch_x, ch_y + h, 0.2f);
}
glEnd();
sx += (GLfloat)pCharTex->m_adv_x / WIDTH * 2 + 0.02;
if (sx > x + maxW)
{
sx = x; sy += maxH + 0.02f;
}
}
glDisable(GL_TEXTURE_2D);
}
void loadTexture(const char* filePath, int index)
{
int picWidth, picHeight;
int channel = 0;
unsigned char* imgData = SOIL_load_image(filePath,
&picWidth, &picHeight, &channel, SOIL_LOAD_RGB);
if (NULL == imgData)
{
OutputDebugString(L"SOIL_load_image failed \r\n");
return;
}
glGenTextures(1, &textures[index]);
glBindTexture(GL_TEXTURE_2D, textures[index]);
glTexImage2D(GL_TEXTURE_2D, 0, 3, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, imgData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 线形滤波
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 线形滤波
SOIL_free_image_data(imgData);
}
void DrawCube(void) // 从这里开始进行所有的绘制
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
glEnable(GL_TEXTURE_2D);//启用纹理贴图
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);//指定纹理贴图与材质的混合模式
glColor4f(1.0, 1.0, 1.0, 1.0);
glLoadIdentity(); // 重置当前的模型观察矩阵
//glTranslatef(0.0f, 0.0f, -5.0f); // 移入屏幕 5 个单位
glRotatef(xRot, 1.0f, 0.0f, 0.0f); // 绕X轴旋转
glRotatef(yRot, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转
glBindTexture(GL_TEXTURE_2D, textures[1]); // 选择纹理
glBegin(GL_QUADS);
// 前面
glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); // 纹理和四边形的左下
glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.5f, 0.5f); // 纹理和四边形的右下
glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.5f, 0.5f); // 纹理和四边形的右上
glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.5f, 0.5f); // 纹理和四边形的左上
// 后面
glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); // 纹理和四边形的右下
glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); // 纹理和四边形的右上
glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f, 0.5f, -0.5f); // 纹理和四边形的左上
glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f, -0.5f, -0.5f); // 纹理和四边形的左下
// 顶面
glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); // 纹理和四边形的左上
glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, 0.5f, 0.5f); // 纹理和四边形的左下
glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, 0.5f, 0.5f); // 纹理和四边形的右下
glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.5f, -0.5f); // 纹理和四边形的右上
glEnd();
glFlush();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);//指定纹理贴图与材质的混合模式
glBindTexture(GL_TEXTURE_2D, textures[0]); // 选择纹理
glBegin(GL_QUADS);
// 底面
glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5f, -0.5f, -0.5f); // 纹理和四边形的右上
glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f, -0.5f, -0.5f); // 纹理和四边形的左上
glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f, -0.5f, 0.5f); // 纹理和四边形的左下
glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); // 纹理和四边形的右下
// 右面
glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.5f, -0.5f); // 纹理和四边形的右下
glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.5f, -0.5f); // 纹理和四边形的右上
glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f, 0.5f, 0.5f); // 纹理和四边形的左上
glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f, -0.5f, 0.5f); // 纹理和四边形的左下
// 左面
glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); // 纹理和四边形的左下
glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); // 纹理和四边形的右下
glTexCoord2f(1.0f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); // 纹理和四边形的右上
glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); // 纹理和四边形的左上
glEnd();
glFlush();
glColor4f(1.0, 0.0, 0.0, 1.0);
drawText(L"大家好呀\n我很好", 0, 0.8, 1, 0.02);
}
void render(GLFWwindow* window)
{
glClearColor(1.0f, 0.8f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
//DrawLine();
//DrawBezier();
//Draw3d();
DrawCube();
}
void initWindow(GLFWwindow* window)
{
glfwMakeContextCurrent(window);
//设置按键回调
glfwSetKeyCallback(window, key_callback);
//设置窗口大小改变的回调,让绘画区域在窗口中间
glfwSetWindowSizeCallback(window, windowResize_callback);
}
void initParam()
{
//显示规则:窗口左下角坐标为0,0;所以下行代码表示在窗口左下角向右向上的400个像素单位作为画布
glViewport(0, 0, WIDTH, HEIGHT);//设置显示区域400*400,但是可以拖动改变窗口大小
glLineWidth(3.0);//设置线条宽度,3个像素
glEnable(GL_BLEND);
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);//设置点圆滑
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);//设置线光滑
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
//启用文字,不支持汉字、unicode
void initData()
{
loadTexture("D:\\2.jpg", 0);
loadTexture("D:\\222huojian.jpg", 1);
initFreeType("C:\\Windows\\Fonts\\msyh.ttc");
}
int main()
{
glfwInit();
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL", nullptr, nullptr);
if (window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
initWindow(window);
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
initParam();
initData();
// Game loop
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
render(window);
glfwSwapBuffers(window);
}
// Terminate GLFW, clearing any resources allocated by GLFW.
glfwTerminate();
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_UP)
xRot -= 5.0f;
else if (key == GLFW_KEY_DOWN)
xRot += 5.0f;
else if (key == GLFW_KEY_LEFT)
yRot -= 5.0f;
else if (key == GLFW_KEY_RIGHT)
yRot += 5.0f;
}
void windowResize_callback(GLFWwindow* window, int width, int height)
{
WIDTH = HEIGHT = width > height ? height : width;
//左下角是0, 0坐标, x,y说就是相对于左下角的像素距离
glViewport((width - WIDTH) / 2, (height - HEIGHT) / 2, WIDTH, HEIGHT);
}
原理:
将图片读取为纹理,将纹理绘制到opengl坐标中
文字设置了颜色,可能会导致立方体的纹理也变色,所以DrawCube中调用了两次glColor4f
效果: