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

opengl+glfw+glew+freetype书写汉字

沿用了上篇博文的绘制立方体代码

#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

效果:

opengl+glfw+glew+freetype书写汉字