Abstract
Keywords OpenGL  技术笔记  OpenGL 
Citation Yao Qing-sheng.OpenGL 超级宝典学习笔记操作像素.FUTURE & CIVILIZATION Natural/Social Philosophy & Infomation Sciences,20220628. https://yaoqs.github.io/20220628/opengl-chao-ji-bao-dian-xue-xi-bi-ji-cao-zuo-xiang-su/

OpenGL 支持放大,缩小,旋转图像。下面将举例介绍这些像素的操作。下面的例子是从 tga 文件中读取图片并显示,而且可以通过右键菜单来选择图像的显示模式和保存图片的快照到磁盘命名为 screenshot.tga。完整的代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "gltools.h"
#include <math.h>
static GLbyte *pImage = NULL;
static GLint iRenderMode = 0;
static GLint iWidth, iHeight, iComponents; static GLenum eFormat;
void ProcessMenu(int value)
{
if (value == 1)
{
//保存图像的快照 gltWriteTGA("screenshot.tga");
} else {
iRenderMode = value;
}

glutPostRedisplay();

}
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (!pImage)
{ return;
} //设置光栅位置 glRasterPos2i(0, 0); //修改的图像,和用于反转的映射数组
GLbyte *pModifyImage = NULL;
GLfloat invertMap[256]; switch (iRenderMode)
{ case 2: //图像倒转 glPixelZoom(-1.0f, -1.0f); //倒转后的图像的,渲染方向也倒转过来了,变成了从右上角开始往左下角渲染,所以设置倒转后的光栅位置为图像的宽高。 glRasterPos2i(iWidth, iHeight); break; case 3: //让图像填充满屏幕 GLint viewport[4]; //取得视口的大小 glGetIntegerv(GL_VIEWPORT, viewport); //按比例缩放 glPixelZoom((GLfloat)viewport[2]/iWidth, (GLfloat)viewport[3]/iHeight); break; case 4: //只保留红色 glPixelTransferf(GL_RED_SCALE, 1.0f);
glPixelTransferf(GL_GREEN_SCALE, 0.0f);
glPixelTransferf(GL_BLUE_SCALE, 0.0f); break; case 5: //只保留绿色 glPixelTransferf(GL_RED_SCALE, 0.0f);
glPixelTransferf(GL_GREEN_SCALE, 1.0f);
glPixelTransferf(GL_BLUE_SCALE, 0.0f); break; case 6: //只保留蓝色 glPixelTransferf(GL_RED_SCALE, 0.0f);
glPixelTransferf(GL_GREEN_SCALE, 0.0f);
glPixelTransferf(GL_BLUE_SCALE, 1.0f); break; case 7: //先渲染图像到颜色缓冲区中 glDrawPixels(iWidth, iHeight, eFormat, GL_UNSIGNED_BYTE, pImage); //NTSC标准,转成黑白图像 glPixelTransferf(GL_RED_SCALE, 0.3f);
glPixelTransferf(GL_GREEN_SCALE, 0.59f);
glPixelTransferf(GL_BLUE_SCALE, 0.11f); //申请临时空间来保存修改后的图像 pModifyImage = (GLbyte *)malloc(iWidth * iHeight * 3); if (!pModifyImage)
{ return;
} //从颜色缓冲区中读取图像的亮度数据 glReadPixels(0, 0, iWidth, iHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE, pModifyImage); //还原 glPixelTransferf(GL_RED_SCALE, 1.0f);
glPixelTransferf(GL_GREEN_SCALE, 1.0f);
glPixelTransferf(GL_BLUE_SCALE, 1.0f); break; case 8: //设置反转的颜色映射 invertMap[0] = 1.0f; for(int i = 0; i < 256; ++i)
{
invertMap[i] = 1.0f - (1.0f / 255.0f * (GLfloat)i);
} //映射 glPixelMapfv(GL_PIXEL_MAP_R_TO_R, 255, invertMap);
glPixelMapfv(GL_PIXEL_MAP_G_TO_G, 255, invertMap);
glPixelMapfv(GL_PIXEL_MAP_B_TO_B, 255, invertMap); //开启颜色映射 glPixelTransferi(GL_MAP_COLOR, GL_TRUE); default: break;
}
if (pModifyImage)
{ //画黑白图像 glDrawPixels(iWidth, iHeight, GL_RGB, GL_UNSIGNED_BYTE, pModifyImage);
free(pModifyImage);
} else {
glDrawPixels(iWidth, iHeight, eFormat, GL_UNSIGNED_BYTE, pImage);
} //还原 glPixelTransferf(GL_RED_SCALE, 1.0f);
glPixelTransferf(GL_GREEN_SCALE, 1.0f);
glPixelTransferf(GL_BLUE_SCALE, 1.0f);
glPixelZoom(1.0f, 1.0f);
glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
glutSwapBuffers();
}
void SetupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //设置像素的存储格式 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //加载图像数据 pImage = gltLoadTGA("horse.tga", &iWidth, &iHeight, &iComponents, &eFormat);

} //释放图像数据占用的内存空间 void ShutdownRC()
{
if (pImage)
{
free(pImage);
pImage = NULL;
}
}

