Dandelion 1.1.1
A light-weight 3D builder for educational usage
载入中...
搜索中...
未找到
gl.hpp
浏览该文件的文档.
1#ifndef DANDELION_PLATFORM_GL_HPP
2#define DANDELION_PLATFORM_GL_HPP
3
4#include <cstddef>
5#include <type_traits>
6#include <vector>
7#include <string>
8#include <array>
9
10#include <Eigen/Core>
11#ifdef _WIN32
12#include <Windows.h>
13#endif
14#include <glad/glad.h>
15
16#include "shader.hpp"
18
19/*!
20 * \file platform/gl.hpp
21 */
22
23/*!
24 * \ingroup platform
25 */
26namespace GL {
27
28/* --------------------------------------------------------
29 * The definition region.
30 * --------------------------------------------------------
31 */
32
33/*!
34 * \ingroup platform
35 * \~chinese
36 * \brief 获取某个基本数据类型对应的枚举值(用于传递给某些 OpenGL API)。
37 * \tparam DataType 指定的基本数据类型
38 * \returns 如果是基本数据类型,则返回对应的枚举值;如果不是,则返回 GL_NONE。
39 */
40template<typename DataType>
41constexpr GLenum get_GL_type_enum();
42
43/*!
44 * \ingroup platform
45 * \~chinese
46 * \brief 对 OpenGL 顶点数组对象 (Vertex Array Object) 的封装。
47 *
48 * 这个结构体只对 VAO 做了最基本的封装,稍微简化了手动构造和使用 VAO
49 * 的过程。由于此结构体的实例将持有 OpenGL VAO 的名字 (name) 或者叫描述符,它不允许被复制构造。
50 */
52{
53 /*! \~chinese
54 * 构造函数将调用 glGenVertexArrays 创建一个 OpenGL VAO,其名字存储于 `descriptor` 属性中。
55 */
57 VertexArrayObject(VertexArrayObject& other) = delete;
58 VertexArrayObject& operator=(VertexArrayObject& other) = delete;
59 /*! \~chinese
60 * 移动构造函数是为满足 MoveInsertable 条件而写的,保证持有 VAO 的其他对象能使用 `std::vector`
61 * 之类的容器存储。此构造函数会将 `other` 的 `descriptor` 设置成 0,从而避免 `other`
62 * 析构时将真正的 OpenGL VAO 删除掉。
63 */
65 /*! \~chinese 调用 glDeleteVertexArrays 删除 VAO。 */
67 /*! \~chinese 绑定 VAO,仅用于更新它持有的 buffer 数据或格式时才需要专门调用。 */
68 void bind();
69 /*! \~chinese 解绑 VAO。 */
70 void release();
71 /*! \~chinese 绘制这个 VAO 记录的所有内容,无需专门绑定和解绑。 */
72 void draw(GLenum mode, int first, std::size_t count);
73
74 unsigned int descriptor;
75};
76
77/*!
78 * \ingroup platform
79 * \~chinese
80 * \brief 对 OpenGL 数组缓冲 (Array Buffer) 的封装。
81 *
82 * Array Buffer 通常用于创建顶点缓冲对象 (Vertex Buffer Object, VBO),用于存储顶点属性。
83 * 与 VertexArrayObject 对象不同的是,ArrayBuffer 对象持有数据。如果希望更新一个
84 * ArrayBuffer 中的数据,首先应当直接修改它持有的 `std::vector<T>`,然后调用 `to_gpu`
85 * 复制到显存。
86 * \tparam T 此缓冲区中存放的数据类型,应当指定为基本数据类型,否则没有实际意义。
87 * \tparam size 每个顶点的数据个数,例如 size 是 3 表示缓冲区中每三个数据是一组,
88 * 这一组数据属于同一个顶点。
89 */
90template<typename T, std::size_t size>
92{
93 /*! \~chinese
94 * 调用 glGenBuffers 创建 Array Buffer,并设置此缓冲区绘制时的 hint 信息。
95 * \param buffer_usage 绘制 hint 信息,参考 `usage` 属性。
96 */
97 ArrayBuffer(GLenum buffer_usage, unsigned int layout_location);
98 ArrayBuffer(const ArrayBuffer& other) = delete;
99 ArrayBuffer& operator=(ArrayBuffer& other) = delete;
100 /*! \~chinese
101 * 为满足 MoveInsertable 编写的移动构造函数,参考 `VertexArrayObject` 的移动构造函数。
102 */
104 /*! \~chinese 调用 glDeleteBuffers 删除 Array Buffer。 */
106 /*! \~chinese
107 * 将 `size` 个数据附加到现有数据的末尾。
108 */
109 template<typename... Ts>
110 void append(Ts... values);
111 /*!
112 * \~chinese
113 * \brief 更新指定位置的 `size` 个数据。
114 *
115 * \param index 要更新的顶点索引
116 * \param value 新的值
117 */
118 void update(size_t index, const Eigen::Vector3f& value);
119 /*! \~chinese
120 * 统计这个 `ArrayBuffer` 中有多少个顶点的数据,也就是数据个数除以 `size`。
121 */
122 std::size_t count() const;
123 /*! \~chinese 绑定 ArrayBuffer。 */
124 void bind();
125 /*! \~chinese 解绑 ArrayBuffer。 */
126 void release();
127 /*! \~chinese 指定数据格式并使该 location 位置的属性生效。 */
129 /*! \~chinese 使该 location 位置的属性无效。 */
130 void disable();
131 /*! \~chinese 将数据传送到 GPU,调用前无需绑定。 */
132 void to_gpu();
133
134 unsigned int descriptor;
135 /*! \~chinese
136 * 绘制时的 hint 信息,可以是 `GL_STATIC_DRAW / GL_DYNAMIC_DRAW / GL_STREAM_DRAW`
137 * 其中之一。根据 OpenGL 标准,这只是一个提示信息,不具有任何强制性。
138 */
139 unsigned int usage;
140 /*! \~chinese
141 * 这个 ArrayBuffer 存储的属性在 vertex shader 中对应的位置。
142 */
143 unsigned int layout_location;
144 std::vector<T> data;
145};
146
147/*!
148 * \ingroup platform
149 * \~chinese
150 * \brief 对 OpenGL 索引数组缓冲 (Element Array Buffer) 的封装。
151 *
152 * Element Array Buffer 通常用于创建索引缓冲对象 (Element Buffer Object, EBO),用于存储顶点索引。
153 * EBO 通常保存边或者面对应的顶点索引,从而避免直接存储数据。
154 * `ElementArrayBuffer` 对象持有数据。如果希望更新一个 `ElementArrayBuffer`
155 * 中的数据,首先应当直接修改它的 `data` 成员,然后调用 `to_gpu` 复制到显存。
156 * \tparam size 每个基元(边或者面)对应的顶点索引个数,例如三角形面的 `size` 是 3。
157 */
158template<std::size_t size>
159struct ElementArrayBuffer
160{
161 ElementArrayBuffer(unsigned int buffer_usage);
162 ElementArrayBuffer(const ElementArrayBuffer& other) = delete;
163 ElementArrayBuffer& operator=(ElementArrayBuffer& other) = delete;
164 /*! \~chinese 参考 `VertexArrayObject` 的移动构造函数。 */
165 ElementArrayBuffer(ElementArrayBuffer&& other);
166 ~ElementArrayBuffer();
167 /*! \~chinese 将 `size` 个数据附加到末尾。 */
168 template<typename... Ts>
169 void append(Ts... values);
170 /*! \~chinese 统计总共有多少个 **基元** (而不是顶点)。 */
171 std::size_t count() const;
172 void bind();
173 void release();
174 void to_gpu();
175
176 unsigned int descriptor;
177 unsigned int usage;
178 /*! \~chinese 使用 `unsigned int` 而非 `size_t` 的原因是 OpenGL 不接受 `size_t`。 */
179 std::vector<unsigned int> data;
180};
181
182/*!
183 * \ingroup platform
184 * \ingroup rendering
185 * \~chinese
186 * \brief 物体材质。
187 */
188struct Material
189{
190 Material(const Eigen::Vector3f& K_ambient = Eigen::Vector3f(1.0f, 1.0f, 1.0f),
191 const Eigen::Vector3f& K_diffuse = Eigen::Vector3f(0.5f, 0.5f, 0.5f),
192 const Eigen::Vector3f& K_specular = Eigen::Vector3f(0.0f, 0.0f, 0.0f),
193 float shininess = 5.0f);
194 /*! \~chinese 环境光反射系数(颜色)。 */
195 Eigen::Vector3f ambient;
196 /*! \~chinese 漫反射光反射系数(颜色)。 */
197 Eigen::Vector3f diffuse;
198 /*! \~chinese 镜面反射光反射系数(颜色)。 */
199 Eigen::Vector3f specular;
200 /*! \~chinese Phong 模型计算镜面反射时的指数 */
202};
203
204/*!
205 * \~chinese
206 * \brief 用于场景预览渲染的 Mesh 类。
207 *
208 * 这个类为了便于和 OpenGL 交互,不会使用 Eigen
209 * 中的各种向量存储顶点坐标、法线和颜色等信息,而是直接持有 VAO、VBO 和 EBO。
210 *
211 * 由于 OpenGL API 只支持绘制三角形,GL::Mesh 存储的面片 (face) 只能是三角形。
212 * 四边形乃至任意多边形面片需要先三角化成三角形才能被渲染。
213 *
214 * 外界读取 Mesh 中的顶点、边等基元时应当调用 `vertex/normal/edge/face` 方法,
215 * 而不是直接访问 `vertices.data` 等内部存储。这些 ArrayBuffer 或 ElementArrayBuffer
216 * 之所以被设为公有成员,是因为在修改数据或进行渲染时需要操作它们,
217 * 其他情况下都不必也不应该使用这些扁平存储的数据。
218 */
219struct Mesh
220{
221 Mesh();
222 /*! \~chinese 由于 VAO 和 ArrayBuffer 不允许复制构造,Mesh 也不允许复制构造。 */
223 Mesh(const Mesh& other) = delete;
224 Mesh(Mesh&& other);
225 /*! \~chinese 读取编号为 index 的顶点。 */
226 Eigen::Vector3f vertex(size_t index) const;
227 /*! \~chinese 读取编号为 index 的顶点法线。 */
228 Eigen::Vector3f normal(size_t index) const;
229 /*! \~chinese 读取编号为 index 的边。 */
230 std::array<size_t, 2> edge(size_t index) const;
231 /*! \~chinese 读取编号为 index 的面片。 */
232 std::array<size_t, 3> face(size_t index) const;
233 void clear();
234 void to_gpu();
235 /*!
236 * \~chinese
237 * \brief 渲染这个 mesh。
238 *
239 * \param element_flags 指定渲染哪些元素的二进制串,可以是 `vertices_flag` / `edges_flag` /
240 * `faces_flag` 中的任意一个或多个
241 * \param face_shading 面片是否根据光照和材质进行着色,若否,则统一使用全局颜色。
242 */
243 void render(const Shader& shader, unsigned int element_flags, bool face_shading = true,
244 const Eigen::Vector3f& global_color = default_wireframe_color);
245
246 constexpr static unsigned int vertices_flag = 1u;
247 constexpr static unsigned int edges_flag = 1u << 1u;
248 constexpr static unsigned int faces_flag = 1u << 2u;
249 const static Eigen::Vector3f default_wireframe_color;
250 const static Eigen::Vector3f default_face_color;
251 const static Eigen::Vector3f highlight_wireframe_color;
252 const static Eigen::Vector3f highlight_face_color;
254 ArrayBuffer<float, 3> vertices;
255 ArrayBuffer<float, 3> normals;
258 /*! \~chinese 每个 Mesh 只能有一个材质 */
260};
261
262/*!
263 * \~chinese
264 * \brief 在预览场景时绘制若干线条。
265 *
266 * 这个类与 `GL::Mesh` 相似但更简单,只有顶点 VBO 和线条 EBO,可用于绘制射线、
267 * 半边等线条元素。
268 */
269struct LineSet
270{
271 LineSet(const std::string& name, Eigen::Vector3f color = GL::Mesh::default_wireframe_color);
272 /*! \~chinese 由于 VAO 和 ArrayBuffer 不允许复制构造,LineSet 也不允许复制构造。 */
273 LineSet(const LineSet& other) = delete;
274 LineSet(LineSet&& other);
275 /*! \~chinese 加入一条从 a 到 b 的线段。 */
276 void add_line_segment(const Eigen::Vector3f& a, const Eigen::Vector3f& b);
277 /*! \~chinese 加入一个从 from 到 to 的箭头。 */
278 void add_arrow(const Eigen::Vector3f& from, const Eigen::Vector3f& to);
279 /*! \~chinese 更新索引为 `index` 的箭头,仅当该 `LineSet` 内全部是箭头时才是安全的。 */
280 void update_arrow(size_t index, const Eigen::Vector3f& from, const Eigen::Vector3f& to);
281 /*! \~chinese 加入一个轴对齐包围盒 (Axis-Aligned Bouding Box, AABB) 。 */
282 void add_AABB(const Eigen::Vector3f& p_min, const Eigen::Vector3f& p_max);
283 /*! \~chinese 清空所有元素,但只影响内存,不会同步到显存。 */
284 void clear();
285 /*! \~chinese 将修改同步到显存。 */
286 void to_gpu();
287 /*!
288 * \~chinese
289 * \brief 渲染该线条集。
290 *
291 * 这个函数只会设置对应全局颜色的 uniform 变量,其他所有变量都需要由调用者自行设置。
292 */
293 void render(const Shader& shader);
294
295 /*! \~chinese 绘制的线条颜色。 */
296 Eigen::Vector3f line_color;
298 ArrayBuffer<float, 3> vertices;
300 std::string name;
301};
302
303/* ---------------------------------------------------------
304 * The implementation region for template class and functions.
305 * ---------------------------------------------------------
306 */
307
308template<typename DataType>
309constexpr GLenum get_GL_type_enum()
310{
311 if constexpr (std::is_same_v<DataType, char>) {
312 return GL_BYTE;
313 } else if constexpr (std::is_same_v<DataType, unsigned char>) {
314 return GL_UNSIGNED_BYTE;
315 } else if constexpr (std::is_same_v<DataType, int>) {
316 return GL_INT;
317 } else if constexpr (std::is_same_v<DataType, unsigned int>) {
318 return GL_UNSIGNED_INT;
319 } else if constexpr (std::is_same_v<DataType, float>) {
320 return GL_FLOAT;
321 } else if constexpr (std::is_same_v<DataType, double>) {
322 return GL_DOUBLE;
323 } else {
324 return GL_NONE;
325 }
326}
327
328// ArrayBuffer ---------------------------------------------
329
330template<typename T, std::size_t size>
331ArrayBuffer<T, size>::ArrayBuffer(GLenum buffer_usage, unsigned int layout_location)
332 : usage(buffer_usage), layout_location(layout_location)
333{
334 glGenBuffers(1, &(this->descriptor));
335}
336
337template<typename T, std::size_t size>
339 : descriptor(other.descriptor), usage(other.usage), layout_location(other.layout_location),
340 data(std::move(other.data))
341{
342 other.descriptor = 0;
343}
344
345template<typename T, std::size_t size>
347{
348 if (this->descriptor != 0) {
349 glDeleteBuffers(1, &(this->descriptor));
350 }
351}
352
353template<typename T, std::size_t size>
354template<typename... Ts>
356{
357 static_assert((std::is_same_v<decltype(values), T> && ...),
358 "ArrayBuffer: all values to be appended must have the same type as T");
359 static_assert(sizeof...(values) == size,
360 "ArrayBuffer: number of values to be appended must be same as size per vertex");
361 (this->data.push_back(values), ...);
362}
363
364template<typename T, std::size_t size>
365void ArrayBuffer<T, size>::update(size_t index, const Eigen::Vector3f& value)
366{
367 const GLintptr offset = index * 3 * sizeof(float);
368 data[index * 3] = value.x();
369 data[index * 3 + 1] = value.y();
370 data[index * 3 + 2] = value.z();
371 bind();
372 glBufferSubData(GL_ARRAY_BUFFER, offset, 3 * sizeof(float), value.data());
373}
374
375template<typename T, std::size_t size>
377{
378 return this->data.size() / size;
379}
380
381template<typename T, std::size_t size>
383{
384 glBindBuffer(GL_ARRAY_BUFFER, this->descriptor);
385}
386
387template<typename T, std::size_t size>
389{
390 glBindBuffer(GL_ARRAY_BUFFER, 0);
391}
392
393template<typename T, std::size_t size>
395{
396 GLenum data_type = get_GL_type_enum<T>();
397 glVertexAttribPointer(this->layout_location, size, data_type, GL_FALSE, size * sizeof(T),
398 (void*)0);
399 glEnableVertexAttribArray(this->layout_location);
400}
401
402template<typename T, std::size_t size>
404{
405 glDisableVertexAttribArray(this->layout_location);
406}
407
408template<typename T, std::size_t size>
410{
411 this->bind();
412 glBufferData(GL_ARRAY_BUFFER, sizeof(T) * this->data.size(), this->data.data(), this->usage);
414}
415
416// ElementArrayBuffer --------------------------------------
417
418template<std::size_t size>
419ElementArrayBuffer<size>::ElementArrayBuffer(unsigned int buffer_usage) : usage(buffer_usage)
420{
421 glGenBuffers(1, &(this->descriptor));
422}
423
424template<std::size_t size>
425ElementArrayBuffer<size>::ElementArrayBuffer(ElementArrayBuffer&& other)
426 : descriptor(other.descriptor), usage(other.usage), data(std::move(other.data))
427{
428 other.descriptor = 0;
429}
430
431template<std::size_t size>
432ElementArrayBuffer<size>::~ElementArrayBuffer()
433{
434 if (this->descriptor != 0) {
435 glDeleteBuffers(1, &(this->descriptor));
436 }
437}
438
439template<std::size_t size>
441{
442 return this->data.size() / size;
443}
444
445template<std::size_t size>
446template<typename... Ts>
448{
449 static_assert((std::is_same_v<decltype(values), unsigned int> && ...),
450 "ElementArrayBuffer: all values to be appended must be unsigned int");
451 static_assert(
452 sizeof...(values) == size,
453 "ElementArrayBuffer: number of values to be appended must be as same as size per vertex");
454 (this->data.push_back(values), ...);
455}
456
457template<std::size_t size>
458void ElementArrayBuffer<size>::bind()
459{
460 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->descriptor);
461}
462
463template<std::size_t size>
464void ElementArrayBuffer<size>::release()
465{
466 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
467}
468
469template<std::size_t size>
470void ElementArrayBuffer<size>::to_gpu()
471{
472 this->bind();
473 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * this->data.size(),
474 this->data.data(), this->usage);
475}
476
477} // namespace GL
478
479#endif // DANDELION_PLATFORM_GL_HPP
对 GLSL Shader 的简单封装。
定义 shader.hpp:24
constexpr GLenum get_GL_type_enum()
获取某个基本数据类型对应的枚举值(用于传递给某些 OpenGL API)。
定义 gl.hpp:309
这个文件定义了一些和渲染(离线渲染或场景预览)相关的常量、枚举等。
对 OpenGL 数组缓冲 (Array Buffer) 的封装。
定义 gl.hpp:92
ArrayBuffer(GLenum buffer_usage, unsigned int layout_location)
定义 gl.hpp:331
void update(size_t index, const Eigen::Vector3f &value)
更新指定位置的 size 个数据。
定义 gl.hpp:365
void specify_vertex_attribute()
定义 gl.hpp:394
unsigned int layout_location
定义 gl.hpp:143
void append(Ts... values)
定义 gl.hpp:355
void release()
定义 gl.hpp:388
~ArrayBuffer()
定义 gl.hpp:346
unsigned int usage
定义 gl.hpp:139
void disable()
定义 gl.hpp:403
std::size_t count() const
定义 gl.hpp:376
void bind()
定义 gl.hpp:382
void to_gpu()
定义 gl.hpp:409
ArrayBuffer(ArrayBuffer &&other)
定义 gl.hpp:338
对 OpenGL 索引数组缓冲 (Element Array Buffer) 的封装。
定义 gl.hpp:160
std::size_t count() const
定义 gl.hpp:440
ElementArrayBuffer(ElementArrayBuffer &&other)
定义 gl.hpp:425
void append(Ts... values)
定义 gl.hpp:447
std::vector< unsigned int > data
定义 gl.hpp:179
void to_gpu()
定义 gl.cpp:281
void add_line_segment(const Eigen::Vector3f &a, const Eigen::Vector3f &b)
定义 gl.cpp:202
void add_AABB(const Eigen::Vector3f &p_min, const Eigen::Vector3f &p_max)
定义 gl.cpp:245
void render(const Shader &shader)
渲染该线条集。
定义 gl.cpp:289
Eigen::Vector3f line_color
定义 gl.hpp:296
void update_arrow(size_t index, const Eigen::Vector3f &from, const Eigen::Vector3f &to)
定义 gl.cpp:232
void add_arrow(const Eigen::Vector3f &from, const Eigen::Vector3f &to)
定义 gl.cpp:217
void clear()
定义 gl.cpp:275
LineSet(const LineSet &other)=delete
物体材质。
定义 gl.hpp:189
Eigen::Vector3f diffuse
定义 gl.hpp:197
float shininess
定义 gl.hpp:201
Eigen::Vector3f specular
定义 gl.hpp:199
Eigen::Vector3f ambient
定义 gl.hpp:195
std::array< size_t, 3 > face(size_t index) const
定义 gl.cpp:112
Material material
定义 gl.hpp:259
void render(const Shader &shader, unsigned int element_flags, bool face_shading=true, const Eigen::Vector3f &global_color=default_wireframe_color)
渲染这个 mesh。
定义 gl.cpp:137
Mesh(const Mesh &other)=delete
Eigen::Vector3f normal(size_t index) const
定义 gl.cpp:102
Eigen::Vector3f vertex(size_t index) const
定义 gl.cpp:97
std::array< size_t, 2 > edge(size_t index) const
定义 gl.cpp:107
对 OpenGL 顶点数组对象 (Vertex Array Object) 的封装。
定义 gl.hpp:52
void bind()
定义 gl.cpp:34
~VertexArrayObject()
定义 gl.cpp:27
VertexArrayObject()
定义 gl.cpp:17
void release()
定义 gl.cpp:39
void draw(GLenum mode, int first, std::size_t count)
定义 gl.cpp:44