考完试有点伤之心,遂发笔记于csdn:考前看了一套11年左右的卷子,以为差不多的内容,最多改一下几道题就行了;结果自己还是图样图森破,十年前的卷子参考价值有但不能只全依赖试卷的题目,还是要好好上课听讲做笔记,并进行复习。于是将课堂笔记与复习笔记记录在csdn,希望复习的同学们如果想考一个好成绩的话不要只依赖于复习课与往年试卷……(当然如果不求高分的话好好把复习课教的内容都吃透就行了T.T我没吃透,所以考试有点难绷)
目录
5. MakeParabolicSurface:数据生成与处理
一、依据《复习提要》形成的部分知识点整理
1. 基本概念
样条:样条是一根富有弹性的细木条或类似物,其两端连接着起固定作用的压铁。通过调整样条两端的压铁可以改变样条的形态,它是手工绘制自由曲线的一种工具。
样条曲线:沿着样条绘制的曲线称为样条曲线。样条曲线可以表示为参数多项式曲线或分段参数多项式曲线。
参数多项式曲线:
曲线拟合:给定一个点列,用该点列来构造曲线的方法称为曲线拟合。
曲线插值:已知曲线上的一个点列,求曲线上的其他点的方法称为曲线插值。
法向量:垂直于平面的直线所表示的向量为该平面的法向量。
单位法向量:长度为单位1的法向量。
观察面:视见体内的物体投影在观察面上。
视口:窗口映射到显示器上的区域。
视口变换:在窗口中定义一个像素矩形,最终将这个图像映射到这个矩阵中。
视点:观察者所处的位置。
正交(射)投影:投影线垂直于投影面,也称为平行投影。
透视投影:类似人眼所看东西的方式,遵循近大远小。
视见体:确定观察坐标系的可见范围。
镜面反射:光线的入射角和反射角相等。
漫反射:一束光打在物体的某个点上,光线会均匀的反射到各个不同的方向上去。
材质:(红绿蓝色)反射率。
颜色缓冲区:存储将用屏幕显示的颜色值,一般有两个颜色缓冲区,一个用于正在显示的颜色缓冲区,一个用于渲染写入的颜色缓冲区。
- 深度缓冲区:记录屏幕对应的每个像素的深度值,通过深度缓冲区可以进行深度测试,从而确定像素的遮挡关系,保证渲染正确。
消隐:在视点确定之后,将对象表面不可见的点线面消去。
颜色混合:颜色具有混合性,任何一种颜色可以用红、绿、蓝三原色按照不同比例混合来得到,三原色等量混合产生白光。
雾化:描述远处的物体看上去较为模糊的现象。
反走样:由于采样不充分重建后造成信息的失真叫走样,用于减少或消除这种现象称为反走样,消除锯齿现象,线会更宽,有更多采样点。
-
2. 计算
例如计算曲面z=f(x, y)在给定点(x0, y0, z0)的法向量,计算三角形的法向量,单位化法向量。
二、编程部分的笔记(以5texture.cpp为例)
源程序:5texture.cpp
1. main
int main(int argc, char** argv)
{
glutInit(&argc, argv); //初始化
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); //[!]或运算
glutInitWindowSize (500, 500); //窗口大小
glutInitWindowPosition (100, 100); //相对于屏幕左上角的位置
glutCreateWindow (argv[0]); //[!]消息 WM_RESIZE, WM_REPAINT
init (); //设置例如光源、材质等内容
//注册消息响应函数。以下四个函数的顺序变化无关紧要,起监听器/消息响应的作用
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
- glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH:颜色缓冲区 | 颜色模式 | 深度缓存区
颜色缓冲区有GLUT_SINGLE与GLUT_DOUBLE两种
GLUT_SINGLE单缓冲,屏幕显示调用glFlush(),将图像在当前显示缓存中直接渲染,会有图形跳动(闪烁)问题
GLUT_DOUBLE双缓冲,屏幕显示调用glutSwapBuffers(),将图像先绘制在另外的缓存中,渲染完毕之后,将其整个缓存贴到当前的窗口,能消除闪烁,一般动画要用双缓冲。
颜色模式有GLUT_RGB与GLUT_RGBA两种
RGBA分别表示红绿蓝三色的分量,A(Alpha)表示颜色的透明度。
或运算:这三个参数的顺序是可以调整的
实际上只传了一个参数(或运算处理之后的这个参数)
- glutCreateWindow (argv[0]);
发送两个消息:WM_RESIZE, WM_REPAINT
WM_RESIZE:窗口大小发生了变化
WM_REPAINT:重绘(重绘界面内容)
ps. 我们考查了这两个消息的回调函数是什么,,,我没查也不知道(小声),下次复习的时候可以注意一下(虽然没下次了应该)
- glutDisplayFunc(display);等
注册消息响应函数。以上四个函数的顺序变化无关紧要,起监听器/消息响应的作用。
2. reshape
void reshape (int w, int h)
{
m_clientWidth = w; //
m_clientHeight = h; //程序员获取客户区的宽和高
glViewport (0, 0, (GLsizei) w, (GLsizei) h); //作视口变换(坐标x,坐标y,w,h)
glMatrixMode (GL_PROJECTION); //投影矩阵
glLoadIdentity(); //单位化投影矩阵
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
reshape函数仅操作系统可以调用,程序员不可调用:因为w和h(客户区的宽度和高度)操作系统知道,而程序员不知道。内容主要为:视口-投影-模型视图矩阵。
- glViewport (0, 0, (GLsizei) w, (GLsizei) h);
作视口:在客户区内定义一个区域,该区域相对于客户区左下角的偏移位置作起始位置(在本例中偏移量为0,0 所以客户区的左下角就是视口的起始位置),该区域的宽和高分别为w和h,该区域即为视口。
隐含意:把观察面上的图像搬到视口上(即视口变换)。
- glMatrixMode (GL_PROJECTION);
glMatrixMode()函数的参数,这个函数其实就是对接下来要做什么进行一下声明,也就是在要做下一步之前告诉计算机我要对“什么”进行操作了,这个“什么”在glMatrixMode的“()”里的选项(参数)有3种模式: GL_PROJECTION 投影, GL_MODELVIEW 模型视图, GL_TEXTURE 纹理。
投影矩阵:用户坐标系到局部坐标系的坐标变化矩阵。
作图时使用的数据都是用户坐标系的数据,通过此投影矩阵,将用户坐标系的数据转变成局部坐标系的数据。
glOrtho函数将当前矩阵乘以正交矩阵,创建一个正交平行的视景体(一般用于物体不会因为离屏幕的远近而产生大小的变换的情况)
3. mouse
void mouse(int button, int state, int x, int y)
{
switch (button) {
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN) {
m_downPoint.x=x;
m_downPoint.y=y;
m_leftDown=1;
//修改红色分量
diffuseMaterial[0] += 0.1; //修改漫反射光的材质-变量
if (diffuseMaterial[0] > 1.0)
diffuseMaterial[0] = 0.0;
glColor4fv(diffuseMaterial); //修改环境光和漫反射光的材质-真正的修改并显示
/*
* glColor4fv(diffuseMaterial)修改什么,取决于init里设置了什么;在init里由于写了
* glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);glEnable(GL_COLOR_MATERIAL);
* 所以修改的是环境光和漫反射光
*/
glEnable(GL_COLOR_MATERIAL);
}
else
{
Point3I curPoint;
curPoint.x=x;
curPoint.y=y;
ProcessMoving(curPoint);
}
glutPostRedisplay(); //发送重绘消息
break;
case GLUT_MIDDLE_BUTTON:
if (state == GLUT_DOWN) {
//修改绿色分量
diffuseMaterial[1] += 0.1;
if (diffuseMaterial[1] > 1.0)
diffuseMaterial[1] = 0.0;
glColor4fv(diffuseMaterial);
glutPostRedisplay();
}
break;
case GLUT_RIGHT_BUTTON:
if (state == GLUT_DOWN) {
//m_transMode = (m_transMode + 1)%3; //add by cxg
flag = (flag + 1)%3; //控制纹理映射
//修改蓝色分量
diffuseMaterial[2] += 0.1;
if (diffuseMaterial[2] > 1.0)
diffuseMaterial[2] = 0.0;
glColor4fv(diffuseMaterial);
glutPostRedisplay();
}
break;
default:
break;
}
}
- glutPostRedisplay();
发送重绘消息。
- glColor4fv(diffuseMaterial);
真正修改材质的函数。有光照的情况下修改材质,无光照的情况下修改颜色。
材质(反射率)有三种:镜面光,漫反射光,环境光。该函数修改的具体是哪一种材质,要看此前init()函数设置了什么。由于init里写了
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL); glEnable(GL_COLOR_MATERIAL); //这两句通常一起用
所以修改的是环境光(GL_FRONT_AND_BACK)和漫反射光(GL_DIFFUSE)。
环境光可使得物体即使在背光情况下也能被看到,如下图:
4. init
void init(void)
{
int i=0;
GLfloat mat_specular[] = { 0.9, 0.9, 0.9, 1.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; //齐次坐标:(方向x,方向y,方向z,光的距离) 光的距离=0表示光在无穷远处
GLfloat ambient[] = { 0.4, 0.4, 0.4, 1.0 };
GLfloat lmodel_ambient[] = { 0.5, 0.5, 0.5, 1.0 };
glClearColor (0.9, 0.9, 0.9, 0.0);
glShadeModel (GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuseMaterial);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialf(GL_FRONT, GL_SHININESS, 25.0);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); //环境光和漫反射光的材质
glEnable(GL_COLOR_MATERIAL);
MakeParabolicSurface(); //抛物面数据生成
ComputeVertexNormal(); //局部化
MakeImage();
{ //add by cxg
m_leftDown=0;
m_rotation=0.0;
m_clip = 2;
for(i=0;i<3;i++)
{
m_axis[i]=1.0;
m_translate[i]=0.0;
}
//初始化旋转矩阵
for(i=0;i<16;i++)
{
m_rotMatrix[i]=0;
}
for(i=0;i<4;i++)
m_rotMatrix[i*4+i]=1;
m_transMode=ROTATE;
flag = 0;
}
}
- GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
1.0, 1.0, 1.0并非表示光源的笛卡尔坐标,而表示光的方向
光的距离=0.0,表示光在无穷远处。(齐次坐标作用之一)
齐次坐标:使得平移变化也能用矩阵表示。(作用之二)
5. MakeParabolicSurface:数据生成与处理
作抛物面z=x²+y²(实际使用了极坐标定义,即:x = rcosa, y = rsina, z = r²)
void MakeParabolicSurface()
{
for(int a = 0; a < 36; a++) //将360°十等分,一分为10° a 角度
{
for(int r = 0; r < 10; r++)
{
parabolicSurf[a][r][0] = (DELTA*r)*cos(a*PI2/36.0); //x = rcosa a*PI2/36.0 将度(2Π)换算为弧度(360°)
parabolicSurf[a][r][1] = (DELTA*r)*sin(a*PI2/36.0);//y = rsina
parabolicSurf[a][r][2] = (DELTA*r)*(DELTA*r);//z = r^2
}
}
}
6. Normal:计算曲面上某点的法向量
void Normal(double a[3], double b[3], double c[3], double n[3])
{
int i;
double p[3], q[3];
for(i = 0; i < 3; i++)
{
p[i] = b[i] - a[i]; //p = AB
q[i] = c[i] - a[i]; //q = AC
}
n[0] = p[1]*q[2] - p[2]*q[1];
n[1] = p[2]*q[0] - p[0]*q[2];
n[2] = p[0]*q[1] - p[1]*q[0];
double t = n[0]*n[0] + n[1]*n[1] + n[2]*n[2];
t = sqrt(t); //计算法向量长度
for(i = 0; i < 3; i++)
n[i] /= t; //将法向量单位化 此处应先作if(t!=0)的检查
}
- 叉积计算
n[0] = p[1]*q[2] - p[2]*q[1];
n[1] = p[2]*q[0] - p[0]*q[2];
n[2] = p[0]*q[1] - p[1]*q[0];
相当于n = pxq。(注意叉积顺序)
三、课堂记录的部分笔记
- 宏定义
函数调用时,指令指针跳转到函数所在的存储空间读取指令进行操作,需要保存现场、恢复现场(汇编-底层);而宏定义时进行了函数代码的拷贝, 因此不需要保存-转移-恢复现场。宏定义虽然浪费了空间,但节省了时间。(宏定义不做类型检查) 若需要反复调用此函数,且注重时间效率时,使用宏定义。
- glClear (GL_COLOR_BUFFER_BIT);
画图之前应清除颜色:清除颜色缓存区
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
三维的glClear:清除颜色缓存区和深度缓存区
- glColor3f (1.0, 1.0, 1.0);
无光照时,表示本身的颜色
- glLineStipple (1, 0x0101); /* dotted */ //1 = 实线
0x0101 = 0101 H = 0000 0001 0000 0001 B 背景色 前景色 背景色 背景色 (虚实虚实)
- 虽然然点的绘制与极短的线相同,但是旋转等操作时点总是朝着某个方向:潮汐
- glNewList(1, GL_COMPILE);
新建显示列表,表示加快运行速度的一种技术:预处理-作图。
- 内存泄漏:向系统申请内存,但使用后不归还给系统,导致内存空间不足(C++);而Java具有虚拟机JVM,有自动垃圾回收机制,会检测内存是否被使用,但时间效率不如C++;例如:大整数分解时不可用Java(速度过慢);而制作网站时可以使用Java(时间效率主要取决与网站的信息收集等) 。
- 显示列表可绘图,其作用是:加快绘图速度 。
- 马赫带效应:当观察两块亮度不同的区域时,边界处亮度对比加强,使轮廓表现得特别明显。这意味着所以在给球“着色”时,真正的法向量会使得球看上去不光滑。所以需要使用一些方法计算法向量以绘图(例如线性插值法)。
- 用户坐标系与局部坐标系:初始时,局部坐标系与用户坐标系相同;用户坐标系通过变换矩阵A成为局部坐标系。坐标变换都相对于局部坐标系。
- 齐次坐标系的作用:使得平移变换也可以用矩阵表示;可以表示无穷远的光点。
- 图片分为点阵与矢量两种。点阵:背景色、前景色、标上点的颜色;矢量:放大后不失真,控制点、计算点,但花费时间。