void ChangeSize(GLsizei w, GLsizei h)
{
if (h == 0)
h = 1;

glViewport(0, 0, w, h);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glutPostRedisplay();
}
int main(int args, char **argv)
{
glutInit(&args, argv);
glutInitDisplayMode(GL_RGB | GL_DOUBLE);
glutInitWindowSize(800, 600);
glutCreateWindow("pixel operation");

glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize); //设置菜单 int menuID = glutCreateMenu(ProcessMenu);
glutAddMenuEntry("Save Image", 1);
glutAddMenuEntry("Flip", 2);
glutAddMenuEntry("zoom pixel fill window", 3);
glutAddMenuEntry("Just Red", 4);
glutAddMenuEntry("Just Green", 5);
glutAddMenuEntry("Just Blue", 6);
glutAddMenuEntry("black & white", 7);
glutAddMenuEntry("invert map", 8);
glutAttachMenu(GLUT_RIGHT_BUTTON);

SetupRC();
glutMainLoop();
ShutdownRC(); return 0;
}

SetupRC 函数用于加载图像数据,保存图像数据,图像的宽高和格式等信息。

1
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //设置像素的存储格式 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //加载图像数据 pImage = gltLoadTGA("horse.tga", &iWidth, &iHeight, &iComponents, &eFormat);

在程序退出时,记得释放为图像数据分配的内存,在 ShutdownRC 中释放图像数据

1
2
3
4
5
if (pImage)
{
free(pImage);
pImage = NULL;
}

在 main 函数中,创建菜单,为每个菜单项分配键值,然后绑定到右键上。

1
2
3
4
5
6
7
8
9
10
//设置菜单 int menuID = glutCreateMenu(ProcessMenu);
glutAddMenuEntry("Save Image", 1);
glutAddMenuEntry("Flip", 2);
glutAddMenuEntry("zoom pixel fill window", 3);
glutAddMenuEntry("Just Red", 4);
glutAddMenuEntry("Just Green", 5);
glutAddMenuEntry("Just Blue", 6);
glutAddMenuEntry("black & white", 7);
glutAddMenuEntry("invert map", 8);
glutAttachMenu(GLUT_RIGHT_BUTTON);

根据所选择的菜单进行相应的操作,默认情况下是通过 glDrawPixels 函数把图像放置在窗口的左下角显示。第一个菜单项 Save Image 是保存图像的快照。

image

像素缩放

常见的图像操作,放大和缩小图像。OpenGL 提供了一个对图像进行缩放的函数:

1
void glPixelZoom(GLfloat xfactor, GLfloat yfactor);

xfactor,yfactor 指定了在 x,y 方向上缩放的倍数。图像的缩放包括了放大,缩小和反转。例如:如果 x 方向上的缩放因子为 2,那么图像在 x 方向上会放大 2 被。在本例中选择第三项菜单可以把图像填满窗口:

1
//让图像填充满屏幕 GLint viewport[4]; //取得视口的大小 glGetIntegerv(GL_VIEWPORT, viewport); //按比例缩放 glPixelZoom((GLfloat)viewport[2]/iWidth, (GLfloat)viewport[3]/iHeight);

image

如果缩放因子为负值,效果就是沿缩放方向进行反转。此时不仅仅反转了图像中像素的排列顺序,而且也翻转图像根据光栅位置在屏幕上绘制的方向。例如,一般是图像的左下角放置在当前光栅位置,如果两个缩放因子都为负值则图像的右上角被放置在当前光栅位置处:

1
//图像倒转 glPixelZoom(-1.0f, -1.0f); //倒转后的图像的,渲染方向也倒转过来了,变成了从右上角���始往左下角渲染,所以设置倒转后的光栅位置为图像的宽高。 glRasterPos2i(iWidth, iHeight);

像素变换

除了像素的缩放之外,OpenGL 还支持对图像进行一些简单的数学操作。把像素转移到颜色缓冲区或者从颜色缓冲区转移出来。可以调用下面两个函数来实现:

1
2
3
void glPixelTransferi(GLenum pname, GLint param);

