├── 3rdParty.rar
├── README.md
├── Render_with_OpenGL
├── include
│ ├── Camera.h
│ ├── Light.h
│ ├── Mesh.h
│ ├── Shader.h
│ └── Texture2D.h
├── shader
│ ├── text3D.frag
│ └── text3D.vert
└── src
│ └── RenderText3D.cpp
├── Text3D
├── include
│ ├── FreeTypeFont.h
│ ├── Glyph3D.h
│ ├── Tessellator.h
│ └── TriangleIndexFunctor.h
└── src
│ ├── Glyph3D.cpp
│ └── Tessellator.cpp
└── fonts
├── STXINWEI.TTF
├── arial.ttf
└── simhei.ttf
/3rdParty.rar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Xiangwk/Text3D_with_OpenGL/60c68ded3e380d13052ca6456f182c0d4c0293d1/3rdParty.rar
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Text3D_with_OpenGL
2 | Render 3D Texts with OpenGL!
3 |
4 | 这是一个用OpenGL渲染3D文字的程序,想要全面了解其原理,需要具备以下知识:
5 |
6 | 1. C/C++
7 | 2. OpenGL & GLSL(OpenGL基本的绘制方式以及顶点着色器和片元着色器)
8 | 3. 计算机图形学(二次贝塞尔曲线)
9 | 4. FreeType2(涉及FT_Outline_Decompose函数,需要了解字形轮廓的分解)
10 | 5. 计算几何相关算法(多边形的三角剖分)
11 |
12 | 当前的实现版本使用GLU库进行三角剖分,不过GLU库非常古老,不太好用了~不久后将会实现我们自己的三角剖分程序!
13 |
14 | 先来看一下基本的流程:
15 | 1. 使用FreeType2获取字形的轮廓信息,我们将获取到的信息封装为FreeTypeFont这个类型;
16 | 2. 使用GLU库对获取到的字形轮廓进行多边形的三角剖分,生成三角网格面片,注意,这时的字形还不是3D文字,而是只有一个面;
17 | 3. 使用在上一步生成的字形三角网格面生成3D文字的背面和侧面;
18 | 4. 使用OpenGL对3D文字模型进行渲染;
19 |
20 | 注:
21 |
22 | 所有第三方依赖库都放在了3rdParty.rar里面,而实现文件都在Text3D文件夹下面!
23 |
24 | FreeType的文档请看这里:https://www.freetype.org/freetype2/docs/documentation.html
25 |
26 | 在处理图形学中的数学类型时,我们使用了glm库,参考这里:https://github.com/g-truc/glm/tags
27 |
28 | GLU库的文档可以看这里https://khronos.org/registry/OpenGL/specs/gl/glu1.3.pdf
29 |
30 | Windows系统的字体文件可以在C:\Windows\Fonts路径下找到!
31 |
32 | Text3D工程中需要包含的第三方依赖库如下图:
33 |
34 |
35 |
36 | Render_with_OpenGL工程中需要包含的第三方依赖库如下图:
37 |
38 |
39 |
40 | 其中opengl32.lib和glu32.lib是已经在Windows系统中存在的了,其他系统尚不清楚,抱歉~
41 |
42 | 下图为实现效果:
43 |
44 | 
45 |
46 | 项目仍在持续更行中,敬请期待!
47 |
48 | 文档尚未完成,README NOT COMPLETED YET!
49 |
--------------------------------------------------------------------------------
/Render_with_OpenGL/include/Camera.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | enum CameraMovement{ FORWARD, BACKWARD, LEFT, RIGHT };
12 |
13 | //the euler angle and camera option's initialize value
14 | const float YAW = -90.0f;
15 | const float PITCH = 0.0f;
16 | const float SPEED = 2.5f;
17 | const float SENSITIVITY = 0.1f;
18 | const float ZOOM = 45.0f;
19 |
20 | //this is an abstract base class
21 | class BaseCamera
22 | {
23 | public:
24 | glm::vec3 position; //camera's position
25 | glm::vec3 front; //camera's front vector
26 | glm::vec3 up; //camera's up vector
27 | glm::vec3 right; //camera's right vector
28 | glm::vec3 worldUp; //world's up vector, it's [0, 1, 0] commonly
29 |
30 | GLfloat pitch;
31 | GLfloat yaw;
32 |
33 | GLfloat moveSpeed;
34 | GLfloat mouseSensitivity;
35 | GLfloat zoom;
36 |
37 | BaseCamera(const glm::vec3 &pos = glm::vec3(0.0f, 0.0f, 0.0f),
38 | const glm::vec3 &u = glm::vec3(0.0f, 1.0f, 0.0f),
39 | GLfloat p = PITCH, GLfloat y = YAW);
40 | //get view matrix, it is the essential of an opengl camera
41 | glm::mat4 getViewMatrix();
42 | //camera movement
43 | virtual void processKeyboard(CameraMovement direction, GLfloat deltaTime) = 0;
44 | //camera rotation
45 | virtual void processMouseMovement(GLfloat x, GLfloat y);
46 | //camera' zoom
47 | virtual void processMouseScroll(GLfloat z);
48 |
49 | private:
50 | //according to the eular angles to construct the camera's coordinate
51 | void updateCamera();
52 | };
53 |
54 | BaseCamera::BaseCamera(const glm::vec3 &pos, const glm::vec3 &u, GLfloat p, GLfloat y) :
55 | position(pos), worldUp(u), pitch(p), yaw(y), moveSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM)
56 | {
57 | updateCamera();
58 | }
59 |
60 | void BaseCamera::updateCamera()
61 | {
62 | front.x = std::cos(glm::radians(pitch)) * std::cos(glm::radians(yaw));
63 | front.y = std::sin(glm::radians(pitch));
64 | front.z = std::cos(glm::radians(pitch)) * std::sin(glm::radians(yaw));
65 | front = glm::normalize(front);
66 |
67 | right = glm::normalize(glm::cross(front, worldUp));
68 | up = glm::normalize(glm::cross(right, front));
69 | }
70 |
71 | glm::mat4 BaseCamera::getViewMatrix()
72 | {
73 | return glm::lookAt(position, position + front, up);
74 | }
75 |
76 | void BaseCamera::processKeyboard(CameraMovement direction, GLfloat deltaTime)
77 | {
78 | GLfloat v = moveSpeed * deltaTime;
79 | switch (direction)
80 | {
81 | case FORWARD:
82 | position += front * v;
83 | break;
84 | case BACKWARD:
85 | position -= front * v;
86 | break;
87 | case LEFT:
88 | position -= right * v;
89 | break;
90 | case RIGHT:
91 | position += right * v;
92 | break;
93 | default:
94 | break;
95 | }
96 | }
97 |
98 | void BaseCamera::processMouseMovement(GLfloat deltaX, GLfloat deltaY)
99 | {
100 | //the x-oriented position's difference in one frame
101 | deltaX *= mouseSensitivity;
102 | //the y-oriented position's difference in one frame
103 | deltaY *= mouseSensitivity;
104 |
105 | yaw += deltaX;
106 | pitch += deltaY;
107 |
108 | if (pitch > 89.0f)
109 | pitch = 89.0f;
110 | else if (pitch < -89.0f)
111 | pitch = -89.0f;
112 | //because we changed eular angle, we update the camera's coordinate
113 | updateCamera();
114 | }
115 |
116 | void BaseCamera::processMouseScroll(GLfloat z)
117 | {
118 | if (zoom >= 1.0f && zoom <= 45.0f)
119 | zoom -= z;
120 | if (zoom < 1.0f)
121 | zoom = 1.0f;
122 | if (zoom > 45.0f)
123 | zoom = 45.0f;
124 | }
125 |
126 | //this is a free camera that can move everywhere in the space
127 | class FreeCamera : public BaseCamera
128 | {
129 | public:
130 | FreeCamera(const glm::vec3 &pos = glm::vec3(0.0f, 0.0f, 0.0f),
131 | const glm::vec3 &u = glm::vec3(0.0f, 1.0f, 0.0f),
132 | GLfloat p = PITCH, GLfloat y = YAW);
133 | void processKeyboard(CameraMovement direction, GLfloat deltaTime) override;
134 | };
135 |
136 | FreeCamera::FreeCamera(const glm::vec3 &pos, const glm::vec3 &u, GLfloat p, GLfloat y) : BaseCamera(pos, u, p, y){}
137 |
138 | void FreeCamera::processKeyboard(CameraMovement direction, GLfloat deltaTime)
139 | {
140 | BaseCamera::processKeyboard(direction, deltaTime);
141 | }
142 |
143 | //this is a fps camera that can move only on the ground(the plane which y = 0)
144 | class FPSCamera : public BaseCamera
145 | {
146 | public:
147 | FPSCamera::FPSCamera(const glm::vec3 &pos = glm::vec3(0.0f, 0.0f, 0.0f),
148 | const glm::vec3 &u = glm::vec3(0.0f, 1.0f, 0.0f),
149 | GLfloat p = PITCH, GLfloat y = YAW);
150 | void processKeyboard(CameraMovement direction, GLfloat deltaTime) override;
151 | };
152 |
153 | FPSCamera::FPSCamera(const glm::vec3 &pos, const glm::vec3 &u, GLfloat p, GLfloat y) : BaseCamera(pos, u, p, y){}
154 |
155 | void FPSCamera::processKeyboard(CameraMovement direction, GLfloat deltaTime)
156 | {
157 | BaseCamera::processKeyboard(direction, deltaTime);
158 | //we cannot leave the ground
159 | position.y = 0;
160 | }
--------------------------------------------------------------------------------
/Render_with_OpenGL/include/Light.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | //attenuation struct
8 | struct Attenuation
9 | {
10 | GLfloat constant;
11 | GLfloat linear;
12 | GLfloat quadratic;
13 | };
14 | //cutoff struct
15 | struct Cutoff
16 | {
17 | //cutoff angle
18 | GLfloat innerCutoff;
19 | GLfloat outerCutoff;
20 | };
21 |
22 | class DirLight
23 | {
24 | public:
25 | glm::vec3 direction;
26 | glm::vec3 ambient;
27 | glm::vec3 diffuse;
28 | glm::vec3 specular;
29 |
30 | void setUniform(GLuint id, GLuint offset);
31 | };
32 |
33 | class PointLight
34 | {
35 | public:
36 | glm::vec3 position;
37 |
38 | glm::vec3 ambient;
39 | glm::vec3 diffuse;
40 | glm::vec3 specular;
41 |
42 | //the attenuation factor
43 | Attenuation atten;
44 |
45 | void setUniform(GLuint id, GLuint offset);
46 | };
47 |
48 | class SpotLight
49 | {
50 | public:
51 | glm::vec3 position;
52 | glm::vec3 direction;
53 |
54 | glm::vec3 ambient;
55 | glm::vec3 diffuse;
56 | glm::vec3 specular;
57 | //the attenuation factor
58 | Attenuation atten;
59 | //cutoff angle
60 | Cutoff cutoff;
61 |
62 | void setUniform(GLuint id, GLuint offset);
63 | };
64 |
65 | void DirLight::setUniform(GLuint id, GLuint offset)
66 | {
67 | glBindBuffer(GL_UNIFORM_BUFFER, id);
68 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 0, 16, glm::value_ptr(direction));
69 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 16, 16, glm::value_ptr(ambient));
70 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 32, 16, glm::value_ptr(diffuse));
71 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 48, 16, glm::value_ptr(specular));
72 | glBindBuffer(GL_UNIFORM_BUFFER, 0);
73 | }
74 |
75 | void PointLight::setUniform(GLuint id, GLuint offset)
76 | {
77 | glBindBuffer(GL_UNIFORM_BUFFER, id);
78 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 0, 16, glm::value_ptr(position));
79 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 16, 16, glm::value_ptr(ambient));
80 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 32, 16, glm::value_ptr(diffuse));
81 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 48, 16, glm::value_ptr(specular));
82 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 64, 4, &atten.constant);
83 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 68, 4, &atten.linear);
84 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 72, 4, &atten.quadratic);
85 | glBindBuffer(GL_UNIFORM_BUFFER, 0);
86 | }
87 |
88 | void SpotLight::setUniform(GLuint id, GLuint offset)
89 | {
90 | glBindBuffer(GL_UNIFORM_BUFFER, id);
91 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 0, 16, glm::value_ptr(position));
92 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 16, 16, glm::value_ptr(direction));
93 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 32, 16, glm::value_ptr(ambient));
94 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 48, 16, glm::value_ptr(diffuse));
95 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 64, 16, glm::value_ptr(specular));
96 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 80, 4, &atten.constant);
97 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 84, 4, &atten.linear);
98 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 88, 4, &atten.quadratic);
99 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 96, 4, &cutoff.innerCutoff);
100 | glBufferSubData(GL_UNIFORM_BUFFER, offset + 100, 4, &cutoff.outerCutoff);
101 | glBindBuffer(GL_UNIFORM_BUFFER, 0);
102 | }
--------------------------------------------------------------------------------
/Render_with_OpenGL/include/Mesh.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | #include
9 | #include
10 |
11 | #include
12 |
13 | struct Vertex
14 | {
15 | glm::vec3 position;
16 | glm::vec3 normal;
17 | glm::vec2 texCoord;
18 |
19 | /*Vertex(const glm::vec3 &pos, const glm::vec3 &n, const glm::vec2 &tex = glm::vec2()) :
20 | position(pos), normal(n), texCoord(tex) {}*/
21 | };
22 |
23 | class Mesh
24 | {
25 | public:
26 | std::vector vertices;
27 | std::vector indices;
28 | std::vector textures;
29 |
30 | GLuint VAO, VBO, EBO;
31 |
32 | explicit Mesh(const std::vector &v, const std::vector &i = {}, const std::vector &t = {});
33 | void draw(Shader shader) const;
34 |
35 | private:
36 | void setupVAO();
37 | };
38 |
39 | Mesh::Mesh(const std::vector &v, const std::vector &i, const std::vector &t) :
40 | vertices(v), indices(i), textures(t)
41 | {
42 | setupVAO();
43 | }
44 |
45 | void Mesh::draw(Shader shader) const
46 | {
47 | unsigned diffNum = 1, specNum = 1;
48 | for (size_t i = 0; i < textures.size(); ++i)
49 | {
50 | glActiveTexture(GL_TEXTURE0 + i);
51 | int n = 0;
52 | std::string t = textures[i].type;
53 | //we use n to judge the texture's name in shader
54 | //in shader we must named textures as "texture_diffuseN" or "texture_specularN" or others(N is start from 1)
55 | if (t == "texture_diffuse")
56 | n = diffNum++;
57 | else if (t == "texture_specular")
58 | n = specNum++;
59 | std::string N = std::to_string(n);
60 | //don't forget "material.", in shader the sampler of texture is in Material structure
61 | shader.setUniformInt("material." + t + N, i);
62 | glBindTexture(GL_TEXTURE_2D, textures[i].id);
63 | }
64 |
65 | glBindVertexArray(VAO);
66 | if (!indices.empty())
67 | glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
68 | else
69 | glDrawArrays(GL_TRIANGLES, 0, vertices.size());
70 | glBindVertexArray(0);
71 |
72 | glActiveTexture(GL_TEXTURE0);
73 | }
74 |
75 | void Mesh::setupVAO()
76 | {
77 | if (vertices.empty())
78 | {
79 | std::cerr << "Vertices load failed!" << std::endl;
80 | return;
81 | }
82 |
83 | glGenVertexArrays(1, &VAO);
84 | glGenBuffers(1, &VBO);
85 | glGenBuffers(1, &EBO);
86 |
87 | glBindVertexArray(VAO);
88 | glBindBuffer(GL_ARRAY_BUFFER, VBO);
89 | glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
90 | if (!indices.empty())
91 | {
92 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
93 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
94 | }
95 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
96 | glEnableVertexAttribArray(0);
97 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
98 | glEnableVertexAttribArray(1);
99 | glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texCoord));
100 | glEnableVertexAttribArray(2);
101 | glBindVertexArray(0);
102 | }
--------------------------------------------------------------------------------
/Render_with_OpenGL/include/Shader.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | class Shader
14 | {
15 | public:
16 | GLuint program;
17 |
18 | Shader(const std::string &vPath, const std::string &fPath);
19 | void use() const;
20 | void setUniformBool(const std::string &name, GLboolean value) const;
21 | void setUniformInt(const std::string &name, GLint value) const;
22 | void setUniformFloat(const std::string &name, GLfloat value) const;
23 | void setUniformVec3(const std::string &name, const glm::vec3 &v);
24 | void setUniformMat4(const std::string &name, const glm::mat4 &m);
25 |
26 | private:
27 | void checkErro(GLuint shader, const std::string &type) const;
28 | };
29 |
30 | Shader::Shader(const std::string &vPath, const std::string &fPath)
31 | {
32 | //read the code from shader file
33 | std::ifstream vertFile, fragFile;
34 | std::string vertCode, fragCode;
35 | vertFile.exceptions(std::ifstream::badbit | std::ifstream::failbit);
36 | fragFile.exceptions(std::ifstream::badbit | std::ifstream::failbit);
37 | try
38 | {
39 | vertFile.open(vPath);
40 | fragFile.open(fPath);
41 | std::stringstream vStream, fStream;
42 | vStream << vertFile.rdbuf();
43 | fStream << fragFile.rdbuf();
44 | vertFile.close();
45 | fragFile.close();
46 | vertCode = vStream.str();
47 | fragCode = fStream.str();
48 | }
49 | catch (std::ifstream::failure e)
50 | {
51 | std::cerr << "ERRO::SHADER::FILE_NOT_SUCCESSFULLY_READ";
52 | std::abort();
53 | }
54 | const char *vc = vertCode.c_str();
55 | const char *fc = fragCode.c_str();
56 | GLint success = 0;
57 | char infoLog[512] = {};
58 | //compile vertex shader
59 | GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
60 | glShaderSource(vshader, 1, &vc, nullptr);
61 | glCompileShader(vshader);
62 | checkErro(vshader, "VERTEX");
63 |
64 | //compile fragment shader
65 | GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
66 | glShaderSource(fshader, 1, &fc, nullptr);
67 | glCompileShader(fshader);
68 | checkErro(fshader, "FRAGMENT");
69 |
70 | //create shader program and link the shaders
71 | program = glCreateProgram();
72 | glAttachShader(program, vshader);
73 | glAttachShader(program, fshader);
74 | glLinkProgram(program);
75 | checkErro(program, "PROGRAM");
76 |
77 | glDeleteShader(vshader);
78 | glDeleteShader(fshader);
79 | }
80 |
81 | void Shader::use() const
82 | {
83 | glUseProgram(program);
84 | }
85 |
86 | void Shader::setUniformBool(const std::string &name, GLboolean value) const
87 | {
88 | glUniform1i(glGetUniformLocation(program, name.c_str()), value);
89 | }
90 | void Shader::setUniformInt(const std::string &name, GLint value) const
91 | {
92 | glUniform1i(glGetUniformLocation(program, name.c_str()), value);
93 | }
94 |
95 | void Shader::setUniformFloat(const std::string &name, GLfloat value) const
96 | {
97 | glUniform1f(glGetUniformLocation(program, name.c_str()), value);
98 | }
99 |
100 | void Shader::setUniformVec3(const std::string &name, const glm::vec3 &v)
101 | {
102 | glUniform3fv(glGetUniformLocation(program, name.c_str()), 1, glm::value_ptr(v));
103 | }
104 |
105 | void Shader::setUniformMat4(const std::string &name, const glm::mat4 &m)
106 | {
107 | glUniformMatrix4fv(glGetUniformLocation(program, name.c_str()), 1, GL_FALSE, glm::value_ptr(m));
108 | }
109 |
110 | void Shader::checkErro(GLuint shader, const std::string &type) const
111 | {
112 | GLint success = 0;
113 | char infoLog[512] = {};
114 |
115 | if (type != "PROGRAM")
116 | {
117 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
118 | if (!success)
119 | {
120 | glGetShaderInfoLog(shader, 512, nullptr, infoLog);
121 | std::cerr << "ERRO::SHADER::" << type << "::COMPILATION_FAILD\n" << infoLog;
122 | std::abort();
123 | }
124 | }
125 | else
126 | {
127 | glGetProgramiv(shader, GL_LINK_STATUS, &success);
128 | if (!success)
129 | {
130 | glGetProgramInfoLog(shader, 512, nullptr, infoLog);
131 | std::cerr << "ERRO::PROGRAM::LINKED_FAILD" << infoLog;
132 | std::abort();
133 | }
134 | }
135 | }
--------------------------------------------------------------------------------
/Render_with_OpenGL/include/Texture2D.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 |
7 | #include
8 | #include
9 |
10 | struct Texture2D
11 | {
12 | GLuint id;
13 | std::string type; //texture's type(diffuse texture or specular texture or others)
14 | std::string path; //texture source image's file path(this member is used to identify the texture's source image)
15 |
16 | void loadFromFile(const std::string p);
17 | };
18 |
19 | void Texture2D::loadFromFile(const std::string p)
20 | {
21 | glGenTextures(1, &id);
22 |
23 | int width, height, nrComponents;
24 | unsigned char *data = stbi_load(p.c_str(), &width, &height, &nrComponents, 0);
25 | if (data)
26 | {
27 | GLenum format;
28 | switch (nrComponents)
29 | {
30 | case 1:
31 | format = GL_RED;
32 | break;
33 | case 3:
34 | format = GL_RGB;
35 | break;
36 | case 4:
37 | format = GL_RGBA;
38 | break;
39 | default:
40 | break;
41 | }
42 | glBindTexture(GL_TEXTURE_2D, id);
43 | glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
44 | glGenerateMipmap(GL_TEXTURE_2D);
45 |
46 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
47 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
48 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
49 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
50 | }
51 | else
52 | {
53 | std::cerr << "Failed to load image from file: " << p << std::endl;
54 | }
55 | stbi_image_free(data);
56 | }
--------------------------------------------------------------------------------
/Render_with_OpenGL/shader/text3D.frag:
--------------------------------------------------------------------------------
1 | #version 420 core
2 |
3 | in vec3 Normal;
4 | in vec3 FragPos;
5 | in vec2 TexCoord;
6 |
7 | out vec4 fragColor;
8 |
9 | struct Material
10 | {
11 | vec3 ambient;
12 | vec3 diffuse;
13 | vec3 specular;
14 | float shininess;
15 | };
16 |
17 | struct Attenuation
18 | {
19 | float constant;
20 | float linear;
21 | float quadratic;
22 | };
23 |
24 | struct Cutoff
25 | {
26 | float cutoff;
27 | float outCutoff;
28 | };
29 |
30 | struct DirectionalLight
31 | {
32 | vec3 direction;
33 |
34 | vec3 ambient;
35 | vec3 diffuse;
36 | vec3 specular;
37 | };
38 |
39 | struct PointLight
40 | {
41 | vec3 position;
42 |
43 | vec3 ambient;
44 | vec3 diffuse;
45 | vec3 specular;
46 |
47 | //the attenuation factor
48 | Attenuation atten;
49 | };
50 |
51 | struct SpotLight
52 | {
53 | vec3 position;
54 | vec3 direction;
55 |
56 | vec3 ambient;
57 | vec3 diffuse;
58 | vec3 specular;
59 |
60 | //the attenuation factor
61 | Attenuation atten;
62 |
63 | //cutoff angle
64 | Cutoff co;
65 | };
66 |
67 | const int PointLightNum = 1;
68 | const int SpotLightNum = 1;
69 |
70 | uniform Material material;
71 |
72 | layout(std140, binding = 1) uniform Lights
73 | {
74 | DirectionalLight dirLight;
75 | PointLight pointLights[PointLightNum];
76 | SpotLight torch[SpotLightNum];
77 | };
78 |
79 | uniform vec3 viewPos;
80 |
81 | float diff(vec3 lightDir, vec3 normal);
82 | float spec(vec3 lightDir, vec3 normal, vec3 viewDir, float shininess);
83 |
84 | vec3 calcDirLight(DirectionalLight light, vec3 normal, vec3 viewPos);
85 | vec3 calcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewPos);
86 | vec3 calcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewPos);
87 |
88 | void main()
89 | {
90 | vec3 result;
91 | result += calcDirLight(dirLight, Normal, viewPos);
92 | for(int i = 0; i < PointLightNum; ++i)
93 | result += calcPointLight(pointLights[i], Normal, FragPos, viewPos);
94 | for(int i = 0; i < SpotLightNum; ++i)
95 | result += calcSpotLight(torch[i], Normal, FragPos, viewPos);
96 |
97 | fragColor = vec4(result, 1.0f);
98 | }
99 |
100 | float diff(vec3 lightDir, vec3 normal)
101 | {
102 | return max(dot(lightDir, normal), 0.0f);
103 | }
104 |
105 | float spec(vec3 lightDir, vec3 normal, vec3 viewDir, float shininess)
106 | {
107 | vec3 rd = reflect(-lightDir, normal);
108 | return pow(max(dot(rd, viewDir), 0.0f), shininess);
109 | }
110 |
111 | vec3 calcDirLight(DirectionalLight light, vec3 normal, vec3 viewPos)
112 | {
113 | vec3 ld = normalize(-light.direction);
114 | vec3 n = normalize(normal);
115 | vec3 vd = normalize(viewPos - FragPos);
116 | vec3 ambient = light.ambient * material.ambient;
117 | vec3 diffuse = light.diffuse * diff(ld, n) * material.diffuse;
118 | vec3 specular = light.specular * spec(ld, n, vd, material.shininess) * material.specular;
119 |
120 | return ambient + diffuse + specular;
121 | }
122 |
123 | vec3 calcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewPos)
124 | {
125 | vec3 ld = normalize(light.position - fragPos);
126 | vec3 n = normalize(normal);
127 | vec3 vd = normalize(viewPos - FragPos);
128 |
129 | //calculate attenuation
130 | float d = length(light.position - fragPos);
131 | float atten = 1.0f / (light.atten.constant + light.atten.linear * d + light.atten.quadratic * d * d);
132 |
133 | vec3 ambient = light.ambient * material.ambient;
134 | vec3 diffuse = light.diffuse * diff(ld, n) * material.diffuse;
135 | vec3 specular = light.specular * spec(ld, n, vd, material.shininess) * material.specular;
136 |
137 | return (ambient + diffuse + specular) * atten;
138 | }
139 |
140 | vec3 calcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewPos)
141 | {
142 | vec3 ld = normalize(light.position - fragPos);
143 | vec3 n = normalize(normal);
144 | vec3 vd = normalize(viewPos - FragPos);
145 |
146 | //calculate cutoff
147 | float theta = dot(-ld, light.direction);
148 | float epsilon = light.co.cutoff - light.co.outCutoff;
149 | float iten = clamp((theta - light.co.outCutoff) / epsilon, 0.0f, 1.0f);
150 |
151 | //calculate attenuation
152 | float d = length(light.position - fragPos);
153 | float atten = 1.0f / (light.atten.constant + light.atten.linear * d + light.atten.quadratic * d * d);
154 |
155 | vec3 ambient = light.ambient * material.ambient;
156 | vec3 diffuse = light.diffuse * diff(ld, n) * material.diffuse;
157 | vec3 specular = light.specular * spec(ld, n, vd, material.shininess) * material.specular;
158 |
159 | return (ambient + (diffuse + specular) * iten) * atten;
160 | }
--------------------------------------------------------------------------------
/Render_with_OpenGL/shader/text3D.vert:
--------------------------------------------------------------------------------
1 | #version 420 core
2 |
3 | layout(location = 0) in vec3 vPosition;
4 | layout(location = 1) in vec3 vNormal;
5 | layout(location = 2) in vec2 vTexCoord;
6 |
7 | out vec3 Normal;
8 | out vec3 FragPos;
9 | out vec2 TexCoord;
10 |
11 | uniform mat4 model;
12 | layout(std140, binding = 0) uniform Matrix
13 | {
14 | mat4 view;
15 | mat4 projection;
16 | };
17 |
18 | void main()
19 | {
20 | gl_Position = projection * view * model * vec4(vPosition, 1.0f);
21 | FragPos = vec3(model * vec4(vPosition, 1.0f));
22 | Normal = mat3(model) * vNormal;
23 | TexCoord = vTexCoord;
24 | }
--------------------------------------------------------------------------------
/Render_with_OpenGL/src/RenderText3D.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Xiangwk/Text3D_with_OpenGL/60c68ded3e380d13052ca6456f182c0d4c0293d1/Render_with_OpenGL/src/RenderText3D.cpp
--------------------------------------------------------------------------------
/Text3D/include/FreeTypeFont.h:
--------------------------------------------------------------------------------
1 | //use freetype2 to decompose the glyph's outline
2 | //a distinction should be made between the concept of outline and contour
3 | //outline is the glyph's whole outline and contours are the seperated lines in the outline
4 | #pragma once
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | //glm
11 | #include
12 |
13 | //FreeType
14 | #include
15 | #include FT_FREETYPE_H
16 | #include FT_OUTLINE_H
17 |
18 | #include "Glyph3D.h"
19 |
20 | namespace FreeType
21 | {
22 | //this class used to store the vertices and contour list of the original glyph
23 | //note that the outline is consist of many coutours so the contour list is an array of an unsigned array
24 | struct Char3DInfo
25 | {
26 | Vec3Array vertices; //store the postions of glyph vertex
27 | ElementArray current_indices; //store the indices of coutour
28 | std::vector contour_list; //contour's indices list
29 | glm::vec3 previous; //the previous vertex of current vertex(use to sample Bezier)
30 | unsigned int num_steps; //number of step to sample the Bezier
31 | float coord_scale; //freetype use the 1/64 pixel format means we shoule set this value to 1/64
32 |
33 | Char3DInfo() : num_steps(10), coord_scale(1.0 / 64.0) {}
34 |
35 | void completeCurrentContour()
36 | {
37 | if (!vertices.empty() && !current_indices.empty())
38 | contour_list.push_back(current_indices);
39 | current_indices.clear();
40 | }
41 | //return the data to Glyph3D to do triangulation
42 | Glyph3D get()
43 | {
44 | completeCurrentContour();
45 | return Glyph3D(vertices, contour_list);
46 | }
47 |
48 | void addVertex(glm::vec3 pos)
49 | {
50 | previous = pos;
51 | pos *= coord_scale;
52 | //no need to add the same vertex
53 | if (!vertices.empty() && vertices.back() == pos)
54 | return;
55 | //for the closed contour
56 | if (!current_indices.empty() && vertices[current_indices[0]] == pos)
57 | current_indices.push_back(current_indices[0]);
58 | else
59 | {
60 | current_indices.push_back(vertices.size());
61 | vertices.push_back(pos);
62 | }
63 | }
64 | //move to a new contour
65 | void moveTo(const glm::vec2 &pos)
66 | {
67 | completeCurrentContour();
68 | addVertex(glm::vec3(pos.x, pos.y, 0));
69 | }
70 | //draw line segment
71 | void lineTo(const glm::vec2 &pos)
72 | {
73 | addVertex(glm::vec3(pos.x, pos.y, 0));
74 | }
75 | //draw conic Bezier
76 | void conicTo(const glm::vec2 &control, const glm::vec2 &pos)
77 | {
78 | glm::vec3 p0 = previous;
79 | glm::vec3 p1 = glm::vec3(control.x, control.y, 0);
80 | glm::vec3 p2 = glm::vec3(pos.x, pos.y, 0);
81 |
82 | GLfloat dt = 1.0 / num_steps;
83 | GLfloat u = 0;
84 | for (unsigned int i = 0; i <= num_steps; ++i)
85 | {
86 | GLfloat w = 1;
87 | GLfloat bs = 1.0 / ((1 - u)*(1 - u) + 2 * (1 - u)*u*w + u*u);
88 | glm::vec3 p = (p0*((1 - u)*(1 - u)) + p1*(2 * (1 - u)*u*w) + p2*(u*u)) * bs;
89 | addVertex(p);
90 |
91 | u += dt;
92 | }
93 | }
94 | //draw cubic Bezier
95 | void cubicTo(const glm::vec2 &control1, const glm::vec2 &control2, const glm::vec2 &pos)
96 | {
97 | glm::vec3 p0 = previous;
98 | glm::vec3 p1 = glm::vec3(control1.x, control1.y, 0);
99 | glm::vec3 p2 = glm::vec3(control2.x, control2.y, 0);
100 | glm::vec3 p3 = glm::vec3(pos.x, pos.y, 0);
101 |
102 | GLfloat cx = 3 * (p1.x - p0.x);
103 | GLfloat bx = 3 * (p2.x - p1.x) - cx;
104 | GLfloat ax = p3.x - p0.x - cx - bx;
105 | GLfloat cy = 3 * (p1.y - p0.y);
106 | GLfloat by = 3 * (p2.y - p1.y) - cy;
107 | GLfloat ay = p3.y - p0.y - cy - by;
108 |
109 | GLfloat dt = 1.0 / num_steps;
110 | GLfloat u = 0;
111 | for (unsigned int i = 0; i <= num_steps; ++i)
112 | {
113 | glm::vec3 p = glm::vec3(ax*u*u*u + bx*u*u + cx*u + p0.x, ay*u*u*u + by*u*u + cy*u + p0.y, 0);
114 | addVertex(p);
115 |
116 | u += dt;
117 | }
118 | }
119 | }; //end of Char3DInfo
120 |
121 | //callback function use to decompose outline
122 | //return 0 means successful
123 | int moveTo(const FT_Vector* to, void* user)
124 | {
125 | Char3DInfo* char3d = (Char3DInfo*)user;
126 | char3d->moveTo(glm::vec2(to->x, to->y));
127 | return 0;
128 | }
129 | int lineTo(const FT_Vector* to, void* user)
130 | {
131 | Char3DInfo* char3d = (Char3DInfo*)user;
132 | char3d->lineTo(glm::vec2(to->x, to->y));
133 | return 0;
134 | }
135 | int conicTo(const FT_Vector* control, const FT_Vector* to, void* user)
136 | {
137 | Char3DInfo* char3d = (Char3DInfo*)user;
138 | char3d->conicTo(glm::vec2(control->x, control->y), glm::vec2(to->x, to->y));
139 | return 0;
140 | }
141 | int cubicTo(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* user)
142 | {
143 | Char3DInfo* char3d = (Char3DInfo*)user;
144 | char3d->cubicTo(
145 | glm::vec2(control1->x, control1->y),
146 | glm::vec2(control2->x, control2->y),
147 | glm::vec2(to->x, to->y));
148 | return 0;
149 | }
150 | } //close namespace FreeType
151 |
152 |
153 | class FreeTypeFont
154 | {
155 | public:
156 | FreeTypeFont(wchar_t code, GLchar *font_file);
157 | ~FreeTypeFont()
158 | {
159 | FT_Done_Face(face);
160 | FT_Done_FreeType(ft);
161 | }
162 | Glyph3D getGlyph3D();
163 | GLuint advanceX() const { return AdvanceX; }
164 |
165 | private:
166 | wchar_t charcode;
167 | FT_Library ft;
168 | FT_Face face;
169 | GLuint AdvanceX;
170 | FreeType::Char3DInfo char3d;
171 | };
172 |
173 | FreeTypeFont::FreeTypeFont(wchar_t code, char *font_file)
174 | {
175 | if (FT_Init_FreeType(&ft))
176 | std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;
177 | if (FT_New_Face(ft, font_file, 0, &face))
178 | std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;
179 | FT_Set_Pixel_Sizes(face, 0, 24);
180 |
181 | charcode = code;
182 | //load character face
183 | FT_Error error = FT_Load_Char(face, charcode, FT_LOAD_DEFAULT);
184 | if (error)
185 | std::cout << "FT_Load_Char(...) error 0x" << std::hex << error << std::dec << std::endl;
186 | if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
187 | std::cout << "FreeTypeFont3D::getGlyph : not a vector font" << std::endl;
188 |
189 | AdvanceX = (face->glyph->advance.x) >> 6;
190 | }
191 |
192 | Glyph3D FreeTypeFont::getGlyph3D()
193 | {
194 | FT_Outline outline = face->glyph->outline;
195 | FT_Outline_Funcs funcs;
196 | funcs.conic_to = (FT_Outline_ConicToFunc)&FreeType::conicTo;
197 | funcs.line_to = (FT_Outline_LineToFunc )&FreeType::lineTo;
198 | funcs.cubic_to = (FT_Outline_CubicToFunc)&FreeType::cubicTo;
199 | funcs.move_to = (FT_Outline_MoveToFunc )&FreeType::moveTo;
200 | funcs.shift = 0;
201 | funcs.delta = 0;
202 |
203 | //register the callback function
204 | FT_Error _error = FT_Outline_Decompose(&outline, &funcs, &char3d);
205 | if (_error)
206 | std::cout << "FreeTypeFont3D::getGlyph : - outline decompose failed ..." << std::endl;
207 |
208 | return char3d.get();
209 | }
--------------------------------------------------------------------------------
/Text3D/include/Glyph3D.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "TriangleIndexFunctor.h"
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | using ElementArray = std::vector;
12 | using Vec3Array = std::vector;
13 |
14 | class Glyph3D
15 | {
16 | public:
17 | Glyph3D() = default;
18 | Glyph3D(const Vec3Array& va, const std::vector& el) :
19 | _vertices(va), _elements(el) {}
20 |
21 | //the positions and contours's indices used to do triangulation
22 | Vec3Array* getVertexPos() { return &_vertices; }
23 | std::vector* getElementsArray() { return &_elements; }
24 |
25 | unsigned int getElementsNum() { return _elements.size(); }
26 | //remove from index b, remove numToRemove's contours
27 | bool removeElements(unsigned int b, unsigned int numToRemove);
28 |
29 | void addNormalArray(const Vec3Array &normals)
30 | {
31 | _normals.push_back(normals);
32 | }
33 |
34 | //add indices
35 | void addPrimitiveSet(ElementArray* element) { _elements.push_back(*element); }
36 |
37 | //add primitive mode
38 | void addMode(GLenum mode) { _modeList.push_back(mode); }
39 | void clearModeList() { _modeList.clear(); }
40 |
41 | //generate the GL_TRIANGLES indices
42 | void accept(PrimitiveIndexFunctor& functor) const;
43 |
44 | //the final data that transmited to opengl
45 | //three face's date(front face, wall face and back face) merge to one mesh
46 | Vec3Array getNormalArray() const;
47 | ElementArray getIndices() const;
48 |
49 | //output final data to txt file
50 | void output();
51 |
52 | public:
53 | Vec3Array _vertices; //positions
54 | std::vector _normals;
55 | std::vector _elements;
56 |
57 | //because the gluTess not only generate GL_TRIANGLES mode but also GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP;
58 | //but we will unify them to GL_TRIANGLES, so we use this vector to store the original data that from gluTess
59 | //and process them by our PrimitiveIndexFuntor
60 | std::vector _modeList;
61 | };
62 |
63 |
64 | struct CollectTriangleIndicesFunctor
65 | {
66 | CollectTriangleIndicesFunctor() = default;
67 |
68 | using Indices = std::vector;
69 | Indices _indices;
70 |
71 | void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
72 | {
73 | if (p1 == p2 || p2 == p3 || p1 == p3)
74 | {
75 | return;
76 | }
77 |
78 | _indices.push_back(p1);
79 | _indices.push_back(p3);
80 | _indices.push_back(p2);
81 | }
82 | };
--------------------------------------------------------------------------------
/Text3D/include/Tessellator.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include