用OpenGL绘制平滑着色的三角形与相交区域的混合着色
一、三角形的绘制
在OpenGL中,面是由多边形构成的。三角形可能是最简单的多边形,它有三条边。可以使用GL_TRIANGLES模式通过把三个顶点连接到一起而绘出三角形。
使用GL_TRIANGLE_STRIP模式可以绘制几个相连的三角形,系统根据前三个顶点绘制第一个多边形,以后每指定一个顶点,就与构成上一个三角形的后两个顶点绘制形的一个三角形。
使用GL_TRIANGLE_FAN模式可以绘制一组相连的三角形,这些三角形绕着一个中心点成扇形排列。
第一个顶点构成扇形的中心,用前三个顶点绘制会最初的三角形后,随后的所有顶点都和扇形中心以及紧跟在它前面的顶点构成下一个三角形,此时是以顺时针方向穿过顶点。
二、绕法
在绘制三角形的过程中,三个顶点将三角形封闭的过程是有序的,即三角形的构成路径具有方向性,我们把指定顶点时顺序和方向的组合称为"绕法"。绕法是任何多边形图元的一个重要特征。
一般默认情况下,OpenGL认为逆时针绕法的多边形是正对着的,这一特性对于希望给多边形的正面和背面赋予不同的物理特性十分有用。
如果要反转OpenGL的默认行为,调用glFrontFace(GL_CW);GL_CW告诉OpenGL应该把顺时针缠绕的多边形正对着的。为了改回把逆时针绕法视为正面,可以使用GL_CCW。
三、明暗处理
在绘制多边形时,我们常常指定绘制的颜色,而在OpenGL中,颜色实际上是对各个顶点而不是对各个多边形指定的。
多边形的轮廓或者内部用单一的颜色或许多不同的颜色来填充的处理方式成为明暗处理。
在OpenGL中,用单一颜色处理的称为平面明暗处理(FlatShading),用许多不同颜色处理的称为光滑明暗处理(Smooth Shading),也称为Gourand明暗处理(Gourand Shading).设置明暗处理模式的函数void glShadeModel(GLenum mode)参数mode取值为GL_FLAT或GL_SMOOTH。
应用平面明暗处理模式时,多边形内每个点的法向一致,其颜色也一致,OpenGL用指定多边形最后一个顶点时的颜色作为填充多边形的纯色。
应用光滑明暗处理模式时,多边形所有点的法向是有内插生产的,具有一定的连续性,因此每个点的颜色也相应内插,故呈现不同色。这种模式下,插值方法采用的是双线性插值法。
Gourand明暗处理通常算法为:先用多边形顶点的光强线性插值出当前扫描线与多边形边交叉处的光强,然后再用交点的光强线插值处扫描线位于多边形内区段上每一像素处的光强值。
采用Gourand明暗处理不但可以使用多边形表示的曲面光强连续,而且计算量很小。这种算法还可以以增量的形式改进,且能用硬件直接实现算法,从而广泛用于计算机实时图形生成。
四、多边形的模式
多边形不是必须用当前颜色填充的。默认情况下绘制的多边形是实心的,但可以通过指定把多边形绘制为轮廓或只是点(只画出顶点)来修改这项默认行为。
函数glPolygonMode(Glenum face,Glenum mode);允许把多边形渲染为填充的实心,轮廓线或只是点。
另外,可以把这项渲染模式应用到多边形的两面或只应用到正面或背面。参数face指定多边形的哪一面受模式改变的影响——GL_FRONT,GL_BACK或GL_FRONT_AND_BACK。
参数mode用于指定新的绘图模式。GL_FILL是默认值,生成填充的多边形;GL_LINE生成多边形的轮廓;而GL_POINT只画出顶点。
GL_LINE和GL_POINT绘制的点和线受glEdgeFlag所设置边缘标记的影响。
五、多边形的绘制规则
在使用大量多边形构造一个复杂表面时,有两条重要规则。
第一条规则是所有多边形都必须是平面的,也就是说,多边形的所有顶点必须唯有一个平面上,不能在空间中扭曲
第二条规则是多边形的边缘不能相交,而且多边形必须是凸的。
六、相交区域的混合着色
glBlendFunc( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ); // 是最常使用的。
若源色为 ( 1.0 ,
0.9 , 0.7 , 0.8 )
源色使用 GL_SRC_ALPHA为0.8,
即 0.8*1.0 , 0.8*0.9 , 0.8*0.8 , 0.8*0.8
结果为 0.8 , 0.72
, 0.64 , 0.56
目标色为 ( 0.6 , 0.5 , 0.4 , 0.3 )
目标色使用GL_ONE_MINUS_SRC_ALPHA
即 1 - 0.8 =
0.2
0.2*0.6 , 0.2*0.5 , 0.2*0.4 , 0.2*0.3
结果为 0.12 , 0.1
, 0.08 , 0.06
由此而见,使用这个混合函数,源色的α值决定了结果颜色的百分比。
这里源色的α值为0.8,即结果颜色中源色占80%,目标色占20%。
将多边形从远至近排列,并使用以下函数
glBlendFunc( GL_SRC_ALPHA_SATURATE , GL_ONE );
glEnable( GL_POLYGON_SMOOTH );
可以优化多边形反走样;但必须有α位平面,以用来存储累加的覆盖值。
七、混合 3D 物体
混合 3D 物体时,基本原理和混合 2D 物体一样,但需要将深度检测关闭或设置为只读。
因为深度检测会剔除被遮挡的部分物体。
glEnable( GL_DEPTH_TEST ); // 启用深度缓存 glDisable( GL_DEPTH_TEST ); // 禁用深度缓存 glDepthMask( GL_FALSE ); // 深度缓存为 只读 glDepthMask( GL_TRUE ); // 深度缓存为 读/写
八、示例程序如下
#include "stdafx.h" //三角形的绘制程序 #include <GL/glut.h> //旋转参数 static GLfloat xRot=0.0f; static GLfloat yRot=0.0f; //确定多边形绕法的方向 bool bWinding = true; //初始化窗口 void init() { //设置窗口背景颜色为黑色 glClearColor(0.0f,0.0f,0.0f,1.0f); } //显示函数:绘制几何体的命令 void display() { //清除背景 glClear(GL_COLOR_BUFFER_BIT); glPushMatrix(); //把当前变换矩阵压入堆栈 glRotatef(xRot,1.0f,0.0f,0.0f);//使整个场景绕着x轴旋转 glRotatef(yRot,0.0f,1.0f,0.0f);//使整个场景绕着y轴旋转 //设置点的大小和线宽 glPointSize(5); glLineWidth(5); //设置多边形绕法的方向是顺时针还是逆时针 if (bWinding) { glFrontFace(GL_CW); //CW---clock wise顺时针 } else { glFrontFace(GL_CCW); //CCW--- } //绘制三角形 glBegin(GL_TRIANGLES); glColor3f(0.8f,1.0f,0.0f); glVertex3f(0,30,30); glColor3f(1.0f,0.0f,0.0f); glVertex3f(-40,-40,40); glColor3f(0.0f,0.0f,1.0f); glVertex3f(40,-40,0); glColor3f(0.0f,0.0f,1.0f); glVertex3f(60,-20,0); glColor3f(0.0f,0.7f,1.0f); glVertex3f(30,-60,0); glEnd(); glBegin(GL_LINES); glVertex3f(-100.3, 10.2, 10.5); glVertex3f(60, -10.2, -11); glEnd(); glPopMatrix(); //还原 glutSwapBuffers();//刷新命令缓冲区 } void reshape(int w,int h) { if (h == 0) { h = 1; } glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); //矩阵模式:投影视图 glLoadIdentity(); if (w <= h) { //正交参数设置:x=[-100, 100], y=[-100.0*h/w, 100.0*h/w], z=[-100,100] glOrtho(-100.0f,100.0f,-100.0f*h/w,100.0f*h/w,-100.0f,100.0f); } else { glOrtho(-100.0f*w/h,100.0f*w/h,-100.0f,100.0f,-100.0f,100.0f); } glMatrixMode(GL_MODELVIEW); //矩阵模式:模型视图 glLoadIdentity(); } //由键盘控制旋转 void SpecialKeys(int key,int x,int y) { if (key == GLUT_KEY_UP) xRot -=5.0f; if (key == GLUT_KEY_DOWN) xRot +=5.0f; if (key == GLUT_KEY_LEFT) yRot -=5.0f; if (key == GLUT_KEY_RIGHT) yRot +=5.0f; if (xRot > 356.0f) xRot = 0.0f; if (xRot < -1.0f) xRot = 355.0f; if (yRot > 356.0f) yRot = 0.0f; if (yRot < -1.0f) yRot = 355.0f; glutPostRedisplay();//刷新窗口 } //菜单处理 void ProcessMenu(int value) { switch(value) { case 1: { //修改多边形正面为填充模式 glPolygonMode(GL_FRONT,GL_FILL); break; } case 2: { //修改多边形正面为线模式 glPolygonMode(GL_FRONT,GL_LINE); break; } case 3: { //修改多边形正面为点填充模式 glPolygonMode(GL_FRONT,GL_POINT); break; } case 4: { //修改多边形背面为填充模式 glPolygonMode(GL_BACK,GL_FILL); break; } case 5: { //修改多边形背面为线模式 glPolygonMode(GL_BACK,GL_LINE); break; } case 6: { //修改多边形背面为点填充模式 glPolygonMode(GL_BACK,GL_POINT); break; } case 7: { //设置多边形的阴影模式为平面明暗模式 glShadeModel(GL_FLAT); break; } case 8: { //设置多边形的阴影模式为光滑明暗模式 glShadeModel(GL_SMOOTH); break; } case 9: { bWinding = !bWinding; break; } default: break; } //提交修改并强制重新绘图 glutPostRedisplay(); } void main() { init(); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); //双缓冲 glutCreateWindow("多边形演示"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutSpecialFunc(SpecialKeys);//设置特殊键响应回调函数 //菜单的句柄 int nModeMenu; int nMainMenu; int nColorMenu; //创建一个子菜单并定于菜单回调函数 nModeMenu = glutCreateMenu(ProcessMenu); //添加菜单项,1表示选择菜单条目时传递的参数值 glutAddMenuEntry("正面多边形填充模式",1); glutAddMenuEntry("正面线框模式",2); glutAddMenuEntry("正面点模式",3); glutAddMenuEntry("反面多边形填充模式",4); glutAddMenuEntry("反面线框模式",5); glutAddMenuEntry("反面点模式",6); //添加一个子菜单 nColorMenu = glutCreateMenu(ProcessMenu); glutAddMenuEntry("平面明暗模式",7); glutAddMenuEntry("光滑明暗模式",8); //创建主菜单 nMainMenu = glutCreateMenu(ProcessMenu); glutAddSubMenu("多边形模式",nModeMenu); glutAddSubMenu("颜色模式",nColorMenu); glutAddMenuEntry("改变绕法",9); //将创建的菜单与右键关联,即把菜单设置为右键弹出式菜单 glutAttachMenu(GLUT_RIGHT_BUTTON); glutMainLoop(); }
#include <GL/glut.h> #include <stdlib.h> static int leftFirst = GL_TRUE; /* Initialize alpha blending function. */ static void init(void) { glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //glBlendFunc(GL_ONE, GL_ONE); glShadeModel (GL_FLAT); glClearColor (0.0, 0.0, 0.0, 0.0); } static void drawLeftTriangle(void) { /* draw yellow triangle on LHS of screen */ glBegin (GL_TRIANGLES); glColor4f(1.0, 0.0, 0.0, 0.55); glVertex3f(0.1, 0.9, 0.0); glVertex3f(0.1, 0.1, 0.0); glVertex3f(0.7, 0.5, 0.0); glEnd(); } static void drawRightTriangle(void) { /* draw cyan triangle on RHS of screen */ glBegin (GL_TRIANGLES); glColor4f(0.0, 1.0, 0.0, 0.55); glVertex3f(0.9, 0.9, 0.0); glVertex3f(0.3, 0.5, 0.0); glVertex3f(0.9, 0.1, 0.0); glEnd(); } void display(void) { glClear(GL_COLOR_BUFFER_BIT); if (leftFirst) { drawLeftTriangle(); drawRightTriangle(); } else { drawRightTriangle(); drawLeftTriangle(); } glFlush(); } void reshape(int w, int h) { glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); //2D正交投影参数 if (w <= h) gluOrtho2D (0.0, 1.0, 0.0, 1.0*(GLfloat)h/(GLfloat)w); else gluOrtho2D (0.0, 1.0*(GLfloat)w/(GLfloat)h, 0.0, 1.0); } void keyboard(unsigned char key, int x, int y) { switch (key) { case 't': case 'T': leftFirst = !leftFirst; glutPostRedisplay(); break; case 27: /* Escape key */ exit(0); break; default: break; } } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); glutInitWindowSize (200, 200); glutCreateWindow (argv[0]); init(); glutReshapeFunc (reshape); glutKeyboardFunc (keyboard); glutDisplayFunc (display); glutMainLoop(); return 0; }
您的资助是我最大的动力!
金额随意,欢迎来赏!
本博客的所有打赏均将用于博主女朋友的化妆品购买以及养肥计划O(∩_∩)O。我是【~不会飞的章鱼~】!