计算机图形学(四)——opengl实现双三次Bizer曲面的绘制

1.实验目的与要求:

掌握图形综合展示效果,包括图形的三维创建,空间旋转,平移,简单纹理映射与光照。

2.实验内容:

实现图形的综合展示效果

1、绘制双三次Bezier表面模型,并给出纹理映射

2、完成给定图形展示要求的相关代码。

计算机图形学(四)——opengl实现双三次Bizer曲面的绘制_第1张图片

  1. 图形几何元素的放大功能,即图形可以利用控件进行缩放操作。
  2. 图形表面生成若干点,根据所展示的图形,采用三角形或者四边形片组成图形,点数可通过控件进行动态调整。
  3. 图形形态实现线框图和实体两种,并且实体是由线框图生成,线框图可以利用控件进行选择是否进行消隐。
  4. 图形实现光照功能与材质功能,控件实现光照关闭,控件实现材质功能,至少实现一种给定光源位置的光照,至少实现三种材质。
  5. 图形平移控制与旋转控制通过控件交互实时更改,并且图形在各轴上的旋转和平移可独立控制,另外实现两种绕中心旋转(即,可以通过三个轴通过控件平移为零实现,也可以通过给定控件选择实现各方向平移为零)
  6. 题目选择立方体的同学,请实现两种纹理效果,一种为生成的网格纹理,一种为给定的图片。

3.实验原理:

1、双三次Bizer曲面的绘制原理:

当m=n=3时,定义一张双三次Bezier曲面,它由16个顶点定义,参数曲线u、v都是三次Bezier曲线,该曲面只通过4个角点P00、P30、P03、P33:

在这里插入图片描述

对相应的坐标乘上其对应的波音斯坦基函数,即可实现曲线的绘制,调用两次即可绘制出曲面。

2.2、OpenGL绘制纹理的步骤:

1. 开启纹理功能

使用glEnable(GL_TEXTURE_2D)开启2D纹理功能,glDisable(GL_TEXTURE_2D)关闭纹理,默认情况下纹理是关闭的。

2. 读取纹理图片到内存,读取的时候注意以下3点:

1) BMP文件数据前54位是文件头和信息头数据,偏移54位之后才是要读取的图像数据。
2) 要检测图像宽度的位数是否是4的整数倍,如果不是,需要补齐,并且以补齐后的内存带下分配内存。
3) 出于兼容较低版本OpenGL的目的,一般需要检测图像的长宽是否是2的整数次幂,以及长宽是否超过了当前版本所支持的最大长宽数值,若条件不符合,需要进行相应的调整,可以使用gluScaleImage函数对图像进行缩放。

3. 分配纹理编号并设置相关属性

使用函数glGenTextures(N,&textureID)来分配N个纹理编号,使用glTexParameteri来设置常用的4个纹理参数,这些参数包括了当前纹理图像大小/小于模型目标时扩展纹理的处理方式。使用gltextImage2D函数根据指定的纹理参数生成一个2D纹理,函数原型是:
glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
第一个参数为指定的目标,可以使用GL_TEXTURE_2D。
第二个参数为“多重细节层次”,不考虑多重纹理的话设置为零。
第三个参数表示RGB数据存储的格式,这里使用GL_RGB。
第四、五个参数是二维纹理像素的宽度和高度。
第六个参数是纹理边框的大小,不使用纹理边框设置为零。
第七个核第八个参数是数据格式和数据保存形式。
第九个参数是保存了纹理图像的内参块地址。
在分配纹理之前最好先获取一下程序当前所使用的纹理编号,并且在生成本次纹理之后恢复之前保存的纹理编号,从而保证不对当前程序产生影响。

4) 设置视景体和观察点

通过gluPerspective和gluLookAt设置“视角”和“观察点”的相关参数。

5) 纹理映射,绘制纹理图像

纹理映射跟颜色的绘制一样,需要指定每一个顶点在纹理图像中所对应的位置,OpenGL会自动计算出顶点之间的其他点在纹理图像中应该对应的位置。这里注意纹理图像的坐标范围是从(0,0)到(1,1),左下角为(0,0),右上角为(1,1),鉴于之前已经在第3步中设置了坐标点大于1或者坐标点小雨0的情况的处理方法,所以不考虑显示效果的话,坐标可以任意指定。

在绘制双三次Bizer曲面时,我先给定16个固定点坐标,存放在三维数组当中,然后利用两次for循环,对每个坐标乘上对应的波音斯坦基函数,先绘制出四行Bizer曲线,每条曲线上有11个点,然后再利用两次for循环对每一列四个点求bizer曲线的坐标,共有11列,便得到了11×11个点的坐标,将其连起来变绘制出了双三次Bizer曲面。

