OpenGL入门3——填充区、像素阵列、字符

25 篇文章 4 订阅

1、多边形填充区

        多数图形软件包使用平面片来显示曲面,这是因为平面方程是线性的,而处理线性方程比二次或其他类曲线方程快的多。因此OpenGL和其他图形软件包提供多边形图元来实施曲面逼近,对象用多边形网络来建模,而几何和属性信息的数据库按处理多边形面片的目标来建立。在OpenGL中,可用于此目的的图元有三角形带(triangle strip)、三角形扇形(triangle fan)和四边形带(quad strip)。高性能图形系统使用快速多边形硬件绘制,使得显示速度达到每秒形成百万以上的多边形(通常为三角形),包括使用表面纹理和特殊光照效果。

       尽管OpenGL的核心函数库只允许凸多边形,但是OpenGL的实用函数库(GLU)提供有关函数处理凹多边形和其他有线性边界的非凸对象。可使用一组GLU多边形细分子程序来将那些形状转换为三角形、三角形网络、三角形扇形和直线段。一旦那些形状被分解,就可以使用OpenGL函数进行处理。

 

2、OpenGL顶点数组

      复杂场景的描述需要使用几百或者几千个坐标描述。另外还必须为各个对象建立各种属性和观察参数。因此,对象和场景描述要使用大量的函数调用,这对系统资源提出了要求并减慢了图形程序的执行。复杂显示进一步的问题是对象表面通常有共享顶点。

      为了简化这些问题,OpenGL提供了一种机制来减少处理坐标信息的函数调用数量,这就是使用顶点数组(vertex array),可以利用很少的函数调用来安排场景的描述信息。

      使用vertex array的步骤:

      1、引用函数glEnableClientState(GL_VERTEX_ARRAY)激活OpenGL的顶点数组特性;

      2、使用函数glVertexPointer指定顶点坐标的位置和数据格式;

      3、使用子程序如glDrawElements显示场景,该子程序可处理多个图元而仅需少量的函数调用。

  绘制一个立方体的例子:

        glLoadIdentity();//将当前的用户坐标系的原点移到了屏幕左下(右x,上y,前z):类似于一个复位操作
	glClearColor(0.0, 1.0, 1.0, 0.3);//指定当前清除颜色的颜色值
	glClear(GL_COLOR_BUFFER_BIT);//把整个窗口清除为当前的清除颜色

	typedef GLint vertex3[3];
	vertex3 pt[8] = { {0,0,0},{0,100,0},{100,0,0},{100,100,0},{0,0,100},{0,100,100},{100,0,100},{100,100,100}};

	/*
	*函数名称:glEnableClientState
	*函数功能:激活客户/服务器系统中的客户端的某种功能(这里指顶点数组)
	*参数:GLenum:指定的功能代码
	*返回值:void
	*/
	glEnableClientState( GL_VERTEX_ARRAY );

	/*
	*函数原型:void glVertexPointer(
	*              GLint size,//指定每个顶点对应的坐标维数,只能是2,3,4中的一个,默认是4
	*              GLenum type,//指定数组中每个顶点坐标的数据格式,可取GL_BYTE、GL_SHORT、GL_FIXED等
	*              GLsizei stride,//指定连续顶点间的字节排列方式,如果为0,数组中的顶点被认为是按照紧凑方式排列,默认值为0
	*              const GLvoid * pointer//指向包含坐标值的顶点数组
	*                        );
	*函数功能:指定对象顶点坐标的位置和格式
	*/
	glVertexPointer(3, GL_INT, 0, pt);

	/*顶点坐标的索引数组*/
	GLubyte vertexIndex[] = {6,2,3,7,5,1,0,4,7,3,1,5,4,0,2,6,2,0,1,3,7,5,4,6};

	glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, vertexIndex);
	SwapBuffers( ::GetDC(m_hWnd) );


3、像素阵列图元

(1)、位图函数

例子:定义一个10行9列的位图阵列,每行使用16个二进制位来描述,在将图案应用于帧缓存像素时,第9列之后的所有位值均被忽略。每行的位值可以用两个八进制数表示。bitShape的阵列值从矩形网格的底部开始逐行指定,代码如下:

<span style="font-size:14px;">glLoadIdentity();//将当前的用户坐标系的原点移到了屏幕左下(右x,上y,前z):类似于一个复位操作
	glClearColor(0.0, 1.0, 1.0, 0.3);//指定当前清除颜色的颜色值
	glClear(GL_COLOR_BUFFER_BIT);//把整个窗口清除为当前的清除颜色

	//定义位图阵列
	GLubyte bitShape[20] ={
	       0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00 ,0x1c, 0x00,
	       0xff, 0x80, 0x7f, 0x00, 0x3e, 0x00, 0x1c, 0x00, 0x08, 0x00};

	//设置像素存储模式
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	//设置当前光栅位置坐标
	//注意:位图的颜色使用glRasterPos被引用时的有效颜色。任何后来的颜色改变不会影响该位图
	glRasterPos2i(30, 40);

	//
	glBitmap(9, 10, 0.0, 0.0, 20.0, 15.0, bitShape);

	SwapBuffers( ::GetDC(m_hWnd) );</span>


(2)、像素图函数

函数: glDrawPixel( width, height, dataFormat, dataType, pixMap);

功能:将用彩色阵列定义的图案应用到一块帧缓存的像素位置。

参数width,height分别给出像素位图(pixMap)的列数和行数;参数dataFormat使用OpenGL的常量赋值,指出如何为阵列指定值,例如GL_BLUE可指定所有像素都使用蓝色,使用常量GL_RGB可按蓝、绿、红次序指定颜色分量;参数dataType设定为常量GL_INT、GL_BYTE、GL_FLOAT等,以指定阵列中颜色的数据类型。

 注意:该颜色阵列的左下角映射到有glRasterPos设定的当前光栅位置。

例如:显示一个128 * 128 的RGB彩色阵列定义的像素图:

          glDrawPixels(128, 128, GL_RGB, GL_UNSIGNED_BYTE, colorShape);

由于OpenGL提供多个缓存,将某个缓存选为glDrawPixels子程序目标即可将一个阵列送进该缓存

 

(3)、光栅操作

光栅操作(raster operation)用于描述以某种方式处理一个像素阵列的任何功能。

bitblt移动(bit-block transfer):将一个像素阵列的值从一个位置移动到另一个位置的光栅操作,称为像素值的bitblt移动,尤其是在该功能由硬件实现时。

参看:http://blog.csdn.net/ghost129/article/details/4409565

OpenGL提供了简洁的函数来操作像素:
     glReadPixels:读取一些像素。当前可以简单理解为“把已经绘制好的像素(它可能已经被保存到显卡的显存中)读取到内存”。
     glDrawPixels:绘制一些像素。当前可以简单理解为“把内存中一些数据作为像素数据,进行绘制”。
     glCopyPixels:复制一些像素。当前可以简单理解为“把已经绘制好的像素从一个位置复制到另一个位置”。虽然从功能上看,好象等价于先读取像素再绘制像素,但实际上它不需要把已经绘制的像素(它可能已经被保存到显卡的显存中)转换为内存数据,然后再由内存数据进行重新的绘制,所以要比先读取后绘制快很多。
这三个函数可以完成简单的像素读取、绘制和复制任务,但实际上也可以完成更复杂的任务。当前,我们仅讨论一些简单的应用。由于这几个函数的参数数目比较多,下面我们分别介绍。

函数解释:

      1*函数原型:glReadPixels(GLint xmin, GLint ymin, GLsizei width, GLsizei height, GLenum dataFormat,  GLenum dataType, GLvoid* array);

      前四个参数可以得到一个矩形,该矩形所包括的像素都会被读取出来。(第一、二个参数表示了矩形的左下角横、纵坐标,坐标以窗口最左下角为零,最右上角为最大值;第三、四个参数表示了矩形的宽度和高度),第五个参数表示读取的内容,例如:GL_RGB就会依次读取像素的红、绿、蓝三种数据,GL_RGBA则会依次读取像素的红、绿、蓝、alpha四种数据,GL_RED则只读取像素的红色数据(类似的还有GL_GREEN、GL_BLUE,以及GL_ALPHA)。如果采用的不是RGBA颜色模式,而是采用颜色索引模式,则也可以使用GL_COLOR_INDEX来读取像素的颜色索引。目前仅需要知道这些,但实际上还可以读取其它内容,例如深度缓冲区的深度数据等。
