数字化方法基础_课程内容详解
Chapter1 VisualStudio2010 Express如何创建新工程
1、新建一个win32 Console Application工程,选择建立一个空项目(带预编译头的也可以,只不过大多数人不太习惯)
2、在左侧解决方案资源管理器中右击Source-add-New item,创建一个C++文件
3、这样就用VS2010创建好了一个简单的工程!
Chapter2 SB-WinSRC的使用方法
1、解压压缩包,得到一个SB-WinSRC文件夹
2、打开SB-WinSrc\examples\projects\microsoft\chapt05\shinyjet文件夹中的shinyjet.vcproj文件,如出现以下对话框则一直Next到最后
3、打开shinyjet.cpp并进行编译(Build solution生成解决方案)
4、出现图示错误
5、打开目录\SB-WinSrc\examples\src\shared,找到freeglut_static.lib文件,将它复制到之前打开的shinyjet文件夹内
6、再次进行编译(Build solution生成解决方案),出现下图错误
7、在左侧Solution Explorer(解决方案资源管理器)中右击shinyjet打开属性,将Linker-Input-忽略特定默认库一栏中输入LIBC.lib
8、再次编译,成功!
Chapter3 用OpenGL生成四面体
已知3点求法向量
1、具体思路为先根据已知3点做差求出两个向量,利用两个向量叉乘运算求出法向量,实现过程中尽量避免将所有代码集中到一个函数中,因为后续的操作(如投影点的计算)还需用到求法向量的函数,到时可直接调用。
2、已知2点求向量的函数十分简单,代码如下
void getvector(float a[3],float b[3],float vec[3])
{
for(int i = 0;i < 3;i++)
vec[i] = a[i] - b[i];
}
3、调用两次上述函数即可获得两个向量,接下来要做的就是拿这两个向量进行叉乘,以得到法向量
//函数中的法向量n[3]是提前在函数外定义的,通过调用函数给n[3]赋值
void crossproject(float vec1[3],float vec2[3],float n[3])
{
n[0] = vec1[1]*vec2[2]-vec1[2]*vec2[1];
n[1] = vec1[2]*vec2[0]-vec1[0]*vec2[2];
n[2] = vec1[0]*vec2[1]-vec1[1]*vec2[0];
}
4、将上面两个函数简单封装一下如下
//函数中的法向量n[3]是提前在函数外定义的,通过调用函数给n[3]赋值
void project(float point1[3],float b[3],float c[3],float n[3])
{
float vec1[3],vec2[3];
getvector(a,b,vec1);
getvector(b,c,vec2);
crossproject(vec1,vec2,n);
}
5、这样一个求法向量的函数就写好了,使用方法如下例
double a[3] = {1.0,0.0,0.0};
double b[3] = {0.0,1.0,0.0};
double c[3] = {0.0,0.0,0.0};
double n[3];
project(a,b,c,n);
//调用过project()函数之后,n[3]数组内的值即为法向量
生成四面体
1、使用OpenGL生成四面体的基本方法为,给定三个点和一个法向量,调用OpenDL的库函数,即可生成一个由这三点围成的三角形平面,四个三角形平面即可组成一个四面体。
2、将课上的shinyjet.cpp模板复制到src相应的文件夹中(\SB-WinSrc\examples\src\chapt05\shinyjet),然后回到project对应文件夹中,打开shinyjet.vcxproj工程,点击调试,成功后出来的应该为一个蓝绿色底的对话框。
3、在RenderSenen()函数中的下图位置 写入glBegin()与glEnd()函数,并在二者之间插入画三角形的代码。
float rgfPoints4[12] = {-0.6f,-0.6f,-0.6f,
0.0f,1.0f,0.0f,
1.0f,0.0f,0.0f,
0.0f,0.0f,1.0f};
//这是定义了一个长为12的数组,每3个元素代表一个点坐标,共4个点
glColor3ub(255,255,0);
//设置要生成图形的颜色
glBegin(GL_TRIANGLES);
//开始生成三角形
DrawTriangle(rgfPoints4,rgfPoints4+3,rgfPoints4+6);
DrawTriangle(rgfPoints4,rgfPoints4+9,rgfPoints4+3);
DrawTriangle(rgfPoints4+3,rgfPoints4+9,rgfPoints4+6);
DrawTriangle(rgfPoints4,rgfPoints4+6,rgfPoints4+9);
//↑函数功能:给定3个点生成一个三角形,调用4次生成4个三角形组成四面体
glEnd();
//结束
4、在这里,glColor3ub、glBegin,glEnd均是OpenGL的库函数,不需要我们定义,直接调用即可,需要我们写的是DrawTriangle函数,接下来我们就开始定义DrawTriangle(),前面已经提到,需要用3个点和一个法向量来确定一个平面,因此我们把第一节中生成法向量的函数复制到此文件的开头处,以便调用。
5、画三角形的函数如下:
void DrawTriangle(float a[3],float b[3],float c[3])
{
float n[3]; //定义一个数组用来存放法向量
project(a,b,c,n); //调用生成法向量的函数由a,b,c三点生成法向量n
glNormal3fv(n);
glVertex3fv(a);
glVertex3fv(b);
glVertex3fv(c); //此四行为利用用库函数,由法向量n和三个点abc生成一个平面
}
5、完成上述步骤后,进行调试,即可得到一个四面体
Chapter4 用OpenGL生成点的投影
计算点的投影的基本原理
如何编写程序实现点的投影
1、我们要实现点的投影就要知道投影点的坐标,由上一节可知,需要计算P0P1矢量(这个直接调用上一讲求向量的函数),en向量(需要写一个单位化函数),向量点乘的函数
2、单位化,即将向量各个坐标除以它的模,函数如下
void Normalize(float n[3])
{
float length;
length = sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
//求向量的模
for(int i = 0;i < 3;i++)
n[i] /= length;
//函数执行过后n[3]即变成单位法向量
}
3、进行向量点乘计算,并求N点坐标
void ProjectPointtoPoint(float point[3],float a[3],float n[3], float ProjectPoint[3])
{
float vector_a_p[3];
float distance;
for(int i = 0;i < 3;i++)
vector_a_p[i] = point[i] - a[i];
//求面内一点与面外一点的向量,即P0P1
distance = vector_a_p[0]*n[0]+vector_a_p[1]*n[1]+vector_a_p[2]*n[2];
//做点乘运算求点到平面距离,即图中|P0N|
for(int j = 0;j < 3;j++)
ProjectPoint[j] = point[j] - n[j]*distance;
//N点坐标=P0坐标 - en * |P0N|
}
4、经过上述步骤之后就获得投影点坐标,然后就可调用库函数显示投影点,以下为显示一个点的函数
void DrawPoint(float a[3],float b[3],float c[3],float point[3])
{
float n[3];
project(a,b,c,n);
//求abc平面法向量
Normalize(n);
//单位化法向量
float ProjectPoint[3];
//定义一个数组用来存放投影点坐标
ProjectPointtoPoint(point,a,n,ProjectPoint);
//获得投影点坐标
glVertex3fv(ProjectPoint);
//显示投影点
}
5、同三角形,在RenderSenen()函数中的画三角形的glEnd()后面 再次写入glBegin()与glEnd()函数,并在二者之间插入显示点的代码。
float point[3] = {0.0f,0.0f,0.0f};
//定义要投影的点
glColor3ub(0,0,0);
//显示的点的颜色
glPointSize(6.0f);
//显示的点的大小
glBegin(GL_POINTS);
//开始生成点
DrawPoint(rgfPoints4,rgfPoints4+3,rgfPoints4+6,point);
//根据第一个面3个点,画第一个投影点
DrawPoint(rgfPoints4,rgfPoints4+9,rgfPoints4+3,point);
DrawPoint(rgfPoints4+3,rgfPoints4+9,rgfPoints4+6,point);
DrawPoint(rgfPoints4,rgfPoints4+6,rgfPoints4+9,point);
glEnd();
6、调试成功,显示如图
Chapter5 如何使用VS2010的调试功能
假如我写完程序调试后发现点没有显示,那么可以一步步调试,找出错误的地方。
1、设置断点,在觉得可能出问题的代码处设置断点
2、点击调试,下图红框内左面按键为单步执行,点一下执行一句话,如果遇到函数就进入函数内部执行函数体的第一句;右面的按键,点一下执行一句,在遇到函数是直接将整个函数执行完,即将函数也当成一句话。
3、这里选择左面按键进入函数内部查看,黄色箭头表示当前执行到哪一句
4、按第二个按键将这个函数执行完(但不要退出这个函数,否则函数内部的变量内存会被释放,无法查看变量的值)
5、此时可以看到下面的监视窗口可以看到变量,单击+即可看到变量的值,图中展开的为第一个面的法向量和投影点坐标
第二个面的法向量和投影点坐标
第三个面
第四个
Chapter 6 导入本地模型
图形的生成需要消耗一定的时间,简单的模型可能没有什么感觉,但是在模型十分复杂时,模型的生成就需要相当长的时间,这是我们不能忍受的。因此,将模型保存为本地文件,使用时直接加载进来,这就变得十分必要了,本节主要讲如何将创建一个列表以及如何加载一个列表。
如何创建一个列表
列表的基本原理就是将之前写的从glPolygonMode、glBegin开始,到glEnd将这些代码用一行glCallList(DrawList)代替,其中DrawList内存放的就是之前生成四面体的代码了。glCallList就相当于把原来的四面体代码加载进来。
那如何将原来的四面体代码创建为一个列表供glCallList读取呢,过程十分简单,只需要按以下步骤即可:
1、在整个文件开头部分定义一个GLuint类型的全局变量(因为这个变量要在不同的函数使用,故须定义为全局变量)
GLuint DrawList;
2、将RenderScene函数(就是之前写glbegin和glEnd的地方)中的有关三角形的代码全部用glCallList函数代替
3、在SetupRC函数中的最后新建列表,框架如下
4、框架写好之后在图中注释位置插入画四面体的代码,插入后结果如下图
5、至此一个存有四面体列表就新建好了,点击调试运行即可看到和以前一样的四面体。
如何读取本地模型
当我们需要读取本地文件中的模型时,如何操作呢
1、既然要读取文件中的模型,首先肯定要打开文件,在创建列表的代码之前插入如下三行代码
(注意,if stream的括号内为文件的路径,其中的\都要写成\,因为在C语言字符串中,\表示转义,\\才表示一个\)
(注意,ifstream若要使用需要先在开头插入以下两行引入头文件同时设置环境)
#include<fstream>
using namespace std;
2、在定义几个数组用来存放一会要读取的数据
3、然后,将之前画四面体的代码,更改为读取文件的代码,更换后的框架如下,其中in每次读取一串字符(到空格或换行停止),in >> String0指的是将读取到的字符存入String0中
DrawList = glGenLists(1);
glNewList(DrawList,GL_COMPILE);
glPolygonMode(GL_BACK,GL_LINE);
//在↓插入代码
in >> String0 >> String0;//这就表示将两个字符串先后存入到String0中
//因为如下图在读取到有用数据之前有两个没用的单词,需要读取两次
while(strcmp(String0,"end"))//读到的字符串为end则退出循环
{
in >> Points[0] >> Points[1] >> Points[2];
//因为刚才已经读掉了前两个没用的字符串,因此直接读取三个坐标到Points里
in >> String0 >> Points[3] >> Points[4] >> Points[5];
//如下图读完第一组坐标后会遇到 vertex这个单词,需要读到垃圾桶(String0)里再读坐标
in >> String0 >> Points[6] >> Points[7] >> Points[8];
//同上
glColor3ub(200,200,2);
glBegin(GL_TRIANGLES);
DrawTriangle(Points,Points+3,Points+6);
//这三行时画一个三角形,根据刚才读到的三个点
}
//在↑插入代码
glEndList();
(因为用到了strcmp函数,需要引入头文件#include<string.h>)
注意一下,计算机里的图像无论是平面还是曲面,都是由无数个三角形组成的,只不过三角形数量无比多时,我们看起来它就是一个曲面,Part1.TXT文件中也是,每读取到三个点就画一个三角形,许许多多个三角形就会组成一个立体图形。
4、至此,如何从文件中导入立体模型就完成了,点击调试,即可看到一个正方体
6、那么对于给定的STL文件如何读取?首先右击Part2.STL,用记事本打开,看到文件内容如下
这看起来和之前差不多,只是他多给了一个法向量,没用的字符串多了一些而已,你可以不用他给的法向量,只读取三个坐标然后自己计算法向量,也可以读取法向量,这个时候 DrawTriangle函数就不需要计算法向量了,直接四行代码就ok,和之前一样,每一次in >> String0操作就读掉一个没用的字符串,自己编写代码就可实现将STL文件中的所有点全部读取出来
需要注意的是,每次循环结束的时候都要保证String内存放的是facet或者最后的endsolid这个单词,以保证循环可以正常退出
void DrawTriangle(float a[3],float b[3],float c[3],float n[3])
{
//float n[3];
//project(a,b,c,n);
glNormal3fv(n);
glVertex3fv(a);
glVertex3fv(b);
glVertex3fv(c);
}
PS.如果程序出现错误,如何进行调试呢,首先在while循环里第一句前面设置断点,如下图
然后点击单步执行第二个按钮
每次循环读9个点,查看你读取到的点的值是否与文件中的坐标值一一对应,其中012对应第一行3个点,345对应第二行三个点,678对应第三行。
Chapter 7 矩阵操作
利用矩阵实现向量平移
1、基本原理:如图,任意给定一个点的坐标(列向量)x,y,z。设置一个矩阵,利用矩阵的乘法运算即可将三个坐标进行平移
注意:过程中所设置的矩阵为单位阵的最后一列加上偏移量Tx,Ty,Tz。如下图,大家自己试一下矩阵乘法即可验证。
2、在了解了如何将一个列向量进行平移之后,我们就可以编写程序进行向量的平移操作了,我们打开生成螺旋线的程序,注意到螺旋线是由许许多多点组成的,下面这个for循环就是每次生成一个点,我们只需要吧每个点的坐标向量进行平移即可使整个螺旋线平移。
3、现在就开始写程序了,首先明确一下程序执行过程
1. 获得一个点的坐标存入P0数组内
2. 设置一个矩阵Translation用来将坐标平移
3. 将上述两个矩阵相乘得到的结果存入P1数组内,此即为平移后的点的坐标
4、第一步,获得一个点的坐标存入P0数组内,这一步十分简单(注:除函数定义外,其余代码均在for循环内)float P0[3] = {x,y,z};
5、第二步,设置一个矩阵Translation用来将坐标平移,我们需要一个下面这样的矩阵
如何操作呢,首先先初始化一个单位矩阵,然后将单位矩阵的最后一列赋值为需要偏移的量(我的代码十分简单粗暴,当然也可单独另写一个函数用于初始化一个单位矩阵)
注意:在OpenGL中,矩阵是按列数的,就是说我定义的I[16]中的前四个元素I[1]、I[2]、I[3]、I[4]实际上是矩阵的第一列,最后的I[12]、I[13]、I[14]、I[15]是矩阵的最后一列,而非上学期C语言中理解的最后一行
void Translate(float fx,float fy,float fz,float Translation[16])
{
float I[16] = {1.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f,0.0f,0.0f,0.0f,1.0f};//定义一个四阶单位阵
I[12] = fx;//将第四列第一行的元素赋fx
I[13] = fy;//第四列第二行赋fy
I[14] = fz;//第四列第三行赋fz
for(int i = 0;i < 16;i++)
Translation[i] = I[i];//将I数组的值循环赋给Translation数组
}
这样操作之后就获得了如上面图片中的数组了。
6、第三步,将上述两个矩阵相乘得到的结果存入P1数组内,此即为平移后的点的坐标,So我们要做的就是定义一个P1数组float P1[3];
这十分简单,然后就是写一个矩阵乘法的运算,用Translation*P0,结果存入P1中。
实现代码如下,注意translation为4*4矩阵,P0为3*1矩阵,P1为4*1矩阵,
(为什么要四阶矩阵是因为我们需要矩阵运算平移,只有多加一行一列才能实现,而P0和P1我们实际只用前3个元素,)
故我们让P0的“第四个”元素默认为1,即下面代码中最后一项为1*translation[i+12]。
void ApplyMatrix(float *P0,float *translation,float *P1)
{
for(int i = 0;i < 3;++i)
P1[i] = P0[0]*translation[i]+P0[1]*translation[i+4]+P0[2]*translation[i+8]+translation[i+12];
}
7、有了设置操作矩阵的函数Translate,和矩阵相乘的函数ApplyMatrix,我们就可以平移点了。
for(angle = 0.0f; angle <= (2.0f*GL_PI)*3.0f; angle += 0.1f)
{
x = 50.0f*sin(angle);
y = 50.0f*cos(angle);
// Specify the point and move the Z value up a little
glVertex3f(x, y, z);
float P0[3] = {x,y,z}; //定义P0存放平移之前的点
float P1[3]; //定义P1存放平移之后的点
float Translation[16]; //存放一个4*4的操作矩阵
Translate(0.0f,30.0f,0.0f,Translation);
//设置操作矩阵为我们想要的格式(单位阵->最后一列赋值)
ApplyMatrix(P0,Translation,P1);
//操作矩阵和P0点相乘,结果放在P1内
glVertex3f(P1[0], P1[1], P1[2]);
//显示平移之后的点
z += 0.5f;
}
以上为螺旋线平移步骤
Continue……