4.效果展示

计算机图形学(四)——opengl实现双三次Bizer曲面的绘制_第2张图片
计算机图形学(四)——opengl实现双三次Bizer曲面的绘制_第3张图片
计算机图形学(四)——opengl实现双三次Bizer曲面的绘制_第4张图片
计算机图形学(四)——opengl实现双三次Bizer曲面的绘制_第5张图片

此外,曲面还可完成三个方向的旋转、放大缩小、切换为实体图、加入纹理和光照模型以及取消纹理和光照模型等操作,成功完成实验目的。

5.完整代码如下:(需加入一张bmp类型的图片,命名为1即可)

#include 
#include 
#include 
#include 
#include 
#define WindowWidth  400
#define WindowHeight 400
/* 控制点的坐标 */
GLfloat ctrlPoints[4][4][3] =
{
	{
		{ -0.8f, -0.7f, 0.2f }, { -0.3f, -0.6f, 0.2f }, { 0.2f, -0.65f, 0.3f }, { 0.7f, -0.7f, 0.2f }
	},
	{
		{ -0.9f, -0.2f, 0.3f }, { -0.3f, -0.6f, 0.2f }, { 0.3f, -0.2f, 0.4f },	{ 0.75f, -0.2f, 0.3f },
	},
	{
		{ -0.9f, 0.3f, 0.3f }, { -0.3f, 0.2f, 0.5f }, { 0.25f, 0.25f, 0.6f },	{ 0.8f, 0.3f, 0.3f },
	},
	{
		{ -0.8f, 0.8f, 0.1f }, { -0.3f, 0.8f, 0.2f }, { 0.2f, 0.85f, 0.1f },	{ 0.7f, 0.8f, 0.1f },
	}
};

//角度
double angle = 0;
int xyz[3] = {1, 1, 1};
float tx=0;
float ty=0;
float sf=0;
void init(void)
{

    //背景色
    glClearColor(0.0, 0.0, 0.0, 1.0);
    //深度测试
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    //打开自动法矢量
    glEnable(GL_AUTO_NORMAL);
    //允许正则化法矢量
    //glEnable(GL_NORMALIZE);
    //设置材质与光源
    GLfloat ambient[] = { 0.4, 0.6, 0.2, 1.0 };
    GLfloat position[] = { 0.0, 0.0, 0.0, 1.0 };//表示光源所在的位置
    GLfloat mat_diffuse[] = { 1.0, 0, 0, 1.0 };//镜面散射参数,表示该光源所发出的光,照射到粗糙表面时经过漫反射,所得到的光的强度(颜色)
    GLfloat mat_specular[] = { 0, 0, 1.0, 1.0 };//镜面反射参数,表示该光源所发出的光,照射到光滑表面时经过镜面反射,所得到的光的强度(颜色)。
    GLfloat mat_shininess[] = { 100.0 };//高光指数
    glEnable(GL_LIGHTING);//开关:使用光
    glEnable(GL_LIGHT0);//打开0#灯
	//灯光设置
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
	//材质属性
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);//散射光属性,表示光线照射到该材质上,经过漫反射后形成的光线强度(颜色)。
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);//镜面反射光
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}
//定义两个纹理对象编号
GLuint texGround;
GLuint texWall;

#define BMP_Header_Length 54  //图像数据在内存块中的偏移量


// 函数power_of_two用于判断一个整数是不是2的整数次幂
int power_of_two(int n)
{
	if( n <= 0 )
		return 0;
	return (n & (n-1)) == 0;
}

