一、文本渲染原理

1.1经典文本渲染:位图字体

早期的文本渲染,是将需要的字符集放到一个大纹理中,这个纹理称为“位图字体”,渲染某个字符时,通过查找坐标,找到该字符对应的区域并渲染出来,再启动混合,让字符纹理的背景保持透明,非常容易理解。

这种方式的缺点也比较明显:

1)字符集非常小,不支持拓展,因为已经生成了字符表);

2)已经预光栅化了,所以分辨率已经固化了,修改分辨率需要重新编译一张字符纹理表(“纹理表”是笔者自创的称呼)

1.2 现代文本渲染

上面的图很清楚的说明了一个字符的定义规则,注意,有些字符在基准线之上,有少数字符在基准线之下,比如g p j等。

关于字符更详细的定义,参考:https://www.supremo.co.uk/typeterms/

基于经典文本渲染的瓶颈,现在有更好的文本渲染方式,基于FreeType处理。

FreeType有以下优点:

1)跨平台

2)能加载TrueType字体,TrueType不是基于像素定义的,而是通过数学公式(曲线)来定义,类似矢量图像,所以方便渲染不同大小的字形,有更好的适配能力

二、基于freetype渲染文本

文本渲染

文本轮廓

关闭混合,可以看到每个字符占的区域大小是不一样的

2.1 先看看着shader的实现

顶点着色器:很简单,有个小技巧,一个vec4 xy存字符position,zw存纹理,节省内存

#version 330 core

layout (location = 0) in vec4 vertex;

out vec2 TexCoords;

uniform mat4 projection;

void main ()

{

gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); // z坐标=0,在原点,w坐标为1,无缩放

TexCoords = vertex.zw;

}

片段着色器: sampled取出r值,字形原始数据没有颜色,r通道存放的是alpha值,r值与设置的color做混合,即可得到带颜色的文字。

注意下面代码中注释的部分,如果不开启混合,需要判断alpha通道的值为0时,进行discard操作,否则按照当前的逻辑字符会渲染成一个矩形图案。开启混合模式,当前窗口的颜色缓冲会把字符的背景中镂空的部分覆盖掉

#version 330 core

in vec2 TexCoords;

out vec4 color;

uniform sampler2D text;

uniform vec3 textColor;

void main()

{

vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);

// if (sampled.w == 0) {

// discard;

// }

color = vec4(textColor, 1.0) * sampled;

}

2.1 工程中引入需要的头文件

#include

#include FT_FREETYPE_H

2.2 启用混合模式,某则渲染出来是方形图案(不启用混合,通过其他方式也可以实现)

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

2.3 freetype库初始化

// FreeType

FT_Library ft;

// All functions return a value different than 0 whenever an error occurred

if (FT_Init_FreeType(&ft)) {

std::cout << "ERROR::freetype: Could not init FreeType Library" << std::endl;

}

// Load font as face

FT_Face face;

if (FT_New_Face(ft, "fonts/Antonio-Bold.ttf", 0, &face)) {

std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;

}

FT_Set_Pixel_Sizes(face, 0, 48);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

2.4 加载128个常用字符(降低使用难度),存放到map中,使用完记得释放freetype

for (GLubyte c = 0; c < 128; c++) {

// Load character glyph

if (FT_Load_Char(face, c, FT_LOAD_RENDER))

{

std::cout << "ERROR::FREETYPE: Failed to load Glyph" << std::endl;

continue;

}

// Generate texture

GLuint texture;

glGenTextures(1, &texture);

glBindTexture(GL_TEXTURE_2D, texture);

glTexImage2D(

GL_TEXTURE_2D,

0,

GL_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(face->glyph->advance.x)

};

Characters.insert(std::pair(c, character));

}

// Destroy FreeType once we're finished

FT_Done_Face(face);

FT_Done_FreeType(ft);

2.5 render loop中渲染两端测试文字

注意,这里渲染文字使用正交矩阵,没有像之前使用投影矩阵,并且投影矩阵的左下角是(0, 0)坐标