第六个参数表示读取的内容保存到内存时所使用的格式,例如:GL_UNSIGNED_BYTE会把各种数据保存为GLubyte,GL_FLOAT会把各种数据保存为GLfloat等第七个参数表示一个指针,像素数据被读取后,将被保存到这个指针所表示的地址注意,需要保证该地址有足够的可以使用的空间,以容纳读取的像素数据例如一幅大小为2568256的图象,如果读取其RGB数据,且每一数据被保存为GLubyte,总大小就是:256*256*3=196608字节,即192千字节。如果是读取RGBA数据,则总大小就是256*256*4=262144字节,即256千字节。
    注意:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓冲区的。因此,如果需要读取已经绘制好的像素,往往需要先交换前后缓冲。

    2*函数原型:glCopyPixels(xmin, ymin, width, height, pixelValues);

    从效果上讲,glCopyPixels进行像素复制操作,等价于将像素读入到内存,再从内存绘制到另一块区域,因此可以通过glReasPixels和glDrawPixels组合来实现像素复制的功能,但是像素数据通常数据量很大,例如,一幅1024*768的图像,如果使用24位RGB方式来表示,就需要至少1024*768*3字节,即2.25M字节。这么多的数据要进行一次读操作和一次写操作,并且因为在glReadPixels和glDrawPixels中设置的数据格式不同,很可能涉及到数据格式的转换,这对cpu无疑是一个不小的负担。使用glCopyPixels直接从像素数据复制出新的像素数据,避免了多余的数据格式的转换,并且也可能减少一些数据复制操作(因为数据可能直接由显卡负责复制,不需要经过主内存),因此效率比较高。

    第一、二个参数表示待复制像素源的矩形的左下角坐标,第三、四个参数表示矩形的宽度和高度,第五个参数可以是GL_COLOR、GL_DEPTH、GL_STENCIL等,标识要复制的缓存。

绘制一个三角形后,复制像素,并同时进行水平和垂直方向的翻转,然后缩小为原来的一半,并绘制。绘制完毕后,调用前面的grab函数,将屏幕中所有内容保存为grab.bmp。其中WindowWidth和WindowHeight是表示窗口宽度和高度的常量。

void display(void)
{
    // 清除屏幕
    glClear(GL_COLOR_BUFFER_BIT);

    // 绘制
    glBegin(GL_TRIANGLES);
        glColor3f(1.0f, 0.0f, 0.0f);    glVertex2f(0.0f, 0.0f);
        glColor3f(0.0f, 1.0f, 0.0f);    glVertex2f(1.0f, 0.0f);
        glColor3f(0.0f, 0.0f, 1.0f);    glVertex2f(0.5f, 1.0f);
    glEnd();
    glPixelZoom(-0.5f, -0.5f);
    glRasterPos2i(1, 1);
    glCopyPixels(WindowWidth/2, WindowHeight/2,
        WindowWidth/2, WindowHeight/2, GL_COLOR);

    // 完成绘制,并抓取图象保存为BMP文件
    glutSwapBuffers();
    grab();
}

#define WindowWidth  400
#define WindowHeight 400

#include <stdio.h>
#include <stdlib.h>

/* 函数grab
 * 抓取窗口中的像素
 * 假设窗口宽度为WindowWidth,高度为WindowHeight
 */
#define BMP_Header_Length 54
void grab(void)
{
    FILE*    pDummyFile;
    FILE*    pWritingFile;
    GLubyte* pPixelData;
    GLubyte  BMP_Header[BMP_Header_Length];
    GLint    i, j;
    GLint    PixelDataLength;

    // 计算像素数据的实际长度
    i = WindowWidth * 3;   // 得到每一行的像素数据长度
    while( i%4 != 0 )      // 补充数据,直到i是的倍数
        ++i;               // 本来还有更快的算法,
                           // 但这里仅追求直观,对速度没有太高要求
    PixelDataLength = i * WindowHeight;

    // 分配内存和打开文件
    pPixelData = (GLubyte*)malloc(PixelDataLength);
    if( pPixelData == 0 )
        exit(0);

    pDummyFile = fopen("dummy.bmp", "rb");
    if( pDummyFile == 0 )
        exit(0);

    pWritingFile = fopen("grab.bmp", "wb");
    if( pWritingFile == 0 )
        exit(0);

    // 读取像素
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glReadPixels(0, 0, WindowWidth, WindowHeight,
        GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);

    // 把dummy.bmp的文件头复制为新文件的文件头
    fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile);
    fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);
    fseek(pWritingFile, 0x0012, SEEK_SET);
    i = WindowWidth;
    j = WindowHeight;
    fwrite(&i, sizeof(i), 1, pWritingFile);
    fwrite(&j, sizeof(j), 1, pWritingFile);

    // 写入像素数据
    fseek(pWritingFile, 0, SEEK_END);
    fwrite(pPixelData, PixelDataLength, 1, pWritingFile);

    // 释放内存和关闭文件
    fclose(pDummyFile);
    fclose(pWritingFile);
    free(pPixelData);
}


 4、字符

      存储在计算机中的字体分为两种:位图字体(bitmap font)也称光栅字体(raster font),另一种字体是轮廓字体(outline font)或称笔画字体(stroke font)。

     OpenGL字符函数

      glutBitmapCharacter(font, character);

      glutStrokeCharacter(font, character);

 