/* 函数load_texture
* 读取一个BMP文件作为纹理
* 如果失败,返回0,如果成功,返回纹理编号
*/
GLuint load_texture(const char* file_name)
{
	GLint width, height, total_bytes;
	GLubyte* pixels = 0;
	GLuint last_texture_ID=0, texture_ID = 0;

	// 打开文件,如果失败,返回
	FILE* pFile = fopen(file_name, "rb");
	if( pFile == 0 )
		return 0;

	// 读取文件中图象的宽度和高度
	fseek(pFile, 0x0012, SEEK_SET);
	fread(&width, 4, 1, pFile);
	fread(&height, 4, 1, pFile);
	fseek(pFile, BMP_Header_Length, SEEK_SET);

	// 计算每行像素所占字节数,并根据此数据计算总像素字节数
	{
		GLint line_bytes = width * 3;
		while( line_bytes % 4 != 0 )
			++line_bytes;
		total_bytes = line_bytes * height;
	}

	// 根据总像素字节数分配内存
	pixels = (GLubyte*)malloc(total_bytes);
	if( pixels == 0 )
	{
		fclose(pFile);
		return 0;
	}

	// 读取像素数据
	if( fread(pixels, total_bytes, 1, pFile) <= 0 )
	{
		free(pixels);
		fclose(pFile);
		return 0;
	}

	// 对就旧版本的兼容,如果图象的宽度和高度不是的整数次方,则需要进行缩放
	// 若图像宽高超过了OpenGL规定的最大值,也缩放
	{
		GLint max;
		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
		if( !power_of_two(width)
			|| !power_of_two(height)
			|| width > max
			|| height > max )
		{
			const GLint new_width = 256;
			const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
			GLint new_line_bytes, new_total_bytes;
			GLubyte* new_pixels = 0;

			// 计算每行需要的字节数和总字节数
			new_line_bytes = new_width * 3;
			while( new_line_bytes % 4 != 0 )
				++new_line_bytes;
			new_total_bytes = new_line_bytes * new_height;

			// 分配内存
			new_pixels = (GLubyte*)malloc(new_total_bytes);
			if( new_pixels == 0 )
			{
				free(pixels);
				fclose(pFile);
				return 0;
			}

			// 进行像素缩放
			gluScaleImage(GL_RGB,
				width, height, GL_UNSIGNED_BYTE, pixels,
				new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);

			// 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height
			free(pixels);
			pixels = new_pixels;
			width = new_width;
			height = new_height;
		}
	}

	// 分配一个新的纹理编号
	glGenTextures(1, &texture_ID);
	if( texture_ID == 0 )
	{
		free(pixels);
		fclose(pFile);
		return 0;
	}

	// 绑定新的纹理,载入纹理并设置纹理参数
	// 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复
	GLint lastTextureID=last_texture_ID;
	glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
	glBindTexture(GL_TEXTURE_2D, texture_ID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
	GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
	glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复之前的纹理绑定
	free(pixels);
	return texture_ID;
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glRotatef(angle, xyz[0], xyz[1], xyz[2]);
	glTranslatef(0.0f+tx,0.0f+ty,-4.0f);// 平移变换
	glScalef(1+sf,1+sf,1+sf); //缩放变换
   
		GLfloat ps[11][4][3];
	for (int j = 0; j < 4; j++){
		GLint i = 0;
		for (double t = 0.0; t <= 1.0; t += 0.1)
		{
 			double a1 = pow((1 - t), 3);
			double a2 = pow((1 - t), 2) * 3 * t;
			double a3 = 3 * t*t*(1 - t);
			double a4 = t*t*t;
			ps[i][j][0] = a1*ctrlPoints[0][j][0] + a2*ctrlPoints[1][j][0] + a3*ctrlPoints[2][j][0]+  a4*ctrlPoints[3][j][0];
			ps[i][j][1] = a1*ctrlPoints[0][j][1] + a2*ctrlPoints[1][j][1] + a3*ctrlPoints[2][j][1] + a4*ctrlPoints[3][j][1];
			ps[i][j][2] = a1*ctrlPoints[0][j][2] + a2*ctrlPoints[1][j][2] + a3*ctrlPoints[2][j][2] + a4*ctrlPoints[3][j][2];
			i = i + 1;
		}
	}
 
 
	GLfloat ps1[11][11][3];
	for (j = 0; j < 11; j++){
		GLint i = 0;
		for (double t = 0.0; t <= 1.0; t += 0.1)
		{
			double a1 = pow((1 - t), 3);
			double a2 = pow((1 - t), 2) * 3 * t;
			double a3 = 3 * t*t*(1 - t);
			double a4 = t*t*t;
			ps1[j][i][0] = a1*ps[j][0][0] + a2*ps[j][1][0] + a3*ps[j][2][0] + a4*ps[j][3][0];
			ps1[j][i][1] = a1*ps[j][0][1] + a2*ps[j][1][1] + a3*ps[j][2][1] + a4*ps[j][3][1];
			ps1[j][i][2] = a1*ps[j][0][2] + a2*ps[j][1][2] + a3*ps[j][2][2] + a4*ps[j][3][2];
			i = i + 1;
		}
	}

	//给出实体图
 	//glColor3f(0.0, 0.0, 1.0);
	for (int i = 0; i < 10; ++i)
	{		
		for (j = 0; j < 10; j++)
		{
			glBindTexture(GL_TEXTURE_2D, texGround);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 0.0f); 	glVertex3fv(&ps1[i][j][0]);
			glTexCoord2f(0.0f, 1.0f); 	glVertex3fv(&ps1[i][j+1][0]);
			glTexCoord2f(1.0f, 1.0f); 	glVertex3fv(&ps1[i+1][j+1][0]);
			glTexCoord2f(1.0f, 0.0f);	glVertex3fv(&ps1[i+1][j][0]);		
			glEnd();
		}
 
	}

	glPointSize(5.0);
	//glColor3f(1.0, 1.0, 0.0);
 //画出控制点
	for (i = 0; i < 4; i++){
		glBegin(GL_POINTS);
		for (int j = 0; j < 4; j++)
			glVertex3fv(&ctrlPoints[i][j][0]);
		glEnd();
	}
 
 //画出控制线
	//glColor3f(0.0, 1.0, 1.0);
	for (i = 0; i < 4; i++){
		glBegin(GL_LINE_STRIP);
		for (j = 0; j < 4; j++)
			glVertex3fv(&ctrlPoints[i][j][0]);
		glEnd();
		glBegin(GL_LINE_STRIP);
		for (j = 0; j < 4; j++)
			glVertex3fv(&ctrlPoints[j][i][0]);
		glEnd();
	}

	angle = angle+0.2;
    angle =fmod(angle,360);
	glutSwapBuffers();
}
void mouseCB(int button, int state, int x, int y)
{
	mouseX = x;
	mouseY = y;
	if (button == GLUT_LEFT_BUTTON)
	{
		if (state == GLUT_DOWN)
		{
			x_scale += 0.1;
		}
		else if (state == GLUT_UP)
			mouseLeftDown = false;
	}
	else if (button == GLUT_RIGHT_BUTTON)
	{
		if (state == GLUT_DOWN)
		{
			mouseRightDown = true;
			move +=0.1;
		}
		else if (state == GLUT_UP)
			mouseRightDown = false;
	}
}