glm::mat4 projection = glm::ortho(0.0f, static_cast(SCR_WIDTH), 0.0f, static_cast(SCR_HEIGHT));

RenderText(shader, "This is sample text", 25.0f, 25.0f, 1.0f, glm::vec3(0.5f, 0.8f, 0.2f));

RenderText(shader, "(C) LearnOpenGL.com", 540.0f, 570.0f, 0.5f, glm::vec3(0.3f, 0.7f, 0.9f));

RenderText的逻辑不复杂,直接看代码理解即可

void RenderText(Shader &shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color)

{

shader.use();

shader.setVec3("textColor", color.x, color.y, color.z);

glActiveTexture(GL_TEXTURE0);

glBindVertexArray(VAO);

std::string::const_iterator c;

for (c = text.begin(); c != text.end(); c++) {

Character ch = Characters[*c];

GLfloat xpos = x + ch.Bearing.x * scale;

GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;

GLfloat w = ch.Size.x * scale;

GLfloat h = ch.Size.y * scale;

// Update VBO for each character

GLfloat vertices[6][4] = {

{xpos, ypos + h, 0.0, 0.0},

{xpos, ypos, 0.0, 1.0},

{xpos + w, ypos, 1.0, 1.0},

{xpos, ypos + h, 0.0, 0.0},

{xpos + w, ypos, 1.0, 1.0},

{xpos + w, ypos + h, 1.0, 0.0},

};

// Render glyph texture over quad

glBindTexture(GL_TEXTURE_2D, ch.TextureID);

// Update content of VBO memory

glBindBuffer(GL_ARRAY_BUFFER, VBO);

glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);

glBindBuffer(GL_ARRAY_BUFFER, 0);

//Render quad

glDrawArrays(GL_TRIANGLES, 0, 6);

// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)

x += (ch.Advance >> 6) * scale;

}

glBindVertexArray(0);

glBindTexture(GL_TEXTURE_2D, 0);

}

三、补充:mac freetype安装配置,不复杂,简单说明

如果是Android/iOS,需通过freetype源码编译对应的平台库,前端直接引用freetype.js更方便

1. brew install freetype

安装完成会在local/include/freetype2/下生成头文件

因为freetype的代码依赖结构,需要修改header文件,讲freetype2目录下的头文件copy到include下,否则会编译报错

2. Xcode环境变量配置

2.1 xcode->preference->locations->custom paths

2.2 Search Paths 增加Header和 Library

2.3 Build Phases 增加库依赖

四、完整代码

#include

#include

#define STB_IMAGE_IMPLEMENTATION

#include "stb_image.h"

#include

#include

#include

#include "Shader.h"

#include "camera.h"

#include "model.h"

#include

#include

#include

#include FT_FREETYPE_H

// Holds all state information relevant to a character as loaded using FreeType

struct Character {

GLuint TextureID; // ID handle of the glyph texture

glm::ivec2 Size;

glm::ivec2 Bearing; // bearing 这里翻译成方位/方向

GLuint Advance;

};

std::map Characters;

GLuint VAO, VBO;

void RenderText(Shader &shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color);

void framebuffer_size_callback(GLFWwindow* window, int width, int height);

void mouse_callback(GLFWwindow* window, double xpos, double ypos);

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

void processInput(GLFWwindow *window);

unsigned int loadTexture(const char *path);

unsigned int loadCubemap(vector<:string> faces);

void renderScene (const Shader &shader);

void renderCube();

void RenderQuad();

void renderSphere();

// settings

const unsigned int SCR_WIDTH = 800;

const unsigned int SCR_HEIGHT = 600;

bool blinn = false;

bool blinnKeyPressed = false;

bool gammaEnabled = true;

bool gammaKeyPressed = false;

bool bloom = true;

bool hdr = true; //change with 'space'

float exposure = 1.0f; // change with Q and E

// camera

Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

float lastX = (float)SCR_WIDTH / 2.0;

float lastY = (float)SCR_HEIGHT / 2.0;

bool firstMouse = true;

// timing

float deltaTime = 0.0f;

float lastFrame = 0.0f;