void glPixelTransferf(GLenum pname, GLfloat param);

pname 的枚举值如下表:

image

缩放和偏转参数允许缩放和偏转单独的颜色通道。缩放因子将与颜色成分值相乘,偏转值则与颜色成分值相加。公式如下:

新值 = (旧值 * 缩放因子)+ 偏转值

默认情况下缩放因子是 1.0,偏转值是 0.0。如果想让图像只显示红色成分值,则可以设置绿色和蓝色的缩放因子为 0.0.

1
2
3
glPixelTransferf(GL_GREEN_SCALE, 0.0f);

glPixelTransferf(GL_BLUE_SCALE, 0.0f);

例子中,分别显示红色,绿色,蓝色成分值的代码:

1
2
3
4
5
6
7
8
case 4: //只保留红色 glPixelTransferf(GL_RED_SCALE, 1.0f);
glPixelTransferf(GL_GREEN_SCALE, 0.0f);
glPixelTransferf(GL_BLUE_SCALE, 0.0f); break; case 5: //只保留绿色 glPixelTransferf(GL_RED_SCALE, 0.0f);
glPixelTransferf(GL_GREEN_SCALE, 1.0f);
glPixelTransferf(GL_BLUE_SCALE, 0.0f); break; case 6: //只保留蓝色 glPixelTransferf(GL_RED_SCALE, 0.0f);
glPixelTransferf(GL_GREEN_SCALE, 0.0f);
glPixelTransferf(GL_BLUE_SCALE, 1.0f); break;

在绘制完成后把各个颜色通道的缩放因子复原。

1
2
3
//还原 glPixelTransferf(GL_RED_SCALE, 1.0f);
glPixelTransferf(GL_GREEN_SCALE, 1.0f);
glPixelTransferf(GL_BLUE_SCALE, 1.0f);

我们还可以把一幅彩色图像转成黑白颜色来显示。首先把彩色图像渲染到颜色缓冲区中:

1
glDrawPixels(iWidth, iHeight, eFormat, GL_UNSIGNED_BYTE, pImage);

然后分配一个用来保存每个像素亮度值的内存空间:

1
pModifyImage = (GLbyte *)malloc(iWidth* iHeight);

亮度图像只有一个通道,所以我们只分配一个字节来存储亮度值。把当前颜色缓冲区中的颜色通道进行变换:使用 NTSC(美国国家电视系统委员会)标准转成黑白图像

1
2
3
glPixelTransferf(GL_RED_SCALE, 0.3f);
glPixelTransferf(GL_GREEN_SCALE, 0.59f);
glPixelTransferf(GL_BLUE_SCALE, 0.11f);

然后通过 glReadPixel 来从颜色缓冲区中读取图像的亮度值到 pModifyImage 指向的内存区域中。

1
glReadPixel(0, 0, iWidth, iHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE, pModifyImage);

最后把 pModifyImage 的数据写会颜色缓冲区中:

1
glDrawPixel(iWidth, iHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE, pModifyImage);

OpenGl 把彩色图像转换成亮度图,只是把各个颜色通道的值相加得到亮度值,超过 1.0 的设置为 1.0,这样得到的效果不是很好:

image

为了得到更好的效果,我们可以通过 NTSC 标准把彩色图像转换成灰度图。从 RGB 颜色空间转换到黑白色彩空间的转换公式是:

亮度 = (0.3 红色) + (0.59 绿色) + (0.11 * 蓝色);

1
2
3
//NTSC标准  glPixelTransferf(GL_RED_SCALE, 0.3f);
glPixelTransferf(GL_GREEN_SCALE, 0.59f);
glPixelTransferf(GL_BLUE_SCALE, 0.11f);

得到的效果是更好看的灰度图:

image

OpenGL 超级宝典 第 4 版 中文版 PDF + 英文版 + 源代码http://www.linuxidc.com/Linux/2013-10/91413.htm

OpenGL 编程指南(原书第 7 版)中文扫描版 PDF 下载 http://www.linuxidc.com/Linux/2012-08/67925.htm

OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm

Ubuntu 13.04 安装 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm

OpenGL 三维球体数据生成与绘制【附源码】 http://www.linuxidc.com/Linux/2013-04/83235.htm

Ubuntu 下 OpenGL 编程基础解析 http://www.linuxidc.com/Linux/2013-03/81675.htm

如何在 Ubuntu 使用 eclipse for c++ 配置 OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm

更多《OpenGL 超级宝典学习笔记》相关知识 见 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581

本文永久更新链接地址http://www.linuxidc.com/Linux/2015-02/114017.htm

linux

References