void mouseMotionCB(int x, int y)
{
	if (mouseLeftDown)
	{
		cameraAngleY += (x - mouseX);
		cameraAngleX += (y - mouseY);
		mouseX = x;
		mouseY = y;
	}
	if (mouseRightDown)
	{
		cameraDistance += (y - mouseY) * 0.2f;
		mouseY = y;
	}

	glutPostRedisplay();
}
void reshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-5.0, 5.0, -5.0*(GLfloat)h / (GLfloat)w, 5.0*(GLfloat)h / (GLfloat)w, -5.0, 5.0);
    else
        glOrtho(-5.0*(GLfloat)w / (GLfloat)h, 5.0*(GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 'x':
        xyz[0] = 1;
        xyz[1] = xyz[2] = 0;
        break;
    case 'y':
        xyz[1] = 1;
        xyz[0] = xyz[2] = 0;
        break;
    case 'z':
        xyz[2] = 1;
        xyz[0] = xyz[1] = 0;
        break;
    case 'a':
        xyz[0] = xyz[1] = xyz[2] = 0;
        break;
    case 'b':
        xyz[0] = xyz[1] = xyz[2] = 1;
        break;
	case '1':
       sf+=0.1;
        break;
	case '2':
       sf-=0.1;
        break;
	case '3':
       glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);//显示线框模型
        break;
	case '4':
       	glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);//显示实体模型
        break;
	case '5':
		glEnable(GL_LIGHTING);//开灯
		break;
	case '6':
		glDisable(GL_LIGHTING);//关灯
		break;
	case '8':
		glDisable(GL_TEXTURE_2D);//关闭纹理映射
		break;
	case '7':
		glEnable(GL_TEXTURE_2D);//打开纹理映射
		break;
    default:
        break;
    }
    glutPostRedisplay();
}
void specialKeyboard(int key,int x,int y)
{
if(key == GLUT_KEY_UP )
ty+=0.1;
if(key == GLUT_KEY_DOWN)
ty-=0.1;
if(key ==GLUT_KEY_LEFT)
tx-=0.1;
if(key==GLUT_KEY_RIGHT)
tx+=0.1;
glutPostRedisplay();
}
int main(int argc, char** argv)
{
    srand((unsigned int)time(0));
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);//使用双缓存模式和深度缓存
    glutInitWindowSize(800, 800);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Bezier曲面");
    init();
	glEnable(GL_TEXTURE_2D);    // 启用纹理
	texGround = load_texture("ground.bmp");  //加载纹理
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
		glutMouseFunc(mouseCB);
	glutMotionFunc(mouseMotionCB);
    glutKeyboardFunc(keyboard);
    glutSpecialFunc(specialKeyboard);  //注册点击上下左右方向按钮时回调rotateMode函数
    glutIdleFunc(display);//设置空闲时调用的函数
    glutMainLoop();
    return 0;
}

演示说明

上下左右键可以控制图形移动
XYZAB可以控制图形旋转
1控制图形放大,2控制图形缩小
3显示线框模型 4显示实体模型
5添加光照 6取消光照
7添加纹理映射 8取消纹理映射

你可能感兴趣的:(计算机图形学)