OpenGL十二讲代码
文章目录
- OpenGL十二讲代码—by yjq
- 参考资料
- 第一讲
- 画一个矩形
- 第二讲
- 画一个五角星
- 画一个圆
- 画一个正弦函数
- 第三讲
- 画点
- 画虚线
- 多边形
- 多边形的两面
- 前后反转
- 剔除多边形表面
- 镂空多边形
- 第四讲
- RGBA颜色
- 颜色索引
- 颜色表
- 第五讲
- 太阳月亮地球
- 第六讲
- 太阳月亮地球加旋转
- 第七讲
- 太阳月亮加光照
- 第八讲
- 一、分配显示列表编号
- 二、创建显示列表
- 三、调用显示列表
- 四、销毁显示列表
- 举例
- 第九讲
- 启动混合
- 源因子与目标因子
- 二维图形混合举例
- 三维混合
- 第十讲
- 像素读取
- 像素绘制
- 像素复制
- 第十一讲
- 1、启用纹理和载入纹理
- 2、纹理坐标
- 3、纹理参数
- 4、纹理对象
- 5、示例
- 第十二讲
- 裁剪测试
- Alpha测试
- 举例
- 模板测试
- 举例
- 深度测试
OpenGL十二讲代码—by yjq
参考资料
OpenGL入门教程
【侵权立删】
第一讲
画一个矩形
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>void myDisplay(void)
{glClear(GL_COLOR_BUFFER_BIT);//glBegin(GL_POINTS);glBegin(GL_POLYGON);//单个简单填充多边形glVertex2f(0.0f, 0.0f);glVertex2f(0.5f, 0.0f);glVertex2f(0.5f, 0.5f);glVertex2f(0.0f, 0.5f);glEnd();//glRectf(-0.5f, -0.5f, 0.5f, 0.5f);glFlush();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("几何图形的绘制");glutDisplayFunc(&myDisplay);glutMainLoop();return 0;
}
第二讲
画一个五角星
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>const int n = 1000;
//修改const int n的值,观察当n=3,4,5,8,10,15,20,30,50等不同数值时输出的变化情况
const GLfloat R = 0.5f;
const GLfloat Pi = 3.1415926536;void myDisplay(void)
{//画一个五角星GLfloat a = 1 / (2 - 2 * cos(72 * Pi / 180));GLfloat bx = a * cos(18 * Pi / 180);GLfloat by = a * sin(18 * Pi / 180);GLfloat cy = -a * cos(18 * Pi / 180);GLfloatPointA[2] = { 0, a },PointB[2] = { bx, by },PointC[2] = { 0.5, cy },PointD[2] = { -0.5, cy },PointE[2] = { -bx, by };glClear(GL_COLOR_BUFFER_BIT);// 按照A->C->E->B->D->A的顺序,可以一笔将五角星画出glBegin(GL_LINE_LOOP);glVertex2fv(PointA);glVertex2fv(PointC);glVertex2fv(PointE);glVertex2fv(PointB);glVertex2fv(PointD);glEnd();glFlush();
}
int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("第二讲——画一个五角星");glutDisplayFunc(&myDisplay);glutMainLoop();return 0;
}
画一个圆
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>const int n = 1000;
//修改const int n的值,观察当n=3,4,5,8,10,15,20,30,50等不同数值时输出的变化情况
const GLfloat R = 0.5f;
const GLfloat Pi = 3.1415926536;void myDisplay(void)
{//画一个圆int i;glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_POLYGON);//单个简单填充多边形for (i = 0; i < n; i++) {glVertex2f(R * cos(2 * Pi / n * i), R * sin(2 * Pi / n * i));}glEnd();glFlush();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("第二讲——画一个圆");glutDisplayFunc(&myDisplay);glutMainLoop();return 0;
}
画一个正弦函数
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>const GLfloat factor = 0.1f;
void myDisplay(void)
{GLfloat x;glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_LINES);glVertex2f(-1.0f, 0.0f);glVertex2f(1.0f, 0.0f); // 以上两个点可以画x轴glVertex2f(0.0f, -1.0f);glVertex2f(0.0f, 1.0f); // 以上两个点可以画y轴glEnd();glBegin(GL_LINE_STRIP);for (x = -1.0f / factor; x < 1.0f / factor; x += 0.01f){glVertex2f(x * factor, sin(x) * factor);}glEnd();glFlush();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("第二讲——画一个正弦函数");glutDisplayFunc(&myDisplay);glutMainLoop();return 0;
}
第三讲
画点
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>void myDisplay(void)
{glClear(GL_COLOR_BUFFER_BIT);glPointSize(5.0f);glBegin(GL_POINTS);glVertex2f(0.0f, 0.0f);glVertex2f(0.5f, 0.5f);glEnd();glFlush();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("第三讲——画两个点");glutDisplayFunc(&myDisplay);glutMainLoop();return 0;
}
画虚线
pattern是由1和0组成的长度为16的序列,从最低位开始看,如果为1,则直线上接下来应该画的factor个点将被画为实的;如果为0,则直线上接下来应该画的factor个点将被画为虚的。
以下是一些例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F4bjJ6WD-1641975777776)(C:/Users/26969/AppData/Local/Temp/msohtmlclip1/01/clip_image001.gif)]
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>void myDisplay(void)
{glClear(GL_COLOR_BUFFER_BIT);glEnable(GL_LINE_STIPPLE);glLineStipple(2, 0x0F0F);glLineWidth(10.0f);glBegin(GL_LINES);glVertex2f(0.0f, 0.0f);glVertex2f(0.5f, 0.5f);glEnd();glFlush();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("第三讲——画虚线");glutDisplayFunc(&myDisplay);glutMainLoop();return 0;
}
多边形
多边形的两面
从三维的角度来看,一个多边形具有两个面。每一个面都可以设置不同的绘制方式:填充、只绘制边缘轮廓线、只绘制顶点,其中“填充”是默认的方式。可以为两个面分别设置不同的方式。
glPolygonMode(GL_FRONT, GL_FILL); // 设置正面为填充方式
glPolygonMode(GL_BACK, GL_LINE); // 设置反面为边缘绘制方式
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 设置两面均为顶点绘制方式
前后反转
可以通过glFrontFace函数来交换“正面”和“反面”的概念。
glFrontFace(GL_CCW); // 设置CCW方向为“正面”,CCW即CounterClockWise,逆时针glFrontFace(GL_CW); // 设置CW方向为“正面”,CW即ClockWise,顺时针
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>
void myDisplay(void)
{glClear(GL_COLOR_BUFFER_BIT);glPolygonMode(GL_FRONT, GL_FILL); // 设置正面为填充模式glPolygonMode(GL_BACK, GL_LINE); // 设置反面为线形模式glFrontFace(GL_CCW); // 设置逆时针方向为正面glBegin(GL_POLYGON); // 按逆时针绘制一个正方形,在左下方glVertex2f(-0.5f, -0.5f);glVertex2f(0.0f, -0.5f);glVertex2f(0.0f, 0.0f);glVertex2f(-0.5f, 0.0f);glEnd();glBegin(GL_POLYGON); // 按顺时针绘制一个正方形,在右上方glVertex2f(0.0f, 0.0f);glVertex2f(0.0f, 0.5f);glVertex2f(0.5f, 0.5f);glVertex2f(0.5f, 0.0f);glEnd();glFlush();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("第三讲——多边形正反面");glutDisplayFunc(&myDisplay);glutMainLoop();return 0;
}
剔除多边形表面
使用glEnable(GL_CULL_FACE);
来启动剔除功能(使用glDisable(GL_CULL_FACE
)可以关闭之)
然后,使用glCullFace来进行剔除。
glCullFace的参数可以是GL_FRONT,GL_BACK或者GL_FRONT_AND_BACK,分别表示剔除正面、剔除反面、剔除正反两面的多边形。
镂空多边形
使用glEnable(GL_POLYGON_STIPPLE);
来启动镂空模式(使用glDisable(GL_POLYGON_STIPPLE)
可以关闭之)。
然后,使用glPolygonStipple来设置镂空的样式。
void glPolygonStipple(const GLubyte*mask);
其中的参数mask指向一个长度为128字节的空间,它表示了一个32*32的矩形应该如何镂空。其中:第一个字节表示了最左下方的从左到右(也可以是从右到左,这个可以修改)8个像素是否镂空(1表示不镂空,显示该像素;0表示镂空,显示其后面的颜色),最后一个字节表示了最右上方的8个像素是否镂空。
黑色对应二进制零(镂空),白色对应二进制一(不镂空),编辑完毕后保存。
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>
void myDisplay(void)
{//镂空效果glClear(GL_COLOR_BUFFER_BIT);static GLubyte Mask[128];FILE* fp;fopen_s(&fp,"mask.bmp", "rb");if (!fp)exit(0);if (fseek(fp, -(int)sizeof(Mask), SEEK_END))exit(0);if (!fread(Mask, sizeof(Mask), 1, fp))exit(0);fclose(fp);glClear(GL_COLOR_BUFFER_BIT);glEnable(GL_POLYGON_STIPPLE);//启动剔除功能glPolygonStipple(Mask);glRectf(-0.5f, -0.5f, 0.5f, 0.5f); // 绘制一个有镂空效果的正方形 glFlush();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("第三讲——多边形镂空");glutDisplayFunc(&myDisplay);glutMainLoop();return 0;
}
第四讲
RGBA颜色
void myDisplay(void)
{glClear(GL_COLOR_BUFFER_BIT);glColor3ub(255,192,203);//粉色glRectf(-0.5f, -0.5f, 0.5f, 0.5f);glFlush();
}
glColor系列函数,在参数类型不同时,表示“最大”颜色的值也不同。
采用f和d做后缀的函数,以1.0表示最大的使用。
采用b做后缀的函数,以127表示最大的使用。
采用ub做后缀的函数,以255表示最大的使用。
采用s做后缀的函数,以32767表示最大的使用。
采用us做后缀的函数,以65535表示最大的使用。
在默认情况下,OpenGL会计算两点顶点之间的其它点,并为它们填上“合适”的颜色,使相邻的点的颜色值都比较接近。如果使用的是RGB模式,看起来就具有渐变的效果。如果是使用颜色索引模式,则其相邻点的索引值是接近的,如果将颜色表中接近的项设置成接近的颜色,则看起来也是渐变的效果。但如果颜色表中接近的项颜色却差距很大,则看起来可能是很奇怪的效果。
使用glShadeModel函数可以关闭这种计算,如果顶点的颜色不同,则将顶点之间的其它点全部设置为与某一个点相同。(直线以后指定的点的颜色为准,而多边形将以任意顶点的颜色为准,由实现决定。)为了避免这个不确定性,尽量在多边形中使用同一种颜色。
颜色索引
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>
#include <time.h>
#include "tex.h"#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glaux.lib")
#pragma comment(lib, "legacy_stdio_definitions.lib")const GLdouble Pi = 3.1415926536;
void myDisplay(void)
{int i;for (i = 0; i < 8; ++i)auxSetOneColor(i, (float)(i & 0x04), (float)(i & 0x02), (float)(i & 0x01));glShadeModel(GL_FLAT);glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_TRIANGLE_FAN);glVertex2f(0.0f, 0.0f);for (i = 0; i <= 8; ++i){glIndexi(i);glVertex2f(cos(i * Pi / 4), sin(i * Pi / 4));}glEnd();glFlush();
}int main(void)
{auxInitDisplayMode(AUX_SINGLE | AUX_INDEX);auxInitPosition(0, 0, 400, 400);auxInitWindow(L"");myDisplay();Sleep(10 * 1000);return 0;
}
颜色表
glShadeModel的使用方法:glShadeModel(GL_SMOOTH); // 平滑方式,这也是默认方式glShadeModel(GL_FLAT); // 单色方式
void myDisplay(void)
{int i;/* glShadeModel(GL_FLAT);*/glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_TRIANGLE_FAN);glColor3f(1.0f, 1.0f, 1.0f);glVertex2f(0.0f, 0.0f);for (i = 0; i <= 8; ++i){glColor3f(i & 0x04, i & 0x02, i & 0x01);glVertex2f(cos(i * Pi / 4), sin(i * Pi / 4));}glEnd();glFlush();
}
第五讲
从“相对移动”的观点来看,改变观察点的位置与方向和改变物体本身的位置与方向具有等效性。在OpenGL中,实现这两种功能甚至使用的是同样的函数。
由于模型和视图的变换都通过矩阵运算来实现,在进行变换前,应先设置当前操作的矩阵为“模型视图矩阵”。设置的方法是以GL_MODELVIEW为参数调用glMatrixMode函数,像这样:
glMatrixMode(GL_MODELVIEW);
通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。这也只需要一行代码:
glLoadIdentity();
然后,就可以进行模型变换和视图变换了。进行模型和视图变换,主要涉及到三个函数:
glTranslate*,把当前矩阵和一个表示移动物体的矩阵相乘。三个参数分别表示了在三个坐标上的位移值。
glRotate*,把当前矩阵和一个表示旋转物体的矩阵相乘。物体将绕着(0,0,0)到(x,y,z)的直线以逆时针旋转,参数angle表示旋转的角度。
glScale*,把当前矩阵和一个表示缩放物体的矩阵相乘。x,y,z分别表示在该方向上的缩放比例。
“先移动后旋转”和“先旋转后移动”得到的结果很可能不同,初学的时候需要特别注意这一点。
旋转的时候,坐标系统随着物体旋转。移动的时候,坐标系统随着物体移动。如此一来,就不需要考虑代码的顺序反转的问题了。
太阳月亮地球
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>// 太阳、地球和月亮
// 假设每个月都是30天
// 一年12个月,共是360天
static int day = 200; // day的变化:从0到359
void myDisplay(void)
{glDepthFunc(GL_ALWAYS);//总是绘制glEnable(GL_DEPTH_TEST);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(75, 1, 1, 400000000);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);// 绘制红色的“太阳”glColor3f(1.0f, 0.0f, 0.0f);glutSolidSphere(69600000, 20, 20);// 绘制蓝色的“地球”glColor3f(0.0f, 0.0f, 1.0f);glRotatef(day / 360.0 * 360.0, 0.0f, 0.0f, -1.0f);glTranslatef(150000000, 0.0f, 0.0f);glutSolidSphere(15945000, 20, 20);// 绘制黄色的“月亮”glColor3f(1.0f, 1.0f, 0.0f);glRotatef(day / 30.0 * 360.0 - day / 360.0 * 360.0, 0.0f, 0.0f, -1.0f);glTranslatef(38000000, 0.0f, 0.0f);glutSolidSphere(4345000, 20, 20);glFlush();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);int cx = glutGet(GLUT_SCREEN_WIDTH);int cy = glutGet(GLUT_SCREEN_HEIGHT);//为了使窗口居中glutInitWindowPosition((cx - 400) / 2, (cy - 400) / 2);glutInitWindowSize(400, 400);glutCreateWindow("第五讲——太阳月亮地球");glutDisplayFunc(myDisplay);glutMainLoop();return 0;
}
第六讲
太阳月亮地球加旋转
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>
#include<time.h>
static int day = 200; // day的变化:从0到359
double CalFrequency()
{static int count;static double save;static clock_t last, current;double timegap;++count;if (count <= 50)return save;count = 0;last = current;current = clock();timegap = (current - last) / (double)CLK_TCK;save = 50.0 / timegap;return save;
}//统计该函数自身的调用频率void myDisplay(void)
{double FPS = CalFrequency();printf("FPS = %f\n", FPS);glDepthFunc(GL_ALWAYS);//总是绘制glEnable(GL_DEPTH_TEST);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(75, 1, 1, 400000000);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);// 绘制红色的“太阳”glColor3f(1.0f, 0.0f, 0.0f);glutSolidSphere(69600000, 20, 20);// 绘制蓝色的“地球”glColor3f(0.0f, 0.0f, 1.0f);glRotatef(day / 360.0 * 360.0, 0.0f, 0.0f, -1.0f);glTranslatef(150000000, 0.0f, 0.0f);glutSolidSphere(15945000, 20, 20);// 绘制黄色的“月亮”glColor3f(1.0f, 1.0f, 0.0f);glRotatef(day / 30.0 * 360.0 - day / 360.0 * 360.0, 0.0f, 0.0f, -1.0f);glTranslatef(38000000, 0.0f, 0.0f);glutSolidSphere(4345000, 20, 20);glFlush();glutSwapBuffers();
}void myIdle(void)
{/* 新的函数,在空闲时调用,作用是把日期往后移动一天并重新绘制,达到动画效果 */++day;if (day >= 360)day = 0;myDisplay();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); // 修改了参数为GLUT_DOUBLEglutInitWindowPosition(100, 100);glutInitWindowSize(400, 400);glutCreateWindow("第六讲——太阳,地球和月亮"); // 改了窗口标题glutDisplayFunc(&myDisplay);glutIdleFunc(&myIdle); // CPU空闲的时间调用某一函数glutMainLoop();return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y5sA4PHu-1641975777778)(C:/Users/26969/Desktop/%E7%AC%AC%E5%85%AD%E8%AE%B2.gif)]
第七讲
太阳月亮加光照
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>
#include<time.h>
#define WIDTH 400
#define HEIGHT 400static GLfloat angle = 0.0f;void myDisplay(void)
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glClearColor(1.0, 1.0, 1.0,1.0);// 创建透视效果视图glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(90.0f, 1.0f, 1.0f, 20.0f);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(0.0, 5.0, -10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);// 定义太阳光源,它是一种白色的光源 {GLfloat sun_light_position[] = { 0.0f, 0.0f, 0.0f, 1.0f };//光源位置GLfloat sun_light_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; //多次反射后遗留光GLfloat sun_light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };//漫反射光GLfloat sun_light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };//镜面反射光glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);glLightfv(GL_LIGHT0, GL_AMBIENT, sun_light_ambient);glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_light_diffuse);glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);glEnable(GL_LIGHT0);glEnable(GL_LIGHTING);glEnable(GL_DEPTH_TEST);}// 定义太阳的材质并绘制太阳{GLfloat sun_mat_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };GLfloat sun_mat_diffuse[] = { 0.0f, 0.0f, 0.0f, 1.0f };GLfloat sun_mat_specular[] = { 0.0f, 0.0f, 0.0f, 1.0f };GLfloat sun_mat_emission[] = { 0.5f, 0.0f, 0.0f, 1.0f };GLfloat sun_mat_shininess = 0.0f;glMaterialfv(GL_FRONT, GL_AMBIENT, sun_mat_ambient);glMaterialfv(GL_FRONT, GL_DIFFUSE, sun_mat_diffuse);glMaterialfv(GL_FRONT, GL_SPECULAR, sun_mat_specular);glMaterialfv(GL_FRONT, GL_EMISSION, sun_mat_emission);glMaterialf(GL_FRONT, GL_SHININESS, sun_mat_shininess);glutSolidSphere(2.0, 40, 32);}// 定义地球的材质并绘制地球{GLfloat earth_mat_ambient[] = { 0.0f, 0.0f, 0.5f, 1.0f };GLfloat earth_mat_diffuse[] = { 0.0f, 0.0f, 0.5f, 1.0f };//类似于蓝色GLfloat earth_mat_specular[] = { 0.0f, 0.0f, 1.0f, 1.0f };GLfloat earth_mat_emission[] = { 0.0f, 0.0f, 0.0f, 1.0f };GLfloat earth_mat_shininess = 30.0f;glMaterialfv(GL_FRONT, GL_AMBIENT, earth_mat_ambient);glMaterialfv(GL_FRONT, GL_DIFFUSE, earth_mat_diffuse);glMaterialfv(GL_FRONT, GL_SPECULAR, earth_mat_specular);glMaterialfv(GL_FRONT, GL_EMISSION, earth_mat_emission);glMaterialf(GL_FRONT, GL_SHININESS, earth_mat_shininess);glRotatef(angle, 0.0f, -1.0f, 0.0f);glTranslatef(5.0f, 0.0f, 0.0f);glutSolidSphere(2.0, 40, 32);}glutSwapBuffers();
}
void myIdle(void)
{angle += 1.0f;if (angle >= 360.0f)angle = 0.0f;myDisplay();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); // 修改了参数为GLUT_DOUBLEglutInitWindowPosition(100, 100);glutInitWindowSize(WIDTH,HEIGHT);glutCreateWindow("第七讲——太阳,地球和月亮"); // 改了窗口标题glutDisplayFunc(&myDisplay);glutIdleFunc(&myIdle); // CPU空闲的时间调用某一函数glutMainLoop();return 0;
}
第八讲
一、分配显示列表编号
OpenGL允许多个显示列表同时存在,就好象C语言允许程序中有多个函数同时存在。C语言中,不同的函数用不同的名字来区分,而在OpenGL中,不同的显示列表用不同的正整数来区分。
你可以自己指定一些各不相同的正整数来表示不同的显示列表。但是如果你不够小心,可能出现一个显示列表将另一个显示列表覆盖的情况。为了避免这一问题,使用glGenLists函数来自动分配一个没有使用的显示列表编号。
glGenLists函数有一个参数i,表示要分配i个连续的未使用的显示列表编号。返回的是分配的若干连续编号中最小的一个。例如,glGenLists(3);如果返回20,则表示分配了20、21、22这三个连续的编号。如果函数返回零,表示分配失败。
可以使用glIsList函数判断一个编号是否已经被用作显示列表。
二、创建显示列表
创建显示列表实际上就是把各种OpenGL函数的调用装入到显示列表中。使用glNewList开始装入,使用glEndList结束装入。glNewList有两个参数,第一个参数是一个正整数表示装入到哪个显示列表。第二个参数有两种取值,如果为GL_COMPILE,则表示以下的内容只是装入到显示列表,但现在不执行它们;如果为GL_COMPILE_AND_EXECUTE,表示在装入的同时,把装入的内容执行一遍。
例如,需要把“设置颜色为红色,并且指定一个坐标为(0, 0)的顶点”这两条命令装入到编号为list的显示列表中,并且在装入的时候不执行,则可以用下面的代码:
glNewList(list, GL_COMPILE);glColor3f(1.0f, 0.0f, 0.0f);glVertex2f(0.0f, 0.0f);glEnd();
注意:显示列表只能装入OpenGL函数,而不能装入其它内容。例如:
int i = 3;glNewList(list, GL_COMPILE);if( i > 20 )glColor3f(1.0f, 0.0f, 0.0f);glVertex2f(0.0f, 0.0f);glEnd();
其中if这个判断就没有被装入到显示列表。以后即使修改i的值,使i>20的条件成立,则glColor3f这个函数也不会被执行。因为它根本就不存在于显示列表中。
另外,并非所有的OpenGL函数都可以装入到显示列表中。例如,各种用于查询的函数,它们无法被装入到显示列表,因为它们都具有返回值,而glCallList和glCallLists函数都不知道如何处理这些返回值。在网络方式下,设置客户端状态的函数也无法被装入到显示列表,这是因为显示列表被保存到服务器端,各种设置客户端状态的函数在发送到服务器端以前就被执行了,而服务器端无法执行这些函数。分配、创建、删除显示列表的动作也无法被装入到另一个显示列表,但调用显示列表的动作则可以被装入到另一个显示列表。
三、调用显示列表
使用glCallList函数可以调用一个显示列表。该函数有一个参数,表示要调用的显示列表的编号。例如,要调用编号为10的显示列表,直接使用glCallList(10);就可以了。
使用glCallLists函数可以调用一系列的显示列表。
该函数有三个参数,第一个参数表示了要调用多少个显示列表。
第二个参数表示了这些显示列表的编号的储存格式,可以是
GL_BYTE(每个编号用一个GLbyte表示),GL_UNSIGNED_BYTE(每个编号
用一个GLubyte表示),GL_SHORT,GL_UNSIGNED_SHORT,GL_INT,GL_UNSIGNED_INT,GL_FLOAT。
第三个参数表示了这些显示列表的编号所在的位置。在使用该函数前,需要用glListBase函数来设置一个偏移量。假设偏移量为k,且glCallLists中要求调用的显示列表编号依次为l1, l2, l3, …,则实际调用的显示列表为l1+k, l2+k, l3+k, …。
例如:
GLuint lists[] = {1, 3, 4, 8};glListBase(10);glCallLists(4, GL_UNSIGNED_INT, lists);
则实际上调用的是编号为11, 13, 14, 18的四个显示列表。
注:“调用显示列表”这个动作本身也可以被装在另一个显示列表中。
四、销毁显示列表
销毁显示列表可以回收资源。使用glDeleteLists来销毁一串编号连续的显示列表。
例如,使用glDeleteLists(20, 4);将销毁20,21,22,23这四个显示列表。
使用显示列表将会带来一些开销,例如,把各种动作保存到显示列表中会占用一定数量的内存资源。但如果使用得当,显示列表可以提升程序的性能。这主要表现在以下方面:
1、明显的减少OpenGL函数的调用次数。如果函数调用是通过网络进行的(Linux等操作系统支持这样的方式,即由应用程序在客户端发出OpenGL请求,由网络上的另一台服务器进行实际的绘图操作),将显示列表保存在服务器端,可以大大减少网络负担。
2、保存中间结果,避免一些不必要的计算。例如前面的样例程序中,cos、sin函数的计算结果被直接保存到显示列表中,以后使用时就不必重复计算。
3、便于优化。我们已经知道,使用glTranslate*、glRotate*、glScale*等函数时,实际上是执行矩阵乘法操作,由于这些函数经常被组合在一起使用,通常会出现矩阵的连乘。这时,如果把这些操作保存到显示列表中,则一些复杂的OpenGL版本会尝试先计算出连乘的一部分结果,从而提高程序的运行速度。在其它方面也可能存在类似的例子。
同时,显示列表也为程序的设计带来方便。我们在设置一些属性时,经常把一些相关的函数放在一起调用,(比如,把设置光源的各种属性的函数放到一起)这时,如果把这些设置属性的操作装入到显示列表中,则可以实现属性的成组的切换。
当然了,即使使用显示列表在某些情况下可以提高性能,但这种提高很可能并不明显。毕竟,在硬件配置和大致的软件算法都不变的前提下,性能可提升的空间并不大。
举例
显示列表的内容就是这么多了,下面我们看一个例子。
假设我们需要绘制一个旋转的彩色正四面体,则可以这样考虑:设置一个全局变量angle,然后让它的值不断的增加(到达360后又恢复为0,周而复始)。每次需要绘制图形时,根据angle的值进行旋转,然后绘制正四面体。这里正四面体采用显示列表来实现,即把绘制正四面体的若干OpenGL函数装到一个显示列表中,然后每次需要绘制时,调用这个显示列表即可。
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<glut.h>
#include<glaux.h>
#define WIDTH 400
#define HEIGHT 400#define ColoredVertex(c, v) do{ glColor3fv(c); glVertex3fv(v); }while(0)GLfloat angle = 0.0f;void myDisplay(void)
{static int list = 0;if (list == 0){// 如果显示列表不存在,则创建GLfloatPointA[] = { 0.5f, (GLfloat) (-sqrt(6.0f) / 12) , (GLfloat)(-sqrt(3.0f) / 6) },PointB[] = { -0.5f, (GLfloat)(-sqrt(6.0f) / 12) , (GLfloat)(-sqrt(3.0f) / 6) },PointC[] = { 0.0f, (GLfloat)(-sqrt(6.0f) / 12) , (GLfloat)(sqrt(3.0f) / 3) },PointD[] = { 0.0f, (GLfloat)(sqrt(6.0f) / 4), 0 };GLfloatColorR[] = { 1, 0, 0 },ColorG[] = { 0, 1, 0 },ColorB[] = { 0, 0, 1 },ColorY[] = { 1, 1, 0 };list = glGenLists(1);glNewList(list, GL_COMPILE);glBegin(GL_TRIANGLES);// 平面ABCColoredVertex(ColorR, PointA);ColoredVertex(ColorG, PointB);ColoredVertex(ColorB, PointC);// 平面ACDColoredVertex(ColorR, PointA);ColoredVertex(ColorB, PointC);ColoredVertex(ColorY, PointD);// 平面CBDColoredVertex(ColorB, PointC);ColoredVertex(ColorG, PointB);ColoredVertex(ColorY, PointD);// 平面BADColoredVertex(ColorG, PointB);ColoredVertex(ColorR, PointA);ColoredVertex(ColorY, PointD);glEnd();glEndList();glEnable(GL_DEPTH_TEST);}// 已经创建了显示列表,在每次绘制正四面体时将调用它glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glPushMatrix();glRotatef(angle, 1, 0.5, 0);glCallList(list);glPopMatrix();glutSwapBuffers();
}void myIdle(void)
{++angle;if (angle >= 360.0f)angle = 0.0f;myDisplay();
}int main(int argc, char* argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);glutInitWindowPosition(200, 200);glutInitWindowSize(WIDTH, HEIGHT);glutCreateWindow("第八讲——显示列表");glutDisplayFunc(&myDisplay);glutIdleFunc(&myIdle);glutMainLoop();return 0;
}
第九讲
启动混合
要使用OpenGL的混合功能,只需要调用:glEnable(GL_BLEND);即可。
要关闭OpenGL的混合功能,只需要调用:glDisable(GL_BLEND);即可。
源因子与目标因子
把将要画上去的颜色称为“源颜色”,把原来的颜色称为“目标颜色”。
OpenGL会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后相加,这样就得到了新的颜色。
下面用数学公式来表达一下这个运算方式。假设源颜色的四个分量(指红色,绿色,蓝色,alpha值)是(Rs, Gs, Bs, As),目标颜色的四个分量是(Rd, Gd, Bd, Ad),又设源因子为(Sr, Sg, Sb, Sa),目标因子为(Dr, Dg, Db, Da)。则混合产生的新颜色可以表示为:
Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da
当然了,如果颜色的某一分量超过了1.0,则它会被自动截取为1.0,不需要考虑越界的问题。
源因子和目标因子是可以通过glBlendFunc函数来进行设置的。**glBlendFunc有两个参数,前者表示源因子,后者表示目标因子。**这两个参数可以是多种值,下面介绍比较常用的几种。
GL_ZERO: 表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。GL_ONE: 表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。
除此以外,还有GL_SRC_COLOR(把源颜色的四个分量分别作为因子的四个分量)、GL_ONE_MINUS_SRC_COLOR、GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR等,前两个在OpenGL旧版本中只能用于设置目标因子,后两个在OpenGL旧版本中只能用于设置源因子。新版本的OpenGL则没有这个限制,并且支持新的GL_CONST_COLOR(设定一种常数颜色,将其四个分量分别作为因子的四个分量)、GL_ONE_MINUS_CONST_COLOR、GL_CONST_ALPHA、GL_ONE_MINUS_CONST_ALPHA。另外还有GL_SRC_ALPHA_SATURATE。
OpenGL十二讲代码相关推荐
- 高翔博士SLAMBOO2十二讲代码库中的三方库没有下载下来 ,需要手动对三方库单独下载的git的命令如下
高翔博士SLAMBOO2十二讲代码库中的三方库没有下载下来 git clone --recursive https://github.com/gaoxiang12/slambook2.git 需要手动 ...
- tf第十二讲:TextCNN做文本分类的实战代码
大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...
- 蓝桥杯第十二讲--图论【习题】
文章目录 前言 地牢大师 题目要求 思路分析 代码 全球变暖 题目要求 思路分析 代码 单链表 题目要求 思路分析 代码 大臣的旅费 题目要求 思路分析 代码 (vectorvectorvector ...
- 第五十二讲 DTS(设备树)
第五十二讲 DTS(设备树) 一.简介 随着硬件设备的种类逐年递增,板级platform平台设备文件越来越多.在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xx ...
- 趣谈网络协议笔记-二(第十二讲)
趣谈网络协议笔记-二(第十二讲) TCP协议(下):西行必定多妖孽,恒心智慧消磨难 前言 哈哈哈,越当我看刘超的通俗讲解,我就越感觉自己的无能.每次当我看了讲解之后,每次当我感觉到这个东西原来是这么简 ...
- 服务器系统报错kernel-power,第十二讲、Linux服务器操作系统1.ppt
第十二讲.Linux服务器操作系统1.ppt 4.1引导与关闭系统 4.1.1 GRUB引导器 GRUB简介 1)grub?是一个多重启动管理器.grub是GRand?Unified?Bootload ...
- 运筹学与最优化方法_[公开课]运筹学之线性规划算法十二讲
运筹学之线性规划算法十二讲 这是最美好的时代,同样带来最无助的希望:这是最丰富多彩的时代,思想同样的匮乏:这是最互联互通的时代,一样找不到自己和同路的人:这是最光明的时代,我们依然要经过黑暗的摸索和摸 ...
- 计算机基础函数运用,计算机应用基础第十二讲:EXCEL中函数的实际运用.doc
文档介绍: 计算机应用基础第十二讲:EXCEL中函数的实际运用.doc计算机应用基础第十二讲:EXCEL中函数的实际运用课 题EXCEL屮函数的实际运用课型多媒体课授课时间第20周教学目的实例分析,掌 ...
- python核心编程-Amy老师第十二讲作业内容
python核心编程-Amy老师第十二讲作业内容: 作业1 import randomclass ComputerNum:def __init__(self):self.__random_num = ...
最新文章
- 组播技术中IP地址到MAC地址的映射
- docker数据卷volume详解
- java2ee和java2se_Java知识:(2)JavaSE和JavaEE
- 晶体(crystal、无源晶振)两端电容取值计算
- iOS9系列专题三——应用瘦身
- php 怎么输出alert,php简单提示框alert封装函数
- 强口令检测(使用正则表达式)
- 局域网传输文件_WinXP系统电脑局域网传输文件的操作方法
- 2.PHP7内核剖析 --- SAPI
- zerorpc java_Zerorpc 支持暴露多个远程Api接口类
- 智慧城市热度不减 产业资本进军智能汽车相关领域
- Jmeter数据库连接(MYSQL)
- 全国省市区 mysql_2017全国省市区数据库【含三款数据库】
- 波束形成算法学习笔记之一(Endfire,broadside)
- 新手刚学js遇到的ie6问题
- Linux计算时间间隔
- 亚马逊云科技的AI新引擎,如何助力企业应对“乌卡时代”?
- Android 如何关闭Navigation Bar
- List(updated 2023.01.29)
- Windows + XMAPP + TestLink 初探