本文共 1822 字,大约阅读时间需要 6 分钟。
近期,我尝试将经典的ASCII Art滤镜从CPU版本迁移到OpenGL版本。该滤镜旨在将输入图像转换为基于ASCII字符的艺术效果。现将本次开发过程总结如下:
基于之前的CPU版本实现,滤镜的效率相对较低,尤其在处理视频时会产生明显卡顿。为提高效率,将滤镜重构为基于OpenGL的实现。
本次开发与CPU版本的滤镜流程大致相同,但在实现细节上进行了优化:
字符尺寸确定
首先需要根据选用的字体确定单元网格的尺寸。例如,设置常用宽度和高度,确保字符不会过大或过小导致效果不佳。字符预处理
CPU版本中会绘制每个可用字符到空白图像上并计算其平均灰度值。本次流程相同,但将其作为预处理阶段进行优化。灰度值映射
根据每个字符的平均灰度值建立映射表,值域在0-255之间。输入图像的每个像素会被灰度化,并根据映射表获取对应字符。纹理处理
在OpenGL中,通过设置顶点着色器和处理片段程序(Fragment Program),将输入图像纹理划分为单元网格。每个网格根据中央灰度值获取对应字符,并在特定位置进行选择和显示。性能优化
通过调用texture
函数启用高效纹理访问,同时利用浮点运算处理像素坐标,为每个像素找出准确字符位置,明显提升了处理速度。 纹理分辨率调整
根据当前屏幕分辨率调整网格大小,确保字符清晰可见。整体流程保持与CPU版本一致,但图形处理更高效。片段着色器代码实现如下:
#version 450 coreout vec4 fragColor;in vec2 texCoord;uniform float charWidth;uniform float charHeight;uniform sampler2D imageTexture;uniform sampler2D asciiTexture;void main() { // 网格尺寸设置 float gridWidth = charWidth; float gridHeight = charHeight; // 确定当前像素的网格位置 int xGrid = int(texCoord.x / gridWidth); int yGrid = int(texCoord.y / gridHeight); // 计算在网格内的绝对位置 float xDelta = texCoord.x - xGrid * gridWidth; float yDelta = texCoord.y - yGrid * gridHeight; // 获取当前像素的原始颜色 vec4 texColor = texture(imageTexture, vec2(xGrid * gridWidth, yGrid * gridHeight)); // 灰度值计算 (RGB到灰度) float gray = (texColor.x * 0.299) + (texColor.y * 0.587) + (texColor.z * 0.114); // 根据灰度值获取字符索引 int index = int(gray / (1.0 / 255.0)); // 在字符纹理中定位字符并获取颜色 float charX = 1.0 / 256.0 * (index + (xDelta / gridWidth)); float charY = yDelta / gridHeight; fragColor = texture(asciiTexture, vec2(charX, charY));}
相当于基于OpenGL的实现表现出色。在窗口缩放或调整大小时,几乎不引起延迟,连续重绘具有椭圆曲线形状。
该实现在终端显示方面存在局限性。由于OpenGL依赖图形显存,无法将生成的字符艺术发送回终端显示域。因此,这一实现在特定场景下仍需改进。
总体来看,基于OpenGL的滤镜实现在性能和流程上均优于CPU版本。尽管存在少量局限,但整体效果令人满意。期待对相关技术进行深入研究,不断提升滤镜表现。
转载地址:http://rmpwk.baihongyu.com/