文章目录

  • 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十二讲代码相关推荐

  1. 高翔博士SLAMBOO2十二讲代码库中的三方库没有下载下来 ,需要手动对三方库单独下载的git的命令如下

    高翔博士SLAMBOO2十二讲代码库中的三方库没有下载下来 git clone --recursive https://github.com/gaoxiang12/slambook2.git 需要手动 ...

  2. tf第十二讲:TextCNN做文本分类的实战代码

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

  3. 蓝桥杯第十二讲--图论【习题】

    文章目录 前言 地牢大师 题目要求 思路分析 代码 全球变暖 题目要求 思路分析 代码 单链表 题目要求 思路分析 代码 大臣的旅费 题目要求 思路分析 代码 (vectorvectorvector ...

  4. 第五十二讲 DTS(设备树)

    第五十二讲 DTS(设备树) 一.简介 随着硬件设备的种类逐年递增,板级platform平台设备文件越来越多.在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xx ...

  5. 趣谈网络协议笔记-二(第十二讲)

    趣谈网络协议笔记-二(第十二讲) TCP协议(下):西行必定多妖孽,恒心智慧消磨难 前言 哈哈哈,越当我看刘超的通俗讲解,我就越感觉自己的无能.每次当我看了讲解之后,每次当我感觉到这个东西原来是这么简 ...

  6. 服务器系统报错kernel-power,第十二讲、Linux服务器操作系统1.ppt

    第十二讲.Linux服务器操作系统1.ppt 4.1引导与关闭系统 4.1.1 GRUB引导器 GRUB简介 1)grub?是一个多重启动管理器.grub是GRand?Unified?Bootload ...

  7. 运筹学与最优化方法_[公开课]运筹学之线性规划算法十二讲

    运筹学之线性规划算法十二讲 这是最美好的时代,同样带来最无助的希望:这是最丰富多彩的时代,思想同样的匮乏:这是最互联互通的时代,一样找不到自己和同路的人:这是最光明的时代,我们依然要经过黑暗的摸索和摸 ...

  8. 计算机基础函数运用,计算机应用基础第十二讲:EXCEL中函数的实际运用.doc

    文档介绍: 计算机应用基础第十二讲:EXCEL中函数的实际运用.doc计算机应用基础第十二讲:EXCEL中函数的实际运用课 题EXCEL屮函数的实际运用课型多媒体课授课时间第20周教学目的实例分析,掌 ...

  9. python核心编程-Amy老师第十二讲作业内容

    python核心编程-Amy老师第十二讲作业内容: 作业1 import randomclass ComputerNum:def __init__(self):self.__random_num = ...

最新文章

  1. 组播技术中IP地址到MAC地址的映射
  2. docker数据卷volume详解
  3. java2ee和java2se_Java知识:(2)JavaSE和JavaEE
  4. 晶体(crystal、无源晶振)两端电容取值计算
  5. iOS9系列专题三——应用瘦身
  6. php 怎么输出alert,php简单提示框alert封装函数
  7. 强口令检测(使用正则表达式)
  8. 局域网传输文件_WinXP系统电脑局域网传输文件的操作方法
  9. 2.PHP7内核剖析 --- SAPI
  10. zerorpc java_Zerorpc 支持暴露多个远程Api接口类
  11. 智慧城市热度不减 产业资本进军智能汽车相关领域
  12. Jmeter数据库连接(MYSQL)
  13. 全国省市区 mysql_2017全国省市区数据库【含三款数据库】
  14. 波束形成算法学习笔记之一(Endfire,broadside)
  15. 新手刚学js遇到的ie6问题
  16. Linux计算时间间隔
  17. 亚马逊云科技的AI新引擎,如何助力企业应对“乌卡时代”?
  18. Android 如何关闭Navigation Bar
  19. List(updated 2023.01.29)
  20. Windows + XMAPP + TestLink 初探

热门文章

  1. 中山大学新华学院计算机,中山大学新华学院信息科学学院电子信息科学与技术、计算机科学与技术、软件...
  2. Beta 测试和 Alpha 测试有什么区别?
  3. VisualSvn破解(VS2019)
  4. unity 动态图集
  5. 电脑派位系统(新生入学摇号) v2016
  6. Cython基础--Cython的类型
  7. RK3399 Android7.1修改系统默认壁纸
  8. oracle dba技术培训视频分享
  9. 【Linux】工具(3)——gcc/g++
  10. verilog基础---always