unsigned int draw_mode = 1;

float lerp(float a, float b, float f)

{

return a + f * (b - a);

}

int main()

{

// glfw: initialize and configure

// ------------------------------

glfwInit();

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);

glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

// glfwWindowHint(GLFW_SAMPLES, 4);

glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__

glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

#endif

// glfw window creation

// --------------------

GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "天哥学opengl", NULL, NULL);

if (window == NULL)

{

std::cout << "Failed to create GLFW window" << std::endl;

glfwTerminate();

return -1;

}

glfwMakeContextCurrent(window);

glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

glfwSetCursorPosCallback(window, mouse_callback);

glfwSetScrollCallback(window, scroll_callback);

// tell GLFW to capture our mouse

// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

// glad: load all OpenGL function pointers

// ---------------------------------------

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))

{

std::cout << "Failed to initialize GLAD" << std::endl;

return -1;

}

glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);

// glPolygonMode(GL_FRONT_AND_BACK ,GL_LINE );

glEnable(GL_CULL_FACE);

// glEnable(GL_BLEND);

// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// build and compile shaders

Shader shader("text.vs", "text.fs");

glm::mat4 projection = glm::ortho(0.0f, static_cast(SCR_WIDTH), 0.0f, static_cast(SCR_HEIGHT));

shader.use();

shader.setMat4("projection", projection);

// FreeType

FT_Library ft;

// All functions return a value different than 0 whenever an error occurred

if (FT_Init_FreeType(&ft)) {

std::cout << "ERROR::freetype: Could not init FreeType Library" << std::endl;

}

// Load font as face

FT_Face face;

if (FT_New_Face(ft, "fonts/Antonio-Bold.ttf", 0, &face)) {

std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;

}

FT_Set_Pixel_Sizes(face, 0, 48);

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

for (GLubyte c = 0; c < 128; c++) {

// Load character glyph

if (FT_Load_Char(face, c, FT_LOAD_RENDER))

{

std::cout << "ERROR::FREETYPE: Failed to load Glyph" << std::endl;

continue;

}

// Generate texture

GLuint texture;

glGenTextures(1, &texture);

glBindTexture(GL_TEXTURE_2D, texture);

glTexImage2D(

GL_TEXTURE_2D,

0,

GL_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(face->glyph->advance.x)

};

Characters.insert(std::pair(c, character));

}

glBindTexture(GL_TEXTURE_2D, 0);

// Destroy FreeType once we're finished

FT_Done_Face(face);

FT_Done_FreeType(ft);

// Confiture VAO/VBO for texture quads

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), 0);

glBindVertexArray(0);

glBindVertexArray(0);

// render loop

// -----------

while (!glfwWindowShouldClose(window))

{

// per-frame time logic

float currentFrame = glfwGetTime();

deltaTime = currentFrame - lastFrame;

lastFrame = currentFrame;

processInput(window);

//render

glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT);

RenderText(shader, "This is sample text", 25.0f, 25.0f, 1.0f, glm::vec3(0.5f, 0.8f, 0.2f));

RenderText(shader, "(C) LearnOpenGL.com", 540.0f, 570.0f, 0.5f, glm::vec3(0.3f, 0.7f, 0.9f));

// -------------------------------------------------------------------------------

glfwSwapBuffers(window);

glfwPollEvents();

}

// glfw: terminate, clearing all previously allocated GLFW resources.

// ------------------------------------------------------------------

glfwTerminate();

return 0;

}

void RenderText(Shader &shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color)

