数字化方法基础教程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44543463/article/details/102650117

Chapter1 VisualStudio2010 Express如何创建新工程

1、新建一个win32 Console Application工程,选择建立一个空项目(带预编译头的也可以,只不过大多数人不太习惯)AltAlt
2、在左侧解决方案资源管理器中右击Source-add-New item,创建一个C++文件Alt
3、这样就用VS2010创建好了一个简单的工程!

Chapter2 SB-WinSRC的使用方法

1、解压压缩包,得到一个SB-WinSRC文件夹
2、打开SB-WinSrc\examples\projects\microsoft\chapt05\shinyjet文件夹中的shinyjet.vcproj文件,如出现以下对话框则一直Next到最后
Huffie
3、打开shinyjet.cpp并进行编译(Build solution生成解决方案)
Huffie
4、出现图示错误
Huffie
5、打开目录\SB-WinSrc\examples\src\shared,找到freeglut_static.lib文件,将它复制到之前打开的shinyjet文件夹内
Huffie
Huffie
6、再次进行编译(Build solution生成解决方案),出现下图错误
Huffie
7、在左侧Solution Explorer(解决方案资源管理器)中右击shinyjet打开属性,将Linker-Input-忽略特定默认库一栏中输入LIBC.lib
Huffie
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工程,点击调试,成功后出来的应该为一个蓝绿色底的对话框。
Huffie
Huffie
3、在RenderSenen()函数中的下图位置 写入glBegin()与glEnd()函数,并在二者之间插入画三角形的代码Huffie

	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个点和一个法向量来确定一个平面,因此我们把第一节中生成法向量的函数复制到此文件的开头处,以便调用。
Huffie
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、完成上述步骤后,进行调试,即可得到一个四面体
Huffie

Chapter4 用OpenGL生成点的投影

计算点的投影的基本原理

Huffie

如何编写程序实现点的投影

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、调试成功,显示如图
Huffir

Chapter5 如何使用VS2010的调试功能

假如我写完程序调试后发现点没有显示,那么可以一步步调试,找出错误的地方。
1、设置断点,在觉得可能出问题的代码处设置断点
Huffie
2、点击调试,下图红框内左面按键为单步执行,点一下执行一句话,如果遇到函数就进入函数内部执行函数体的第一句;右面的按键,点一下执行一句,在遇到函数是直接将整个函数执行完,即将函数也当成一句话。
Huffie
3、这里选择左面按键进入函数内部查看,黄色箭头表示当前执行到哪一句
Huffie
4、按第二个按键将这个函数执行完(但不要退出这个函数,否则函数内部的变量内存会被释放,无法查看变量的值)
Huffie
5、此时可以看到下面的监视窗口可以看到变量,单击+即可看到变量的值,图中展开的为第一个面的法向量和投影点坐标
Huffie
第二个面的法向量和投影点坐标
Huffie
第三个面
Huffie
第四个
Huffie

Chapter 6 导入本地模型

图形的生成需要消耗一定的时间,简单的模型可能没有什么感觉,但是在模型十分复杂时,模型的生成就需要相当长的时间,这是我们不能忍受的。因此,将模型保存为本地文件,使用时直接加载进来,这就变得十分必要了,本节主要讲如何将创建一个列表以及如何加载一个列表。

如何创建一个列表

列表的基本原理就是将之前写的从glPolygonMode、glBegin开始,到glEnd将这些代码用一行glCallList(DrawList)代替,其中DrawList内存放的就是之前生成四面体的代码了。glCallList就相当于把原来的四面体代码加载进来。
那如何将原来的四面体代码创建为一个列表供glCallList读取呢,过程十分简单,只需要按以下步骤即可:
1、在整个文件开头部分定义一个GLuint类型的全局变量(因为这个变量要在不同的函数使用,故须定义为全局变量)

GLuint DrawList;

2、将RenderScene函数(就是之前写glbegin和glEnd的地方)中的有关三角形的代码全部用glCallList函数代替Huffie
3、在SetupRC函数中的最后新建列表框架如下
Huffie
4、框架写好之后在图中注释位置插入画四面体的代码,插入后结果如下图
Huffie
5、至此一个存有四面体列表就新建好了,点击调试运行即可看到和以前一样的四面体。
Huffie

如何读取本地模型

当我们需要读取本地文件中的模型时,如何操作呢
1、既然要读取文件中的模型,首先肯定要打开文件,在创建列表的代码之前插入如下三行代码
Huffie
(注意,if stream的括号内为文件的路径,其中的\都要写成\,因为在C语言字符串中,\表示转义,\\才表示一个\)
(注意,ifstream若要使用需要先在开头插入以下两行引入头文件同时设置环境)

#include<fstream>
using namespace std;

2、在定义几个数组用来存放一会要读取的数据
Huffie
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>)
Huffie
Huffie
注意一下,计算机里的图像无论是平面还是曲面,都是由无数个三角形组成的,只不过三角形数量无比多时,我们看起来它就是一个曲面,Part1.TXT文件中也是,每读取到三个点就画一个三角形,许许多多个三角形就会组成一个立体图形。
4、至此,如何从文件中导入立体模型就完成了,点击调试,即可看到一个正方体
6、那么对于给定的STL文件如何读取?首先右击Part2.STL,用记事本打开,看到文件内容如下
Huffie
这看起来和之前差不多,只是他多给了一个法向量,没用的字符串多了一些而已,你可以不用他给的法向量,只读取三个坐标然后自己计算法向量,也可以读取法向量,这个时候 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循环里第一句前面设置断点,如下图
Huffie
然后点击单步执行第二个按钮
Huffie
每次循环读9个点,查看你读取到的点的值是否与文件中的坐标值一一对应,其中012对应第一行3个点,345对应第二行三个点,678对应第三行。
Huffie

Chapter 7 矩阵操作

利用矩阵实现向量平移

1、基本原理:如图,任意给定一个点的坐标(列向量)x,y,z。设置一个矩阵,利用矩阵的乘法运算即可将三个坐标进行平移
注意:过程中所设置的矩阵为单位阵的最后一列加上偏移量Tx,Ty,Tz。如下图,大家自己试一下矩阵乘法即可验证。
Huffie
2、在了解了如何将一个列向量进行平移之后,我们就可以编写程序进行向量的平移操作了,我们打开生成螺旋线的程序,注意到螺旋线是由许许多多点组成的,下面这个for循环就是每次生成一个点,我们只需要吧每个点的坐标向量进行平移即可使整个螺旋线平移。
Huffie
3、现在就开始写程序了,首先明确一下程序执行过程

1. 获得一个点的坐标存入P0数组内
2. 设置一个矩阵Translation用来将坐标平移
3. 将上述两个矩阵相乘得到的结果存入P1数组内,此即为平移后的点的坐标

4、第一步,获得一个点的坐标存入P0数组内,这一步十分简单(注:除函数定义外,其余代码均在for循环内)float P0[3] = {x,y,z};
5、第二步,设置一个矩阵Translation用来将坐标平移,我们需要一个下面这样的矩阵
Huffie
如何操作呢,首先先初始化一个单位矩阵,然后将单位矩阵的最后一列赋值为需要偏移的量(我的代码十分简单粗暴,当然也可单独另写一个函数用于初始化一个单位矩阵)
注意:在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……

猜你喜欢

转载自blog.csdn.net/weixin_44543463/article/details/102650117