调试:
视觉错误与CPU调试不同,在GLSL代码中也不能设置断点,出现错误的时候寻找错误的源头可能会非常困难。
glGetError()
GLenum glGetError();返回整形数字,查询错误标记,但是当一个错误标记被返回的时候,将不会报告其它的错误标记。
因此根据这个特性,代码中通常使用大量的glGetError()函数(间隔尽量小),以便确定错误发生的的大致位置
为了显示更多的信息,可以写一个错误检测宏,它返回错误类型,文件名,和行数
如果你使用GLEW库,调用glewInit()会设置一个GL_INVALID_ENUM的错误标记,所以首先需要glewInit之后立即调用glGetError消除这个标记
调试输出
更为完善的错误或警告信息给用户,只有4.3以上的openGL版本支持
在调用glfwCreateWindow之前提醒到GLFW中:调试输出启用glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
检查我们是否成功地初始化了调试上下文,我们可以对OpenGL进行查询。GL_CONTEXT_FLAG_DEBUG_BIT
调试输出回调函数,在这里我们将输出一些有用的错误数据到控制台中。
void APIENTRY glDebugOutput(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar *message, void *userParam);
可以通过glDebugMessageControl()过滤出你需要的错误类型
调式输出可以很容易的找出错误发生的准确行号或者调用,通过在glDebugOutput()中(特定的错误类型上)设置一个断点,查找调用栈(vs的Call Stack)直到找到消息发出的源头。
我们也可以使用glDebugMessageInsert将自定义信息推送到调试输出系统:
着色器调试
因为shader无法断点或者输出日志,所以只能用颜色来调试
通常将所有相关的变量直接发送到片段着色器的输出通道,以评估它们
比如法线可视化
参考编译器
不同的显卡商(AMD,NVidia,以及Intel)的GLSL间有不同的细微差别,如果你想要保证你的着色器代码在所有的机器上都能运行,可以直接使用官方的参考编译器(语言校验器),你可以很方便的检查你的着色器代码规范
GLSL语言校验器是通过下列固定的后缀名来决定着色器的类型的
- .vert:顶点着色器(Vertex Shader)
- .frag:片段着色器(Fragment Shader)
- .geom:几何着色器(Geometry Shader)
- .tesc:细分控制着色器(Tessellation Control Shader)
- .tese:细分计算着色器(Tessellation Evaluation Shader)
- .comp:计算着色器(Compute Shader)
帧缓冲输出
在OpenGL程序中一块特定区域显示帧缓冲的内容
也属于shader调试,但是这种可以看到多个效果,主场景,和调试的帧缓冲
外部调试器
RenderDoc
文本渲染
OpenGL这样的底层API没有包含文本处理的功能
可能的做法:通过GL_LINES来绘制字形,创建文本的3D网格(Mesh),或在3D环境中将字符纹理渲染到2D四边形上。
原始渲染方式
/ | 位图(Bitmap) | 矢量图(Vector Graphics) |
---|---|---|
定义 | 由像素组成的图像,每个像素存储颜色信息 | 由几何图形(点、线、曲线)组成,使用数学公式描述图像 |
缩放效果 | 放大会失真,出现锯齿或模糊,分辨率依赖 | 可无损缩放,边缘始终光滑,分辨率无关 |
文件大小 | 文件较大,尤其是高分辨率图像 | 文件较小,复杂图形也比同等尺寸位图小 |
适用场景 | 适合照片、数码绘画、复杂细节和渐变色的图像 | 适合图标、标志、插图和排版设计,简单且可缩放的图像 |
常见格式 | .bmp , .png , .jpg , .gif , .tiff | .svg , .eps , .pdf , .ai |
颜色表现 | 支持丰富的颜色和渐变,适合细节精细的图像 | 通常使用填充和边框定义颜色,渐变较为有限 |
编辑方式 | 像素级编辑,修改每个像素,适合精细图像编辑 | 形状编辑,调整几何属性,适合图形设计 |
渲染方式 | 直接将像素绘制到屏幕,处理高分辨率时资源占用高 | 由几何公式计算形状,渲染时较轻量 |
文件依赖分辨率 | 分辨率依赖,图像缩放会导致质量变化 | 分辨率无关,可无损缩放,不影响质量 |
适用软件 | Photoshop, GIMP | Adobe Illustrator, CorelDRAW, Inkscape |
位图字体(Bitmap Font):在纹理的预定义区域中包含了我们想要使用的所有字形(Glyph)。每个字形都关联着一个特定的纹理坐标区域。
渲染方式:从特定位图纹理区域采样对应的字形,并渲染它们到多个2D四边形上,需要启用混合,让背景保持透明
优点:很容易实现
缺点:会被限制在一个固定的分辨率,通常会局限于非常小的字符集,当你想要使用不同的字体时,你需要重新编译一套全新的位图字体
现代文本FreeType库
FreeType跨平台字体库:
是一个能够用于加载字体(例如TrueType字体),并为每一个字形生成位图以及计算unsigned char度量值(Metric),我们可以提取出它生成的位图作为字形纹理,并使用这些度量值指定每个字符的大小和位置。
TrueType字体:它是通过数学公式(曲线的组合)来定义的。类似矢量图,轻易渲染不同大小的字形而不造成任何质量损失。
首先包含官方预编译好的lib库,并配置到项目中GitHub - ubawurinna/freetype-windows-binaries: Windows binaries of FreeType
字体定义了字符的外观,字形是单个字符的具体形状
//
API:
FT_Init_FreeType初始化FreeType库,
FT_New_Face指定一个ttf格式的字体文件,加载为FT_Face,
FT_Set_Pixel_Sizes定义字体大小(FT_Face ,宽度,高度)
FT_Load_Char,加载某个字符:面中包含了一个字形的集合,将其中一个字形设置为激活字形,我们告诉FreeType去创建一个8位的灰度位图(大小恰好能包含这个字符可见区域),我们可以通过face->glyph->bitmap
来访问这个位图。
每一个字形都放在一个水平的基准线(Baseline)上
FT_Done_Face,FT_Done_FreeType释放资源
//
字体:
直接查询:在需要渲染字符的时候,为了直接通过它的度量值,获取纹理,预处理字符的数据,保存到字符集map中,
对于每个字符,值存储,纹理的ID,字形大小,基准线到字形左部/顶部的偏移值,原点距下一个字形原点的距离
循环ASCII字符集的前128个字符,FT_Load_Char加载每个字符,将数据存储到字符集map中
因为通过字形生成的位图是一个8位灰度图,我们这里将纹理的internalFormat(
如何存储纹理数据)和format(
纹理数据的格式)设置为GL_RED,
只使用纹理中的红色通道,没有rgb,也就无法表示颜色(比如红色),适合灰度图像(只有亮度信息,没有颜色信息),
RGB 位深度:每个颜色通道(红、绿、蓝)占用 8 位,共 24 位
//
Shader:指定顶点位置,纹理坐标,字形位图(控制alpha,需要启用混合),字体颜色
正交投影矩阵:不需要透视,将范围指定位窗口范围
VAO,VBO
为了方便渲染文本,创建一个RenderText函数