{

shader.use();

shader.setVec3("textColor", color.x, color.y, color.z);

glActiveTexture(GL_TEXTURE0);

glBindVertexArray(VAO);

std::string::const_iterator c;

for (c = text.begin(); c != text.end(); c++) {

Character ch = Characters[*c];

GLfloat xpos = x + ch.Bearing.x * scale;

GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;

GLfloat w = ch.Size.x * scale;

GLfloat h = ch.Size.y * scale;

// Update VBO for each character

GLfloat vertices[6][4] = {

{xpos, ypos + h, 0.0, 0.0},

{xpos, ypos, 0.0, 1.0},

{xpos + w, ypos, 1.0, 1.0},

{xpos, ypos + h, 0.0, 0.0},

{xpos + w, ypos, 1.0, 1.0},

{xpos + w, ypos + h, 1.0, 0.0},

};

// Render glyph texture over quad

glBindTexture(GL_TEXTURE_2D, ch.TextureID);

// Update content of VBO memory

glBindBuffer(GL_ARRAY_BUFFER, VBO);

glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);

glBindBuffer(GL_ARRAY_BUFFER, 0);

//Render quad

glDrawArrays(GL_TRIANGLES, 0, 6);

// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)

x += (ch.Advance >> 6) * scale;

}

glBindVertexArray(0);

glBindTexture(GL_TEXTURE_2D, 0);

}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly

// ---------------------------------------------------------------------------------------------------------

bool startRecord = false;

void processInput(GLFWwindow *window)

{

if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) {

draw_mode = 1;

}

if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS) {

draw_mode = 2;

}

if (glfwGetKey(window, GLFW_KEY_3) == GLFW_PRESS) {

draw_mode = 3;

}

if (glfwGetKey(window, GLFW_KEY_4) == GLFW_PRESS) {

draw_mode = 4;

}

if (glfwGetKey(window, GLFW_KEY_B) == GLFW_PRESS && !gammaKeyPressed)

{

gammaEnabled = !gammaEnabled;

gammaKeyPressed = true;

}

if (glfwGetKey(window, GLFW_KEY_B) == GLFW_RELEASE)

{

gammaKeyPressed = false;

}

if (glfwGetKey(window, GLFW_KEY_Y))

{

std::cout << "Y" << std::endl;

startRecord = true;

firstMouse = true;

}

if (glfwGetKey(window, GLFW_KEY_N))

{

std::cout << "N" << std::endl;

startRecord = false;

}

if (startRecord) {

return;

}

if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)

glfwSetWindowShouldClose(window, true);

if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)

camera.ProcessKeyboard(FORWARD, deltaTime);

if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)

camera.ProcessKeyboard(BACKWARD, deltaTime);

if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)

camera.ProcessKeyboard(LEFT, deltaTime);

if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)

camera.ProcessKeyboard(RIGHT, deltaTime);

if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)

exposure -= 0.5 * deltaTime;

if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)

exposure += 0.5 * deltaTime;

if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS && !gammaKeyPressed)

{

hdr = !hdr;

gammaKeyPressed = true;

}

if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_RELEASE)

{

gammaKeyPressed = false;

}

}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes

// ---------------------------------------------------------------------------------------------

void framebuffer_size_callback(GLFWwindow* window, int width, int height)

{

// make sure the viewport matches the new window dimensions; note that width and

// height will be significantly larger than specified on retina displays.

glViewport(0, 0, width, height);

}

// glfw: whenever the mouse moves, this callback is called

// -------------------------------------------------------

void mouse_callback(GLFWwindow* window, double xpos, double ypos)

{

// std::cout << "xpos : " << xpos << std::endl;

// std::cout << "ypos : " << ypos << std::endl;

if (startRecord) {

return;

}

if (firstMouse)

{

lastX = xpos;

lastY = ypos;

firstMouse = false;

}

float xoffset = xpos - lastX;

float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

lastX = xpos;

lastY = ypos;

// std::cout << "xoffset : " << xoffset << std::endl;

// std::cout << "yoffset : " << yoffset << std::endl;

camera.ProcessMouseMovement(xoffset, yoffset);

}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called

// ----------------------------------------------------------------------

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)

{

camera.ProcessMouseScroll(yoffset);

}

// utility function for loading a 2D texture from file

// ---------------------------------------------------

unsigned int loadTexture(char const * path)

{

unsigned int textureID;

glGenTextures(1, &textureID);

int width, height, nrComponents;

unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);

if (data)

{

GLenum format;

if (nrComponents == 1)

format = GL_RED;

else if (nrComponents == 3)

format = GL_RGB;

else if (nrComponents == 4)

format = GL_RGBA;

glBindTexture(GL_TEXTURE_2D, textureID);

glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);

