其实在画虚线和点划线时就已经利用16进制的每位来代表线段的一部分是开还是关(转换为二进制后也就是1画,0不画),这里的道理类似,只不过因为是绘制平面所以规则更多,但原理和画线一样。所谓镂空,不就是舍弃一部分不画来达到更好的效果吗。
用到的重要函数:
(1)glEnable(GL_POLYGON_STIPPLE);
启用多边形填充模式
顾名思义,POLYGON是多边形,STIPPLE是v(动词)点画; 点彩画出;,所以可以看出这是一种依次看每个点画不画的模式,恰好就是和1画,0不画在语义上吻合。
(2)void glPolygonStipple(const GLubyte *mask);
指定用于填充多边形的模板位图
mask就是一个指向32×32位图的指针,这里用必要重新理清位、字节、像素(对于当前情况,不同情况下像素对应的可能不同)的关系,在这里,因为8bit=1字节(B),32×32位图就是128字节,因为默认情况一位控制一个像素画还是不画,那么一个字节对应8个像素,清楚了对应的单位,就容易理解模板位图。
此外,还要注意模板位图在创建时,是反着来的,也就是mask第一行对应画出来的图的最后一行,这么说很难理解,下面给出书中画苍蝇的例子来更好的体会
原理:(对位图的理解)
下面的flydata中一个元素就是8位,分别控制相邻八个像素(一定要转化成二进制才能看明白,跟着写几行就明白了),结合后面的效果图理解:可以看到苍蝇的最后两行是空的,正好对应flydata前两行元素(二进制都是0),而苍蝇的倒数第三行的两个翅膀的末尾,分别是0x03, 0x80, 0x01, 0xC0对应0000 0011 1000 0000 0001 1100 0000,其中两个相连的三个1正好是对应的两个翅膀的末尾,其他为0可以不画。
GLubyte flydata[128] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0,
0x06, 0xC0, 0x03, 0x60,
0x04, 0x60, 0x06, 0x20,
0x04, 0x30, 0x0C, 0x20,
0x04, 0x18, 0x18, 0x20,
0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20,
0x44, 0x03, 0xC0, 0x22,
0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22,
0x66, 0x01, 0x80, 0x66,
0x33, 0x01, 0x80, 0xCC,
0x19, 0x81, 0x81, 0x98,
0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0,
0x03, 0x3f, 0xfc, 0xc0,
0x03, 0x31, 0x8c, 0xc0,
0x03, 0x33, 0xcc, 0xc0,
0x06, 0x64, 0x26, 0x60,
0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18,
0x10, 0xc4, 0x23, 0x08,
0x10, 0x63, 0xC6, 0x08,
0x10, 0x30, 0x0c, 0x08,
0x10, 0x18, 0x18, 0x08,
0x10, 0x00, 0x00, 0x08,
};
glEnable(GL_POLYGON_STIPPLE);
glPolygonStipple(flydata);
glRectf(64.0f,0.0f,128.0f,64.0f);
通过这个glRectf画矩形的函数也可以体会到位图实现镂空的奥妙。
苍蝇的效果图:
理解上面这些,画黑白棋盘那太简单了,间隔都是固定的,只用想几个像素画一个格即可,这里我是四个像素为一格画的
#include
int winWidth=600,winHeight=500;
void myinit(void)
{
glClearColor(1.0f,1.0f,1.0f,1.0f);
}
void myReshape(int w,int h)
{
winWidth=w;
winHeight=h;
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,winWidth,0.0,winHeight);
}
//每桢OpenGL都会调用这个函数,用户应该把显示代码放在这个函数中
void display(void)
{
//设置清除屏幕的颜色,并清除屏幕和深度缓冲
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0,0.0,0.0);
GLubyte Black_and_white_chessboard[]={//黑白间隔为四个像素
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
};
glEnable(GL_POLYGON_STIPPLE);
glPolygonStipple(Black_and_white_chessboard);
glRectf(200.0,200.0,300.0,300.0);
//交换前后缓冲区
glutSwapBuffers();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
//初始化OPENGL显示方式
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);
//设定OPENGL窗口位置和大小
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
//打开窗口
glutCreateWindow ("");
//调用初始化函数
myinit();
//设定窗口大小变化的回调函数
glutReshapeFunc(myReshape);
//开始OPENGL的循环
glutDisplayFunc(display);
glutMainLoop();
return 0;
}