5、OpenGL显示列表

     在OpenGL中使用显示表,可以把对象描述为一个命名的语句序列(或其他的命令集)并存储起来。显示表对层次式建模特别有用,因为一个复杂的对象可以用一组简单的对象来描述。

      (1)、创建和命名显示表

       使用glNewList和glEndList函数对包围一组OpenGL命令就可形成显示表。例如:

       glNewList( listID, listMode);

       .....

       glEndList();

       注意:显示表创建后,立即对包含有如坐标位置和颜色分量等参数的表示进行赋值计算,从而使表中仅存储参数的值,因此对这些参数的任何后继修改都不起作用。因为不能修改显示表中的值,因此在显示表中不能包含如顶点表指针等OpenGL命令。

       (2)、执行显示表

      调用glCallList( listID );

      (3)、删除显示表

      调用glDeleteLists( startID, nLists);//删除连续的一组显示表。

     例子:创建并执行一组显示表

glLoadIdentity();//将当前的用户坐标系的原点移到了屏幕左下(右x,上y,前z):类似于一个复位操作
	glClearColor(0.0, 1.0, 1.0, 0.3);//指定当前清除颜色的颜色值
	glClear(GL_COLOR_BUFFER_BIT);//把整个窗口清除为当前的清除颜色

	const double TWO_PI = 6.2831853;
	GLuint regHex;
	GLdouble theta;
	GLint x, y, k;

	//产生一个显示表ID(正整数)
	if(regHex = glGenLists( 1 ) == 0)
		return ;
	glNewList(regHex, GL_COMPILE);
	    glBegin( GL_POLYGON );
		    for( k=0; k<6; k++)
			{
				theta = TWO_PI * k /6.0;
				x = 200 + 150 * cos(theta);
				y = 200 + 150 * sin(theta);
				glVertex2i(x, y);
			}
		glEnd();
	glEndList();

	//执行显示表
	glCallList( regHex );
	SwapBuffers( ::GetDC(m_hWnd) );


 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python的OpenGL库是一个用于渲染图形和实现3D图形应用的强大工具。如果你是一个初学者,以下是一些学习OpenGL的指南: 1. 学习基本的计算机图形学概念:在深入学习OpenGL之前,了解一些基本的计算机图形学概念是很重要的。你可以学习像坐标系、向量、矩阵变换等基础知识。 2. 学习Python语言基础:作为一个初学者,你需要先掌握Python的基本语法和编程概念。这将帮助你更好地理解和使用OpenGL库。 3. 安装OpenGL库:在开始之前,你需要确保你的计算机上已经安装了OpenGL库。你可以使用pip来安装PyOpenGL库。 4. 学习OpenGL的核心知识:一旦你准备好了,可以开始学习OpenGL的核心知识,如顶点缓冲对象(VBO)、着色器(programs)、着色器语言(GLSL)等。掌握这些基本概念对于理解和使用OpenGL非常重要。 5. 编写简单的OpenGL程序:接下来,你可以开始编写一些简单的OpenGL程序来实践所学的知识。你可以从简单的绘制一些基本图形开始,然后逐渐扩展到更复杂的场景和效果。 6. 学习OpenGL的高级特性:一旦你熟悉了OpenGL的基本知识,你可以探索一些更高级的主题,如光照、纹理映射、深度测试、投影等。这将帮助你创建更逼真和交互式的3D图形应用。 7. 参考文档和教程:除了上述的自学方法外,你还可以参考一些优秀的OpenGL文档和教程。一些推荐的资源包括OpenGL官方文档、PyOpenGL官方文档、学习OpenGL的在线教程等。 记住,学习OpenGL需要时间和实践。通过不断地编写代码和实验,你将逐渐掌握OpenGL的技能并创建出令人惊叹的图形应用。祝你好运!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值