glGenerateMipmap(GL_TEXTURE_2D);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

stbi_image_free(data);

}

else

{

std::cout << "Texture failed to load at path: " << path << std::endl;

stbi_image_free(data);

}

return textureID;

}

unsigned int loadCubemap(vector<:string> faces)

{

unsigned int textureID;

glGenTextures(1, &textureID);

glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

int width, height, nrChannels;

for (unsigned int i = 0; i < faces.size(); i++) {

unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);

if (data)

{

glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

stbi_image_free(data);

}

else

{

std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;

stbi_image_free(data);

}

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

}

return textureID;

}

void renderScene(const Shader &shader)

{

// room cube

glm::mat4 model = glm::mat4(1.0f);

model = glm::scale(model, glm::vec3(5.0f));

shader.setMat4("model", model);

glDisable(GL_CULL_FACE); // note that we disable culling here since we render 'inside' the cube instead of the usual 'outside' which throws off the normal culling methods.

shader.setInt("reverse_normals", 1); // A small little hack to invert normals when drawing cube from the inside so lighting still works.

renderCube();

shader.setInt("reverse_normals", 0); // and of course disable it

glEnable(GL_CULL_FACE);

// cubes

model = glm::mat4(1.0f);

model = glm::translate(model, glm::vec3(4.0f, -3.5f, 0.0));

model = glm::scale(model, glm::vec3(0.5f));

shader.setMat4("model", model);

renderCube();

model = glm::mat4(1.0f);

model = glm::translate(model, glm::vec3(2.0f, 3.0f, 1.0));

model = glm::scale(model, glm::vec3(0.75f));

shader.setMat4("model", model);

renderCube();

model = glm::mat4(1.0f);

model = glm::translate(model, glm::vec3(-3.0f, -1.0f, 0.0));

model = glm::scale(model, glm::vec3(0.5f));

shader.setMat4("model", model);

renderCube();

model = glm::mat4(1.0f);

model = glm::translate(model, glm::vec3(-1.5f, 1.0f, 1.5));

model = glm::scale(model, glm::vec3(0.5f));

shader.setMat4("model", model);

renderCube();

model = glm::mat4(1.0f);

model = glm::translate(model, glm::vec3(-1.5f, 2.0f, -3.0));

model = glm::rotate(model, glm::radians(60.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0)));

model = glm::scale(model, glm::vec3(0.75f));

shader.setMat4("model", model);

renderCube();

}

// renderCube() renders a 1x1 3D cube in NDC.

// -------------------------------------------------

unsigned int cubeVAO = 0;

unsigned int cubeVBO = 0;

void renderCube()

{

// initialize (if necessary)

if (cubeVAO == 0)

{

float vertices[] = {

// back face

-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left

1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right

1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right

1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right

-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left

-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left

// front face

-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left

1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right

1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right

1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right

-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left

-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left

// left face

-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right

-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left

-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left

-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left

-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right

-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right

// right face

1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left

1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right

1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right

1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right

1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left

1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left

// bottom face

-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right

1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left

1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left

1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left

-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right

-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right

// top face

-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left

1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right

1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right

1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right

-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left

-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left

};

glGenVertexArrays(1, &cubeVAO);

glGenBuffers(1, &cubeVBO);

// fill buffer

glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// link vertex attributes

glBindVertexArray(cubeVAO);

glEnableVertexAttribArray(0);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);

glEnableVertexAttribArray(1);

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));

glEnableVertexAttribArray(2);

glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));

glBindBuffer(GL_ARRAY_BUFFER, 0);

glBindVertexArray(0);

}

glBindVertexArray(cubeVAO);

glDrawArrays(GL_TRIANGLES, 0, 36);

glBindVertexArray(0);

}

// RenderQuad() Renders a 1x1 quad in NDC

unsigned int quadVAO = 0;

unsigned int quadVBO;

void RenderQuad()

