本节是OpenGL学习的第六个课时,下面介绍OpenGL图形的相关知识:
(1)多边形的概念:
多边形是由多条线段首尾相连而形成的闭合区域。OpenGL规定,一个多边形必须是一个“凸多边形”。通过点、直线和多边形,就可以组合成各种几何图形。一段弧可以看成是是很多短的直线段相连,这些直线段足够短,以至于其长度小于一个像素的宽度。通过位于不同平面的相连的小多边形,还可以组成一个“曲面”。
什么是凸边形:
凸边形:多边形内任意两点所确定的线段都在多边形内,由此也可以推导出,凸多边形不能是空心的。
凸边形与凹边形:
凸多边形又可称为平面多边形,是多边形中的一种,与凹多边形相对。凸多边形的内角均小于180°,而凹边形内角有的可以大于180°。
验证方法:
将图形的任意一边无限延长,如果延长线与图形有交点就是凹边形,反之就是凸边形。
多边形是OpenGL中绘制曲面的基础。
(2)如何绘制多边形:
1)三维坐标系下多边形的两面及其绘制方式:
从三维的角度来看,一个多边形具有两个面,每一个面都可以设置不同的绘制方式:
绘制方式:
.填充
.只绘制边缘轮廓线
.只绘制顶点
(“填充”是默认的方式)
void glPolygonMode(GLenum face,GLenum mode)函数用于控制多边形的显示方式:
face参数确定显示模式将适用于物体的哪些部分,控制多边形的正面和背面的绘图模式:
.GL_FRONT 表示显示模式将适用于物体的前向面(也就是物体能看到的面)
.GL_BACK 表示显示模式将适用于物体的后向面(也就是物体上不能看到的面)
.GL_FRONT_AND_BACK 表示显示模式将适用于物体的所有面
mode参数确定选中的物体的面以何种方式显示(显示模式):
.GL_POINT 表示只显示顶点,多边形用点显示
.GL_LINE 表示显示线段,多边形用轮廓显示
.GL_FILL 表示显示面,多边形采用填充形式
可以为两个面分别设置不同的显示方式:
glPolygonMode(GL_FRONT, GL_FILL); //设置正面为填充方式
glPolygonMode(GL_BACK, GL_LINE); //设置反面为边缘绘制方式
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); //设置两面均为顶点绘制方式
2)反转(切换正反面):
一般约定:顶点以逆时针顺序出现在屏幕上的面为正面,另一个面即成为反面。可以使用glFrontFace函数来调换正面与反面的概念。
void glFrontFace(GLenum mode)作用是控制多边形的正面是如何决定的:
mode参数的值:
.GL_CCW 表示窗口坐标上投影多边形的顶点顺序为逆时针方向的表面为正面。(CounterClockWise)
.GL_CW 表示顶点顺序为顺时针方向的表面为正面。(ClockWise)
顶点的方向又称为环绕。
(在默认情况下,mode是GL_CCW)
如果一个虚构的对象的顶点是按照多边形内部顺时针的方向进行绘制的,那么可以称这个多边形基于窗口坐标的投影是顺时针的。反之,则为逆时针。glFrontFace就是用来指定多边形在窗口坐标中的方向是逆时针还是顺时针的。
3)剔除多边形表面:
在一个全部由不透明封闭表面组成的场景中,背面多边形是永远看不见的。剔除这些不可见的多边形对于加速图形的渲染有很大的益处。
OpenGL中使用glCullFace()来进行剔除操作:
剔除操作的步骤:
.glEnable(GL_CULL_FACE):启动剔除功能;(glDisable(GL_CULL_FACE)关闭剔除功能)
.使用glCullFace()来进行剔除;
void glCullFace(GLenum mode)指明前面或后面的多边形是否要剔除:
mode参数的值:
.GL_FRONT 表示剔除正面
.GL_BACK 剔除反面
.GL_FRONT_AND_BACK 剔除正反两面的多边形
4)镂空多边形:
直线可以被画成虚线,而多边形则可以进行镂空。
OpenGL中使用glPolygonStipple()函数设置镂空的样式:
镂空操作的步骤:
.使用glEnable(GL_POLYGON_STIPPLE)来启动镂空模式。(使用glDisable(GL_POLYGON_STIPPLE)可以关闭之)。
.使用glPolygonStipple()来设置镂空的样式。
void glPolygonStipple(const GLubyte *mask)
glPolygonStipple() -- set the polygon stippling pattern
PARAMETERS
mask Specifies a pointer to a 32*32 stipple pattern that will be unpacked from memory in the same way that glDrawPixels unpacks pixels.
DESCRIPTION
Polygon stippling, like line stippling (see glLineStipple), masks out certain fragments produced by rasterization, creating a pattern. Stippling is independent of polygon antialiasing. mask is a pointer to a 32*32 stipple pattern that is stored in memory just like the pixel data supplied to a glDrawPixels with height and width both equal to 32, a pixel format of GL_COLOR_INDEX, and data type of GL_BITMAP. That is, the stipple pattern is represented as a 32*32 array of 1-bit color indices packed in unsigned bytes. glPixelStore parameters like GL_UNPACK_SWAP_BYTES and GL_UNPACK_LSB_FIRST affect the assembling of the bits into a stipple pattern. Pixel transfer operations (shift, offset, pixel map) are not applied to the stipple image, however. Polygon stippling is enabled and disabled with glEnable and glDisable, using argument GL_POLYGON_STIPPLE. If enabled, a rasterized polygon fragment with window coordinates xw and yw is sent to the next stage of the GL if and only if the (xw mod 32)th bit in the (yw mod 32)th row of the stipple pattern is one. When polygon stippling is disabled, it is as if the stipple pattern were all ones.
此函数定义填充多边形的当前点画模式。其中的参数mask指向一个长度为128字节的空间,它表示了一个32*32的矩形应该如何镂空。其中:第一个字节表示了最左下方的从左到右(也可以是从右到左,这个可以修改)8个像素是否镂空(1表示不镂空,显示该像素;0表示镂空,显示其后面的颜色),最后一个字节表示了最右上方的8个像素是否镂空。
(3)绘制实例:
1)不同绘图模式下的对比和反转的对比:
#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#define Pi 3.1416
void Init(void)
{
glClearColor(0.5,0.5,0.5,0.5);
glClear(GL_COLOR_BUFFER_BIT);
} void paint()
{
glViewport(,,,); //先画分割线
glColor3f(1.0f,0.0f,0.0f);
glPointSize(5.0f);
glBegin(GL_LINES);
glVertex2f(-0.6f, -1.0f); glVertex2f(-0.6f, 1.0f);
glVertex2f( 0.2f, -1.0f); glVertex2f( 0.2f, 1.0f);
glEnd(); GLfloat a = / ( - * cos( * Pi / ));
GLfloat bx = a * cos( * Pi / );
GLfloat by = a * sin( * Pi / );
GLfloat cy = -a * cos( * Pi / );
GLfloat
PointA[] = { , a},
PointB[] = { bx , by},
PointC[] = { 0.25, cy},
PointD[] = { -0.25, cy},
PointE[] = { -bx, by }; //图1,为只无论正反面绘制顶点
glViewport(, , , );
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);//前面和后面均为以顶点的方式绘制
glColor3f(1.0f, 1.0f, 0.0f); glPointSize(5.0f);//黄色的顶点
glBegin(GL_POLYGON);
glVertex2fv(PointA);
glVertex2fv(PointC);
glVertex2fv(PointE);
glVertex2fv(PointB);
glVertex2fv(PointD);
glEnd(); //其余的图,均为正面填充,反面线形
glPolygonMode(GL_FRONT, GL_FILL);// 设置正面为填充模式
glPolygonMode(GL_BACK, GL_LINE);// 设置反面为线形模式
glPointSize(2.0f);
//图2和图三形成对比,一个看得是正面,一个看得是反面
//图2
glViewport(,,,);
glFrontFace(GL_CCW);//切换正反面
glColor3f(0.0, 1.0, 1.0f);
glBegin(GL_POLYGON);
glVertex2fv(PointA);
glVertex2fv(PointC);
glVertex2fv(PointE);
glVertex2fv(PointB);
glVertex2fv(PointD);
glEnd();
//图3
glViewport(,,,);
glColor3f(0.0, 1.0, 1.0f);
glFrontFace(GL_CW);//切换正反面
glBegin(GL_POLYGON);
glVertex2fv(PointA);
glVertex2fv(PointC);
glVertex2fv(PointE);
glVertex2fv(PointB);
glVertex2fv(PointD);
glEnd();
//图4和图5形成对比,一个看得是正面,一个看得是反面
//图4
glViewport(, , , );
glFrontFace(GL_CCW);//切换正反面
glColor3f(0.0, 0.0, 1.0f);
glBegin(GL_POLYGON);
glVertex2fv(PointA);
glVertex2fv(PointB);
glVertex2fv(PointC);
glVertex2fv(PointD);
glVertex2fv(PointE);
glEnd();
//图5
glViewport(,,,);
glFrontFace(GL_CW);//切换正反面
glColor3f(0.0, 0.0, 1.0f);
glBegin(GL_POLYGON);
glVertex2fv(PointA);
glVertex2fv(PointB);
glVertex2fv(PointC);
glVertex2fv(PointD);
glVertex2fv(PointE);
glEnd();
//图6
glViewport(, , , );
glFrontFace(GL_CW);//切换正反面
glColor3f(0.0, 0.0, 1.0f);
glBegin(GL_POLYGON);
glVertex2fv(PointE);
glVertex2fv(PointD);
glVertex2fv(PointC);
glVertex2fv(PointB);
glVertex2fv(PointA);
glEnd();
//图5和图6形成对比,按不同顶点顺序绘制,正反面的定义不同。
glFlush();
}
int main(int argc,char *argv[])
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
Init();
glutInitWindowSize(,);
glutInitWindowPosition(,);
glutCreateWindow("不同绘图模式下的对比和反转的对比");
glutDisplayFunc(paint);
glutMainLoop();
}
2)在3D空间中绘制旋转立方体:
#include<GL/freeglut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //绘制立方体的八个立体空间中的坐标值,原点即其中点
GLfloat vertexNum[][] = {
{ 1.0, -1.0, -1.0 },
{ 1.0, -1.0, 1.0 },
{ 1.0, 1.0, 1.0 },
{ 1.0, 1.0, -1.0 },
{ -1.0, -1.0, -1.0 },
{ -1.0, -1.0, 1.0 },
{ -1.0, 1.0, 1.0 },
{ -1.0, 1.0, -1.0 }
};
GLfloat colors[][] = {
{ 1.0, 0.0, 0.0 },//红色
{ 1.0, 1.0, 0.0 },//黄色
{ 0.0, 1.0, 0.0 },//绿色
{ 0.0, 1.0, 1.0 },//青色
{ 1.0, 0.0, 1.0 },//品红色
{ 0.0, 0.0, 1.0 },//淡蓝色
{ 0.0, 0.5, 0.0 },//淡绿色
{ 0.0, 0.5, 0.5 },//淡青色 }; float angle = ; void polygon(int a, int b, int c, int d)
{ //4个点组成一个面
glBegin(GL_QUADS);//绘制多组独立的填充四边形
glColor3fv(colors[a]);
glVertex3fv(vertexNum[a]);
glColor3fv(colors[a]);
glVertex3fv(vertexNum[b]);
glColor3fv(colors[a]);
glVertex3fv(vertexNum[c]);
glColor3fv(colors[a]);
glVertex3fv(vertexNum[d]);
glEnd();
} void cube(void)
{
polygon(, , , );
polygon(, , , );
polygon(, , , );
polygon(, , , );
polygon(, , , );
polygon(, , , );
} void paint(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix();
{
glRotatef(angle, , , );
glColor3f(, , );
glutWireCube(2.0);//绘制边长为2的线框
cube();
}
glPopMatrix();
glutSwapBuffers();
} void IdleFunction()
{
angle += 0.02;
if (angle > )
angle = ;
glutPostRedisplay();
} void reshapeFunction(int w, int h)
{
glViewport(, , w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-2.0, 2.0, -2.0, 2.0, -8.0, 8.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(3.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
void Init()
{
glEnable(GL_DEPTH_TEST);
glClearColor(0.3, 0.3, 0.3, 1.0f);
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowPosition(,);
glutInitWindowSize(, );
glutCreateWindow("RotateCube");
Init(); glutReshapeFunc(reshapeFunction);
glutIdleFunc(IdleFunction);
glutDisplayFunc(paint);
glutMainLoop();
return ; }
3)面的镂空操作的讲解实例:
#include <GL/glut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") GLubyte data[] = {//数组就是一个苍蝇的图像。 数组有128个字节, 表示了一个32x32的矩阵型镂空的数据。数组里面的第一个字节表示了左下方从左到右的8个像素是否镂空, 以此类推。
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,
}; void display(void)
{
glClearColor(0.5,0.5,0.5,0.5);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_POLYGON_STIPPLE);
glPolygonStipple(data);
glRectf(-0.75f, -0.75f, 0.75f, 0.75f);
glFinish();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(, );
glutInitWindowPosition(, );
glutCreateWindow("利用多边形镂空绘制苍蝇");
glutDisplayFunc(display);
glutMainLoop();
}
我们可以自定义进行图形镂空的图形:
保存为.bmp形式。
#include <GL/glut.h>
#include <stdio.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void display(void)
{
GLubyte data[];
FILE* fp;
fp = fopen("C:/Users/JMSun/Desktop/图片/logo.bmp", "rb");
if (!fp)
{
exit();
}
if (fseek(fp, -(int)sizeof(data), SEEK_END))
{
exit();
}
if (!fread(data, sizeof(data), , fp))
{
exit();
}
fclose(fp); glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_POLYGON_STIPPLE);
glPolygonStipple(data);
glRectf(-0.75f, -0.75f, 0.75f, 0.75f);
glFinish();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(, );
glutInitWindowPosition(, );
glutCreateWindow("利自定义logo进行多边形镂空");
glutDisplayFunc(display);
glutMainLoop();
}
4)相关知识:
1.设置颜色:
void glColor3f (GLfloat red, GLfloat green, GLfloat blue);//设置颜色
void glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);//设置颜色, 带透明通道
如果需要使用透明通道, 必须要打开ALPHA混合器,并指定源与目标的混合方式:
使用透明通道时,打开ALPHA混合器的方式:
.glEnable(GL_BLEND); // 打开混合
.glDisable(GL_DEPTH_TEST); // 关闭深度测试
.glBlendFunc(GL_SRC_ALPHA, GL_ONE); // 基于源象素alpha通道值的半透明混合函数
使用ALPHA通道的实例:
#include <GL/glut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void display()
{
glClearColor(0.5f,0.5f,0.5f,0.5f);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND); // 打开混合
glDisable(GL_DEPTH_TEST); // 关闭深度测试
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // 基于源象素alpha通道值的半透明混合函数
glColor3f(0.0f, 1.0f, 0.0f);
//glBegin(GL_POLYGON);//运用系统自带的函数绘制图形不需要glBegin和glEnd;
glRectf(-0.6,-0.6,0.6,0.6);
//glEnd(); glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
glLineWidth(20.0f);
glBegin(GL_LINES);
glVertex2f(-0.25f, );
glVertex2f(0.25f, );
glEnd();
glFinish();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(, );
glutInitWindowPosition(, );
glutCreateWindow("带透明值Alpha的图形");
glutDisplayFunc(display);
glutMainLoop();
}
(4)实时动态绘制正弦曲线上的点:(在之前的版本基础上的改动)
#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") #define GL_PI 3.1416f
const GLfloat factor = 0.1f; GLfloat transferNum = 0.0f; void SetupRC(void)
{
glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
}
void paint(void)
{
glClear(GL_COLOR_BUFFER_BIT);
GLfloat x;
glColor3f(1.0f, 1.0f, 1.0f);
glPointSize(4.0f);//应在glBegin之前
glBegin(GL_POINTS);
for (x = -; x<; x += 0.1)
{
glVertex2f(x*factor, sin(x -transferNum)*factor * );
}
glEnd();
glColor3f(1.0f, 0.0f, 0.0f);
glBegin(GL_LINES);
glVertex2f(-1.0f, 0.0f);
glVertex2f(1.0f, 0.0f);
glVertex2f(0.0f, -1.0f);
glVertex2f(0.0f, 1.0f);
glEnd();
glutSwapBuffers();
}
void IdleFunction()
{
transferNum += 0.003 / ( * GL_PI);
if (transferNum>*GL_PI){
transferNum = ;
}
glutPostRedisplay();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//当没有键盘监听时不需要用双缓冲模式
glutInitWindowSize(, );
glutCreateWindow("动态正弦图像");
glutDisplayFunc(paint);
SetupRC();
glutIdleFunc(IdleFunction);
glutMainLoop(); return ;
}