标签:include 1.0 OpenGL 0.0 代码 十二 像素 GL 绘制
文章目录
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);
GLfloat
PointA[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_DOUBLE
glutInitWindowPosition(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 400
static 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_DOUBLE
glutInitWindowPosition(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)
{
// 如果显示列表不存在,则创建
GLfloat
PointA[] = { 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 };
GLfloat
ColorR[] = { 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);
// 平面ABC
ColoredVertex(ColorR, PointA);
ColoredVertex(ColorG, PointB);
ColoredVertex(ColorB, PointC);
// 平面ACD
ColoredVertex(ColorR, PointA);
ColoredVertex(ColorB, PointC);
ColoredVertex(ColorY, PointD);
// 平面CBD
ColoredVertex(ColorB, PointC);
ColoredVertex(ColorG, PointB);
ColoredVertex(ColorY, PointD);
// 平面BAD
ColoredVertex(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。
标签:include,1.0,OpenGL,0.0,代码,十二,像素,GL,绘制 来源: https://blog.csdn.net/weixin_47692652/article/details/122456474
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。