{

if (quadVAO == 0)

{

GLfloat quadVertices[] = {

// Positions // Texture Coords

-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,

-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,

1.0f, 1.0f, 0.0f, 1.0f, 1.0f,

1.0f, -1.0f, 0.0f, 1.0f, 0.0f,

};

// Setup plane VAO

glGenVertexArrays(1, &quadVAO);

glGenBuffers(1, &quadVBO);

glBindVertexArray(quadVAO);

glBindBuffer(GL_ARRAY_BUFFER, quadVBO);

glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);

glEnableVertexAttribArray(0);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);

glEnableVertexAttribArray(1);

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));

}

glBindVertexArray(quadVAO);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glBindVertexArray(0);

}

unsigned int sphereVAO = 0;

unsigned int indexCount;

void renderSphere()

{

if (sphereVAO == 0)

{

glGenVertexArrays(1, &sphereVAO);

unsigned int vbo, ebo;

glGenBuffers(1, &vbo);

glGenBuffers(1, &ebo);

std::vector<:vec3> positions;

std::vector<:vec2> uv;

std::vector<:vec3> normals;

std::vector indices;

const unsigned int X_SEGMENTS = 64;

const unsigned int Y_SEGMENTS = 64;

const float PI = 3.14159265359;

for (unsigned int y = 0; y <= Y_SEGMENTS; ++y)

{

for (unsigned int x = 0; x <= X_SEGMENTS; ++x)

{

float xSegment = (float)x / (float)X_SEGMENTS;

float ySegment = (float)y / (float)Y_SEGMENTS;

float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI);

float yPos = std::cos(ySegment * PI);

float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI);

positions.push_back(glm::vec3(xPos, yPos, zPos));

uv.push_back(glm::vec2(xSegment, ySegment));

normals.push_back(glm::vec3(xPos, yPos, zPos));

}

}

bool oddRow = false;

for (unsigned int y = 0; y < Y_SEGMENTS; ++y)

{

if (!oddRow) // even rows: y == 0, y == 2; and so on

{

for (unsigned int x = 0; x <= X_SEGMENTS; ++x)

{

indices.push_back(y * (X_SEGMENTS + 1) + x);

indices.push_back((y + 1) * (X_SEGMENTS + 1) + x);

}

}

else

{

for (int x = X_SEGMENTS; x >= 0; --x)

{

indices.push_back((y + 1) * (X_SEGMENTS + 1) + x);

indices.push_back(y * (X_SEGMENTS + 1) + x);

}

}

oddRow = !oddRow;

}

indexCount = indices.size();

std::vector data;

for (unsigned int i = 0; i < positions.size(); ++i)

{

data.push_back(positions[i].x);

data.push_back(positions[i].y);

data.push_back(positions[i].z);

if (uv.size() > 0)

{

data.push_back(uv[i].x);

data.push_back(uv[i].y);

}

if (normals.size() > 0)

{

data.push_back(normals[i].x);

data.push_back(normals[i].y);

data.push_back(normals[i].z);

}

}

glBindVertexArray(sphereVAO);

glBindBuffer(GL_ARRAY_BUFFER, vbo);

glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), &data[0], GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);

float stride = (3 + 2 + 3) * sizeof(float);

glEnableVertexAttribArray(0);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0);

glEnableVertexAttribArray(1);

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*)(3 * sizeof(float)));

glEnableVertexAttribArray(2);

glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (void*)(5 * sizeof(float)));

}

glBindVertexArray(sphereVAO);

glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_INT, 0);

}

