android n 字体渲染,38.opengl-字体渲染

一、文本渲染原理

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

050f7cb3cb9c

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

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

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

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

1.2 现代文本渲染

050f7cb3cb9c

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

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

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

FreeType有以下优点:

1)跨平台

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

二、基于freetype渲染文本

050f7cb3cb9c

文本渲染

050f7cb3cb9c

文本轮廓

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

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下,否则会编译报错

050f7cb3cb9c

2. Xcode环境变量配置

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

050f7cb3cb9c

2.2 Search Paths 增加Header和 Library

050f7cb3cb9c

2.3 Build Phases 增加库依赖

050f7cb3cb9c

四、完整代码

#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);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值