android n 字体渲染,38.opengl-字体渲染相关推荐

  1. Android OpenGL ES 渲染文本

    1 前言 先来个灵魂拷问:为什么要研究OpenGL渲染文本? 用Android的canvas,不是更香吗?! 这就看应用场景了,一个纯粹的UI界面,确实不需要用到OpenGL,但是,复杂一些的,例如弹 ...

  2. android 动画引擎,一个使用openGL渲染的炫丽Android动画库

    这是一个 android 动画特效库 可以实现各种炫酷动画. github地址: ht t ps:// gith  u b.co m/g pl ib s/an dro id- ma gic-s ur ...

  3. html字体加载规则,CSS-等待字体加载,然后渲染网页

    CSS-等待字体加载,然后渲染网页 我正在使用@ font-face将字体嵌入到我的网站中. 首先,文本将作为系统默认值呈现,然后(假定字体文件已加载)正确的字体将在几分之一秒后呈现. 有没有一种方法 ...

  4. linux tty字体,ArchLinux TTY 中文字体渲染

    ArchLinux 的 User Centrality 原则中提出, 该发行版意图满足贡献者的需求,而不是吸引尽量多的用户. 正是这一原则使得 ArchLinux 吸引了大量的开发者, 其 AUR 也 ...

  5. Android OpenGL+Camera2渲染(3) —— 大眼,贴纸功能实现

    Android OpenGL+Camera2渲染(1) -- OpenGL简单介绍 Android OpenGL+Camera2渲染(2) -- OpenGL实现Camera2图像预览 Android ...

  6. Android硬件加速(二)-RenderThread与OpenGL GPU渲染

    Android4.0之后,系统默认开启硬件加速来渲染视图,之前,理解Android硬件加速的小白文简单的讲述了硬件加速的简单模型,不过主要针对前半阶段,并没怎么说是如何使用OpenGL.GPU处理数据 ...

  7. Android OpenGL+Camera2渲染(2) —— OpenGL实现Camera2图像预览

    Android OpenGL+Camera2渲染(1) -- OpenGL简单介绍 Android OpenGL+Camera2渲染(2) -- OpenGL实现Camera2图像预览 Android ...

  8. qt android opengl,案例:实现Qt和OpenGL混合渲染 | 求索阁

    Qt自有一个绘图的引擎,这个引擎的核心就是QPainter,我们知道QPainter在渲染二维图形和文字有很大的优势,而OpenGL是当前流行的三维渲染器,怎样才能将两者结合起来,制作更为丰富的应用程 ...

  9. OpenGL 文本渲染Text Rendering

    OpenGL文本渲染Text Rendering 文本渲染Text Rendering简介 经典文本渲染:位图字体 现代文本渲染:FreeType 着色器 渲染一行文本 更进一步 文本渲染Text R ...

最新文章

  1. 【LVS】负载均衡集群
  2. oracle服务怎么删除数据库,Oracle中手动删除数据库教程
  3. Zipline Development Guidelines
  4. Java中用JS那些_java web中javascript主要用哪些?
  5. linux 查看shell脚本执行了多长时间
  6. 第13篇: Flowable-BPMN操作流程之流程进展查看之流程图
  7. 牛客OI周赛6-提高组 B 践踏
  8. 算法之路 level 01 problem set
  9. excel密码破解软件Excel Password Unlocker下载和使用技巧(亲测有效!)
  10. tune声场测试软件_SIA SmaartLive(声场测试软件) V7.2.1 官方版
  11. MatLab 2016b下载资源
  12. 虚拟机安装linux系统教程
  13. SWFObject 2.0 官方文档二
  14. 新浪微博批量删除微博方法
  15. 使用MetaHuman Creator 塑造你心中的人物
  16. FPGA内部资源结构——以Altera CycloneⅣ 为例
  17. Java微信支付开发之查询订单
  18. 从软件测试培训班出来后找工作的这段经历,教会了我这五件事...
  19. linux利用源码安装madplay
  20. 【用三大件写出的开门烟花特效】

热门文章

  1. html怎么让两个div重叠,两个DIV或多个DIV顺序重叠加
  2. python爬虫抓取分页_Python爬虫—简书首页数据抓取
  3. android编程strings,Android开发: strings.xml文件中的错误
  4. 优化Python开发环境的几个神技巧
  5. 用python编写猜成语游戏_10分钟学会用python写游戏!Python其实很简单!
  6. python一键安装神器_超级实用的Python环境搭建以及神器推荐!抓紧时间收藏
  7. MATLAB绘制Z平面
  8. C语言数组之指针数组和数组指针
  9. Sharepoint 最终作用是什么
  10. 超级简单的EOS代币转账教程(EETH)