第一篇:图形学实验5
《3D游戏图形学》
实验报告书
(实验五)
姓名:
学号: 班级:
浙江理工大学 二0一二 年 十二 月
数字图像处理实验指导书
实验五 纹理映射实验
一、实验目的和要求
掌握纹理映射的基本原理,利用VC++ OpenGL实现纹理映射技术。
二、实验原理
纹理映射是真实感图形制作的一个重要部分,运用纹理映射可以方面地制作真实感图形,而不必花更多的时间去考虑物体的表面纹理。如一张木制桌子其表面的木纹是不规范的,看上去又是那么自然,如果在图形制作中不用纹理映射,那么只是这张桌面纹理的设计,就要花费很大精力,而且设计结果也未必能像现实中那么自然。如果运用纹理映射就非常方便,可以用扫描仪将这样的一张桌子扫成一个位图。然后的具体的操作中,只需把桌面形状用多边形画出来,把桌面纹理贴上去就可以了。
另外,纹理映射能够在多边形进行变换时仍保证纹理的图案与多边形保持一致性。例如,以透视投影方式观察墙面时,远端的砖会变小,而近处的砖就会大一些。
此外,纹理映射也可以用于其他方面。例如,使用一大片植被的图像映射到一些连续的多边形上,以模拟地貌,或者以大理石、木纹等自然物质的图像作为纹理映射到相应的多边形上,作为物体的真实表面。
在OpenGL中提供了一系列完整的纹理操作函数,用户可以用它们构造理想的物体表面,可以对光照物体进行处理,使其映射出所处环境的景象,可以用不同方式应用到曲面上,而且可以随几何物体的几何属性变换而变化,从而使制作的三维场景和三维物体更真实更自然。
在OpenGL中要实现纹理映射,需要经历创建纹理、指定纹理应用方式、启用纹理映射、使用纹理坐标和几何坐标绘制场景几个过程。
用于指定一维、二维和三维纹理的函数分别为: Void glTexImage1D(GLenum target, Glint level, Glint components, GLsizei width, Glint border, GLenum format, GLenum type, const GLvoid *texels);Void glTexImage2D(GLenum target, Glint level, Glint components, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *texels);Void glTexImage3D(GLenum target, Glint level, Glint components, GLsizei width, GLsizei height, GLsizei depth, Glint border, GLenum format, GLenum type, const GLvoid *texels);其中,参数target取值一般为GL_TEXTURE_1D, GL_TEXTURE_2D和GL_TEXTURE_3D,分别与一维、二维和三维的纹理相对应。参数Level表示纹理多分辨率层数,通常取值为0,表示只有一种分辨率。参数components的可能取值为1~4的整数以及多种符号常量(如GL_RGBA),表示纹理元素中存储的哪些分量(RGBA颜色、深度等)在纹理映射中被使用,1表示使用R颜色分量,2表示使用R和A颜色分量,3表示使用RGB颜色分量,4表示使用RGBA颜色分量。参数width,height,depth分别指定纹理的宽度、高度、深度。参数format和type表示给出的图像数据的数据格式和数据类型,这两个参数的取值都是符号常量(比如format指定为GL_RGBA,type指定为GL_UNSIGNED_BYTE,参数texels指向内存中指定的纹理图像数据。
在定义了纹理之后,需要启用纹理的函数: 数字图像处理实验指导书
glEnable(GL_TEXTURE_1D);glEnable(GL_TEXTURE_2D);glEnable(GL_TEXTURE_3D);在启用纹理之后,需要建立物体表面上点与纹理空间的对应关系,即在绘制基本图元时,在glVertex函数调用之前调用glTexCoord函数,明确指定当前顶点所对应的纹理坐标,例如:
glBegin(GL_TRIANGLES);glTexCoord2f(0.0, 0.0);glVertex2f(0.0, 0.0);glTexCoord2f(1.0, 1.0);glVertex2f(15.0, 15.0);glTexCoord2f(1.0, 0.0);glVertex2f(30.0, 0.0);glEnd();其图元内部点的纹理坐标利用顶点处的纹理坐标采用线性插值的方法计算出来。
在OpenGL中,纹理坐标的范围被指定在[0,1]之间,而在使用映射函数进行纹理坐标计算时,有可能得到不在[0,1]之间的坐标。此时OpenGL有两种处理方式,一种是截断,另一种是重复,它们被称为环绕模式。在截断模式(GL_CLAMP)中,将大于1.0的纹理坐标设置为1.0,将小于0.0的纹理坐标设置为0.0。在重复模式(GL_REPEAT)中,如果纹理坐标不在[0,1]之间,则将纹理坐标值的整数部分舍弃,只使用小数部分,这样使纹理图像在物体表面重复出现。例如,使用下面的函数:
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);分别指定二维纹理中s坐标采用截断或重复处理方式。
另外,在变换和纹理映射后,屏幕上的一个像素可能对应纹理元素的一小部分(放大),也可能对应大量的处理元素(缩小)。在OpenGL中,允许指定多种方式来决定如何完成像素与纹理元素对应的计算方法(滤波)。比如,下面的函数可以指定放大和缩小的滤波方法:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);其中,glTexParameteri函数的第一个参数指定使用的是一维、二维或三维纹理;第二个参数为GL_TEXTURE_MAG_FILTER或GL_TEXTURE_MIN_FILTER,指出要指定缩小还是放大滤波算法;最后一个参数指定滤波的方法。
补充:透视投影函数
void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);它也创建一个对称透视视景体,但它的参数定义于前面的不同。其操作是创建一个对称的透视投影矩阵,并且用这个矩阵乘以当前矩阵。参数fovy定义视野在X-Z平面的角度,范围是[0.0,180.0];参数aspect是投影平面宽度与高度的比率;参数zNear和Far分别是远近裁剪面沿Z负轴到视点的距离,它们总为正值。
三、实验内容
在OpenGL中纹理映射所使用的纹理数据,既可以是程序生成的一组数据,也可以从外部文件中直接读取,参考示范代码完成以下两项内容: 1.利用直接创建纹理的方法生成二维纹理并映射到四边形上。参考代码:
void makeImage(void){ 数字图像处理实验指导书
int i, j, r,g,b;for(i = 0;i < ImageWidth;i++){
for(j = 0;j < ImageHeight;j++)
{
r=(i*j)%255;
g=(4*i)%255;
b=(4*j)%255;
Image[i][j][0] =(GLubyte)r;
Image[i][j][1] =(GLubyte)g;
Image[i][j][2] =(GLubyte)b;
} }} void myinit(void){ glClearColor(0.0, 0.0, 0.0, 0.0);glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LESS);// 生成纹理数据 makeImage();// 设置像素存储模式
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);// 定义二维纹理映射 glTexImage2D(……);// 定义纹理映射参数 glTexParameterf(……);glTexParameterf(……);glTexParameterf(……);glTexParameterf(……);// 启用二维纹理
glEnable(GL_TEXTURE_2D);glShadeModel(GL_FLAT);} void display(void){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//绘制四边形,并完成纹理映射
……
glFlush();} void myReshape(GLsizei w, GLsizei h){ ……} void main(int argc, char* argv[]){ ……} 2.从外部文件中直接读取纹理实现正方体每个面的纹理映射,并使正方体转动。
数字图像处理实验指导书
整个过程需要三个步骤:创建纹理、启用纹理映射和使用纹理坐标和几何坐标绘制,下面我们对三个过程进行阐述,并给出参考代码。1)创建纹理对象并绑定纹理
纹理创建即在内存中创建保存纹理数据的数组,一般是先读入一个图像文件,将图像文件的RGBA信息存入我们创建的纹理空间中,当然图像的位图不同,创建的纹理空间结构也会有所不同。为了更加简单易懂地实现这个过程,我们使用未压缩的纹理。代码:
GLuinttexture[1];//创建一个纹理空间
AUX_RGBImageRec *LoadBMP(CHAR *Filename)//载入位图图像 { FILE *File=NULL;//文件句柄
if(!Filename)//确保文件名已提供
{
return NULL;} File=fopen(Filename, “r”);//尝试打开文件
if(File){
fclose(File);//关闭文件
return auxDIBImageLoadA(Filename);//载入位图并返回指针
} return NULL;} //如果载入失败,返回NULL int LoadGLTextures()//载入位图并转换成纹理 { int Status=FALSE;//状态指示器
AUX_RGBImageRec *TextureImage[1];//创建纹理的存储空间
memset(TextureImage, 0, sizeof(void *)*1);//初始化 //载入位图,检查有无错误,如果位图没找到则退出
if(TextureImage[0]=LoadBMP(“data.bmp”)){
Status=TRUE;
glGenTextures(1,&texture[0]);//创建纹理 //使用来自位图数据生成的典型纹理
glBindTexture(GL_TEXTURE_2D, texture[0]);//生成2D纹理
glTexImage2D(GL_TEXTURE_2D,0,3,TextureImage[0]->sizeX,TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);} if(TextureImage[0])//纹理是否存在{
if(TextureImage[0]->data)//纹理图像是否存在 { 数字图像处理实验指导书
free(TextureImage[0]->data);//释放纹理图像占用的内存
}
free(TextureImage[0]);//释放图像结构
} return Status;//返回Status }
2)启用纹理映射操作,初始化相关参数
在OpenGL中使用纹理映射之前,必须打开纹理映射。int InitGL(GLvoid){ if(!LoadGLTextures())//调用纹理载入子例程
{
return FALSE;} glEnable(GL_TEXTURE_2D);//启用纹理映射
glShadeModel(GL_SMOOTH);//启用阴影平滑
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);//黑色背景
glClearDepth(1.0f);//设置深度缓存
glEnable(GL_DEPTH_TEST);//启用深度测试
return TRUE;}
3)使用纹理坐标和几何坐标绘制 void DrawGLScene(void){ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);glLoadIdentity();glTranslatef(0.0f,0.0f,-5.0f);glRotatef(xrot,1.0f,0.0f,0.0f);glRotatef(yrot,0.0f,1.0f,0.0f);glRotatef(zrot,0.0f,0.0f,1.0f);// 选择纹理
glBindTexture(GL_TEXTURE_2D,texture[0]);//绘制一个正方体,给每个面贴上纹理,并使之转动
glBegin(GL_QUADS);……
glEnd();xrot+=0.3f;yrot+=0.2f;zrot+=0.4f;}
四、实验代码 数字图像处理实验指导书
1、利用直接创建纹理的方法生成二维纹理并映射到四边形上。#include
#define imageWidth 64 #define imageHeight 64 GLubyte image[imageWidth][imageHeight][3];
/*绘制一个简单的二维纹理图*/ void makeImage(void){ int i,j,r,g,b;
/*根据点的位置设置不同的颜色*/ for(i = 0;i < imageWidth;i++){
for(j = 0;j r =(i*j)%255; g =(i*i)%255; b =(j*j)%255; image[i][j][0] =(GLubyte)r; image[i][j][1] =(GLubyte)g; image[i][j][2] =(GLubyte)b; } } } void myInit(void){ glClearColor(0.0,0.0,0.0,0.0);glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LESS);// 生成纹理数据 makeImage();// 设置像素存储模式 glPixelStorei(GL_UNPACK_ALIGNMENT,1);/*指定二维纹理映射*/ glTexImage2D(GL_TEXTURE_2D,0,3,imageWidth,imageHeight,0,GL_RGB,GL_UNSIGNED_BYTE,&image[0][0][0]);//纹理过滤函数 /*GL_TEXTURE_2D: 操作D纹理.GL_TEXTURE_WRAP_S: S方向上的贴图模式.GL_CLAMP: 将纹理坐标限制在.0,1.0的范围之内.如果超出了会如何呢.不会错误,只是会边缘拉伸填充.GL_TEXTURE_MAG_FILTER: 放大过滤 GL_LINEAR: 线性过滤, 使用距离当前渲染像素中心最近的个纹素加权平均值.数字图像处理实验指导书 GL_TEXTURE_MIN_FILTER: 缩小过滤 GL_LINEAR_MIPMAP_NEAREST: 使用GL_NEAREST对最接近当前多边形的解析度的两个层级贴图进行采样,然后用这两个值进行线性插值.*/ glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);/*设置纹理环境参数*/ //glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);// 启用二维纹理 glEnable(GL_TEXTURE_2D);glShadeModel(GL_FLAT);} void myDisplay(void){ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);/*将纹理映射到四边形上*/ glBegin(GL_QUADS);/*纹理的坐标和四边形顶点的对应*/ glTexCoord2f(0.0,0.0);glVertex3f(-0.7,-0.25,0.0);glTexCoord2f(0.0,1.0);glVertex3f(-0.2,-0.25,0.0);glTexCoord2f(1.0,1.0);glVertex3f(-0.2,0.25,0.0);glTexCoord2f(1.0,0.0);glVertex3f(-0.7,0.25,0.0);glTexCoord2f(0.0,0.0);glVertex3f(0.2,-0.25,1.875);glTexCoord2f(0.0,1.0);glVertex3f(0.6,-0.25,0.0);glTexCoord2f(1.0,1.0);glVertex3f(0.6,0.25,0.125);glTexCoord2f(1.0,0.0);glVertex3f(0.2,0.25,2.0);glEnd();glFlush();} void myReshape(int w,int h){ glViewport(0,0,(GLsizei)w,(GLsizei)h);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(80.0,1.0-(GLfloat)w/(GLfloat)h,1.0,30.0);glMatrixMode(GL_MODELVIEW);glLoadIdentity();} int main(int argc,char **argv){ /*初始化*/ glutInit(&argc,argv);glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);glutInitWindowSize(400,400);数字图像处理实验指导书 glutInitWindowPosition(200,200);glutCreateWindow(“ Texture ”);//创建窗口 myInit();//绘制与显示 glutReshapeFunc(myReshape);glutDisplayFunc(myDisplay);glutMainLoop();return 0;} 2、从外部文件中直接读取纹理实现正方体每个面的纹理映射,并使正方体转动。#include #include #include #include #include #pragma comment(lib, “openGL32.lib”)#pragma comment(lib, “glu32.lib”)#pragma comment(lib, “glaux.lib”)#pragma comment(lib,“openGL32.lib”)GLuint texture[1];//创建纹理空间 GLfloat xRot,yRot,zRot;//控制正方体的旋转 //载入位图图像 AUX_RGBImageRec *LoadBMP(CHAR *Filename){ //载入位图图像 FILE *File=NULL; //文件句柄 if(!Filename){ //确保文件名已提供 return NULL;} File=fopen(Filename, “r”); //尝试打开文件 if(File){ fclose(File); //关闭文件 return auxDIBImageLoadA(Filename); //载入位图并返回指针 } return NULL; //如果载入失败,返回NULL } int LoadGLTextures(){ //载入位图并转换成纹理 int Status=FALSE; //状态指示器 AUX_RGBImageRec *TextureImage[1]; //创建纹理的存储空间 memset(TextureImage, 0, sizeof(void *)*1);//初始化 //载入位图,检查有无错误,如果位图没找到则退出 if(TextureImage[0]=LoadBMP(“data.bmp”)){ Status=TRUE; glGenTextures(1,&texture[0]); //创建纹理 //使用来自位图数据生成的典型纹理 glBindTexture(GL_TEXTURE_2D, texture[0]); //生成D纹理 数字图像处理实验指导书 glTexImage2D(GL_TEXTURE_2D,0,3,TextureImage[0]->sizeX,TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);} if(TextureImage[0]){ //纹理是否存在if(TextureImage[0]->data){ //纹理图像是否存在free(TextureImage[0]->data); //释放纹理图像占用的内存 } free(TextureImage[0]); //释放图像结构 } return Status; //返回Status } int InitGL(GLvoid){ if(!LoadGLTextures()){ //调用纹理载入子例程 return FALSE;} glEnable(GL_TEXTURE_2D); //启用纹理映射 glShadeModel(GL_SMOOTH); //启用阴影平滑 glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //黑色背景 glClearDepth(1.0f); //设置深度缓存 glEnable(GL_DEPTH_TEST); //启用深度测试 return TRUE;} void display(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f,0.0f,-5.0f); glRotatef(xRot,1.0f,0.0f,0.0f); glRotatef(yRot,0.0f,1.0f,0.0f); glRotatef(zRot,0.0f,0.0f,1.0f); //绘制正方体,贴上纹理并使之转动 glBindTexture(GL_TEXTURE_2D,texture[0]);//选择纹理 glBegin(GL_QUADS); //前 glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f,-1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f,-1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, 1.0f, 1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0]); glBegin(GL_QUADS); //后 glTexCoord2f(1.0f, 0.0f);glVertex3f(-1.0f,-1.0f,-1.0f);数字图像处理实验指导书 glTexCoord2f(1.0f, 1.0f);glVertex3f(-1.0f, 1.0f,-1.0f); glTexCoord2f(0.0f, 1.0f);glVertex3f(1.0f, 1.0f,-1.0f); glTexCoord2f(0.0f, 0.0f);glVertex3f(1.0f,-1.0f,-1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0]); glBegin(GL_QUADS); // 上 glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, 1.0f,-1.0f); glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, 1.0f,-1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0]); glBegin(GL_QUADS); //下 glTexCoord2f(1.0f, 1.0f);glVertex3f(-1.0f,-1.0f,-1.0f); glTexCoord2f(0.0f, 1.0f);glVertex3f(1.0f,-1.0f,-1.0f); glTexCoord2f(0.0f, 0.0f);glVertex3f(1.0f,-1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f);glVertex3f(-1.0f,-1.0f, 1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0]); glBegin(GL_QUADS); //右 glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f,-1.0f,-1.0f); glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, 1.0f,-1.0f); glTexCoord2f(0.0f, 1.0f);glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f);glVertex3f(1.0f,-1.0f, 1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0]); glBegin(GL_QUADS); //左 glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f,-1.0f,-1.0f); glTexCoord2f(1.0f, 0.0f);glVertex3f(-1.0f,-1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f);glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, 1.0f,-1.0f); glEnd(); glutPostRedisplay(); glutSwapBuffers(); } void reshape(int w,int h){ if(0 == h) h = 1; glViewport(0,0,(GLsizei)w,(GLsizei)h); glMatrixMode(GL_PROJECTION);数字图像处理实验指导书 glLoadIdentity(); gluPerspective(60.0f,(GLfloat)w /(GLfloat)h,1,100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void spinDisplay(void){ xRot += 0.2f; yRot += 0.2f; glutPostRedisplay();} void mouse(int button, int state, int x, int y)//鼠标监听 { switch(button){ case GLUT_LEFT_BUTTON: if(state == GLUT_DOWN) glutIdleFunc(spinDisplay);//设备空闲时调用的函数 break; case GLUT_MIDDLE_BUTTON: case GLUT_RIGHT_BUTTON: if(state == GLUT_DOWN) glutIdleFunc(NULL); break; default: break; } } int main(int argc,char** argv){ glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(400,400); glutInitWindowPosition(100,100); glutCreateWindow(“Texture Map”); InitGL(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse);//鼠标监听 glutMainLoop(); return 0; } 五、实验结果 1、利用直接创建纹理的方法生成二维纹理并映射到四边形上。数字图像处理实验指导书 2、从外部文件中直接读取纹理实现正方体每个面的纹理映射,并使正方体转动。(按左键转动,按右键停止) 六、实验心得 实验三 MFC画直线 最近自己在学习如何在VC 6.0 开发环境下的使用MFC AppWizard(exe)来绘画一条直线,虽然比较简单,通过这样的练习可以帮助你熟悉MFC的开发环境以及其中的消息传递机制,希望对于像我一样初入MFC图形绘制学习的人有帮 助 第一步:构建MFC窗体 打开Visual C++ 6.0编译器 新建→工程→MFC AppWizard(exe),工程名以DrawLine为例,然后确定。为了方便,在MFC应用程序向导—步骤1当中选择“单文档”,其余所有的步骤都为默认值,直接“完成”。这样一个简单的MFC窗体就构建好了,自己不妨Compile—Build—BuildExecute一下。 第二步:编辑菜单项 选择ResourceView视窗展开Menu文件夹,左键双击IDR_DRAWLITYPE,右边就会出现菜单图形编辑界面,为了简化,我们只在添加帮助→DrawLine功能选择项。双击空白会弹出“菜单项目 属性”对话框。ID:ID_DRAW_LINE;标明: DrawLine(&D),其它的为缺省。 第三步:建立消息命令 如果此时运行该程序,你会发现帮助—DrawLine的功能选项是灰色的,原因就在于我们还没有添加该功能的消息命令相应函数。通过“查看—Message Maps—Project:DrawLine—Class name:CDrawLineView—Object IDs:ID_DRAW_LINE—选定COMMAND—Add Function„”,其它为默认,最后确定完成。现在如果再重新运行该程序的话,会发现原来的灰色已经消除了。 第四步:添加鼠标消息响应 打开ClassView视窗,右键选定CDrawLineView,选择Add Windows Messsage Handler会弹出对话框,完成CDrawLineView类的WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP三个Windows消息事件的新建。 第五步:添加响应代码 首先,在ClassView视窗中双击CDrawLineView会定位到“DrawLineView.h : interface of the CDrawLineView class”的文件,添加CDrawLineView类的成员:protected: int m_Drag;POINT m_pPrev;POINT m_pOrigin;三个成员变量。视窗中展开CDrawLineView类,双击定位OnLBUTTONDOWN()函数。在该函数消息响应 处添加如下代码: //建立好绘图的设备环境 CClientDC dc(this);OnPrepareDC(&dc); dc.DPtoLP(&point); //获取起始点坐标 m_pPrev=point;m_pOrigin=point; m_Drag=1; 然后,定位于OnMouseMove(),添加如下代码(其中关键用到了橡皮筋技术): //建立好绘图的设备环境 CClientDC dc(this); OnPrepareDC(&dc);dc.DPtoLP(&point); dc.SetROP2(R2_NOT);//橡皮筋绘图技术 //判断是否BUTTONDOWN if(m_Drag) { dc.MoveTo(m_pOrigin);dc.LineTo(m_pPrev);dc.MoveTo(m_pOrigin);dc.LineTo(point); } m_pPrev=point; 最后,在OnLBUTTONDOWN()添加代码: m_Drag=0; 程序运行效果图 实验4 实现圆的生成算法 一、实验目的 1.熟悉CDC图形程序库; 2.掌握中点画圆生成算法; 3.掌握Bresenham画圆算法。 二、实验内容 利用VisualC++6.0设计一个简易画圆绘图板,验证圆生成算法。 三、实验指导 1.生成绘图应用程序的框架,如下图所示。具体实现见第二次实验,过程不再详细说明。 2.在应用程序中增加菜单 完成相关菜单的设计,具体的效果如下图所示,并设置好相关菜单消息的映射,具体的实现在前面的实验中介绍过,再此不在详细说明。 3.在绘图函数中添加代码 通过以上步骤,得到了与菜单对应的消息映射,就可以在函数中添加代码绘制图形了。(1)利用中点画圆算法实现圆的生成(算法原理见教材)。void CDraw_CirView::OnMid(){ // TODO: Add your command handler code here CDC*pDC=GetDC();//得到绘图类指针 RedrawWindow();//重绘窗口 int x,y,x0=200,y0=200,r=100;//圆的圆心为(x0,y0),半径为r float d;x=0;y=r;d=1.25-r; pDC->SetPixel(x+x0,y+y0,RGB(255,0,0));pDC->SetPixel(y+x0,x+y0,RGB(255,0,0));pDC->SetPixel(y+x0,-x+y0,RGB(255,0,0));pDC->SetPixel(x+x0,-y+y0,RGB(255,0,0));pDC->SetPixel(-x+x0,-y+y0,RGB(255,0,0));pDC->SetPixel(-y+x0,-x+y0,RGB(255,0,0));pDC->SetPixel(-y+x0,x+y0,RGB(255,0,0));pDC->SetPixel(-x+x0,y+y0,RGB(255,0,0));while(x<=y){ if(d<0) { d=d+2*x+3; x++; } else { d=d+2*(x-y)+5; x++; y--;} pDC->SetPixel(x+x0,y+y0,RGB(255,0,0)); pDC->SetPixel(y+x0,x+y0,RGB(255,0,0)); pDC->SetPixel(y+x0,-x+y0,RGB(255,0,0)); pDC->SetPixel(x+x0,-y+y0,RGB(255,0,0)); pDC->SetPixel(-x+x0,-y+y0,RGB(255,0,0)); pDC->SetPixel(-y+x0,-x+y0,RGB(255,0,0)); pDC->SetPixel(-y+x0,x+y0,RGB(255,0,0)); pDC->SetPixel(-x+x0,y+y0,RGB(255,0,0));} } 由以上代码绘出的图形如下: (2)利用Bresenham算法生成圆(算法原理见教材)。void CDraw_CirView::OnBre(){ // TODO: Add your command handler code here CDC*pDC=GetDC();//得到绘图类指针 //RedrawWindow();//重绘窗口 int x,y,x0=200,y0=200,r=50;//圆的圆心为(x0,y0),半径为r int delta,delta1,delta2,direction;x=0;y=r;delta=2*(1-r);while(y>=0){ pDC->SetPixel(x+x0,y+y0,RGB(0,0,255)); pDC->SetPixel(x+x0,-y+y0,RGB(0,0,255)); pDC->SetPixel(-x+x0,y+y0,RGB(0,0,255)); pDC->SetPixel(-x+x0,-y+y0,RGB(0,0,255)); if(delta<0) { delta1=2*(delta+y)-1; if(delta<=0)direction=1; else direction=2; } else if(delta>0) { delta2=2*(delta-x)-1; if(delta2<=0)direction=2; else direction=3; } else direction=2; switch(direction) { case 1:x++; delta+=2*x+1; break; case 2:x++;y--; delta+=2*(x-y+1); break; case 3:y--; delta+=(-2*y+1); break; } } } 由以上代码绘出的图形如下: (3)以上是本次实验的基本部分,利用中点画圆和Bresenham画圆算法实现的基本图形的绘制。能不能利用该算法,完成一些复杂图形的生成,比如利用基本的画圆算法绘制一个奥运五环。甚至根据画圆算法,实现二次曲线的生成,如椭圆的生成等等。请同学们认真考虑,完成这部分的内容,上机调试。 四、思考 1.如何实现圆心为任意位置的圆的绘制; 2.两种画圆算法的比较。 《3D游戏图形学》 实验指导书 浙江理工大学 二0一五年十月 数字图像处理实验指导书 课程实验指导 一、实验总体方案 1.教学目标与基本要求 1)掌握本书所介绍的图形算法的原理。 2)掌握通过具体的平台实现图形算法的方法,培养学生使用现代图形系统API的能力。 3)通过实验培养具有开发一个基本图形软件包的能力。2.实验平台 实验主要结合OpenGL设计程序,实现各种课堂教学中讲过的图形算法。程序设计语言主要以C/C++为主,开发平台是Visual C++。 3.实验步骤 1)预习教材与实验指导的实验具体方案部分相关的算法理论及原理。2)仿照教材与实验指导提供的算法,利用VC++ OpenGL进行实验。 3)调试、编译、运行程序,运行通过后,可考虑对程序进行修改或改进。 二、实验预备知识 OpenGL作为当前主流的图形API之一,在一些场合具有比DirectX更优越的特性。 (1)与C语言紧密结合 OpenGL命令最初就是用C语言函数来进行描述的,对于学习过C语言的人来讲,OpenGL很容易理解和学习。 (2)强大的可移植性 微软的Direct3D虽然也是十分优秀的图形API,但它只适用于Windows系统,而OpenGL不仅适用于Windows,还可以用于Unix/Linux等其他系统,它甚至在大型计算机、各种专业计算机上都有应用。并且,OpenGL的基本命令都做到了硬件无关,甚至是平台无关。 (3)高性能的图形渲染 OpenGL是一个工业标准,它的技术紧跟时代,现今各个显卡厂家无一不对OpenGL提供强力支持,激烈的竞争中使得OpenGL性能一直领先。 总之,OpenGL是一个非常优秀的图形软件接口。 下面对Windows下的OpenGL编程进行简单介绍。以下几点是学习OpenGL前的准备工作。 1.选择一个编译环境 现在Windows系统的主流编译环境有Visual C++,C++Builder,Dev-C++等,它们都支持OpenGL。这里选择Visual C++作为学习OpenGL的实验环境。 2.安装OpenGL工具包 1)将OpenGL工具包dll文件夹中的*.dll文件放到操作系统目录下面的system32文件夹(其路径一般为:C:WindowsSystem32)。 2)打开VC,在VC中选择Tools→Options→Directories,然后在Show directories for中选择Include files,在下面添加OpenGL工具包中Include文件夹的路径,如下图所示: 数字图像处理实验指导书 3)类似地,在Show directories for中选择library files,在下面添加OpenGL工具包中lib文件夹的路径,然后按OK。如下图所示: 3.建立一个OpenGL工程 打开VC后,在VC中选择File→New→Project,然后选择Win32 Console Application, 选择一个名字,然后按“OK”。在弹出的对话框中点An empty project,选择Finish。然后向该工程添加一个源文件,选择一个名字。 三、实验报告要求 (1)有实验报告封面 (2)给出简要的设计思路(原理)。(3)给出实现代码。 (4)给出实验结果的屏幕截图。(5)实验的心得体会或建议。 数字图像处理实验指导书 实验一VC++6.0+OpenGL绘图环境及基本图形学算法 实验项目性质:验证性实验 所属课程名称:3D游戏图形学 实验计划学时:3学时 一. 实验目的 1、熟悉OpenGL的主要功能; 2、掌握OpenGL的绘图流程和原理; 3、掌握OpenGL核心函数的使用; 4、理解基本图形元素光栅的基本原理; 5、掌握直线和圆的多种生成算法。 二. 实验内容 1、创建一个OpenGL工程,利用OpenGL库函数进行简单图形设计与绘制; 2、编程实现DDA算法和Bresenham算法生成直线; 3、编程实现中点算法生成圆。 三. 实验原理 1、基本语法(C版本下的OpenGL语法) OpenGL基本函数均使用gl作为函数名的前缀,如glClearColor();实用函数则使用glu作为函数名的前缀,如gluSphere()。OpenGL基本常量的名字以GL_开头,如GL_LINE_LOOP;实用常量的名字以GLU_开头,如GLU_FILL。一些函数如glColor*()(定义颜色值),函数名后可以接不同的后缀以支持不同的数据类型和格式,如glColor3b()、glColor3d()、glColor3f()和glColor3bv()等,这几个函数在功能上是相似的,只是适用于不同的数据类型和格式,其中3表示该函数带有三个参数,b、d、f分别表示参数的类型是字节型、双精度浮点型和单精度浮点型,v则表示这些参数是以向量形式出现的。 OpenGL定义了一些特殊标识符,如GLfloat、GLvoid,它们其实就是C中的float和void。 2、程序的基本结构 OpenGL程序的基本结构可分为三个部分: 第一部分是初始化,主要是设置一些OpenGL的状态开关,如颜色模式(RGBA或ALPHA)的选择,是否作光照处理(若有的话,还需设置光源的特性),深度检验、裁剪等。这些状态一般都用函数glEnable(„)、glDisable(„)来设置,数字图像处理实验指导书 “„”表示特定的状态。 第二部分设置观察坐标系下的取景模式和取景框位置大小,主要利用了三个函数: 函数void glViewport(left, top, right, bottom)设置在屏幕上的窗口大小,四个参数描述屏幕窗口四个角上的坐标(以像素表示); 函数void glOrtho(left, right, bottom, top, near, far)设置投影方式为正交投影(平行投影),其取景体积是一个各面均为矩形的六面体; 函数void gluPerspective(fovy, aspect, zNear, zFar)设置投影方式为透视投影,其取景体积是一个截头锥体。 第三部分是OpenGL的主要部分,是使用OpenGL的库函数构造几何物体对象的数学描述,包括点线面的位置和拓扑关系、几何变换、光照处理等。 3、OpenGL状态机制 OpenGL的工作方式是一种状态机制,它可以进行各种状态或模式设置,这些状态或模式在重新改变它们之前一直有效。例如,当前颜色就是一个状态变量,在这个状态改变之前,绘制的每个像素都将使用该颜色,直到当前颜色被设置为其他颜色为止。OpenGL中大量地使用了这种状态机制,如颜色模式、投影模式、单双显示缓存区的设置、背景色的设置、光源的位置和特性等。许多状态变量可以通过glEnabel()、glDisable()这两个函数来设置成有效或无效状态,如是否设置光照、是否进行深度测试等;在被设置成有效状态之后,绝大部分状态变量都有一个缺省值。 四. 实验代码 1、如预备知识所述,安装OpenGL工具包,创建一个OpenGL工程,在源文件上输入如下代码: #include //将背景置成黑色 数字图像处理实验指导书 { glClear(GL_COLOR_BUFFER_BIT);//清除缓存 glBegin(GL_LINES); //开始画一根白线 glColor3f(1.0f, 1.0f, 1.0f);//设置颜色为白色 //设置第一根线的两个端点,请注意:OpenGL坐标系的原点是在屏幕左下角 glVertex2f(10.0f, 50.0f);glVertex2f(110.0f, 50.0f);glColor3f(1.0f, 0.0f, 0.0f);//设置颜色为红色 //设置第二根线的两个端点 glVertex2f(110.0f, 50.0f);glVertex2f(110.0f, 150.0f);glEnd();//画线结束 glFlush();//绘图结束 } // //主过程: // 初始化Windows的窗口界面 // 并初始化OpenGL场景,绘图 int main(int argc, char** argv){ glutInit(&argc,argv);glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);//初始化窗口的显示模式 glutInitWindowSize(400,300);//设置窗口的尺寸 glutInitWindowPosition(100,120);//设置窗口的位置 glutCreatWindow(“”); //创建一个名为“”的窗口 glutDisplayFunc(Display);//设置当前窗口的显示回调函数 myinit(); //完成窗口初始化 glutMainLoop();//启动主GLUT事件处理循环 数字图像处理实验指导书 return(0);} glaux.lib glu32.lib glut32.lib opengl32.lib 注:glShadeModel选择平坦或光滑渐变模式。GL_SMOOTH为缺省值,为光滑渐变模式,GL_FLAT为平坦渐变模式。 该程序是在一个黑色的窗口中画两条线,分别用白色和红色绘制。首先,需要包含头文件#include 函数glColor3f()以RGB方式设置颜色,格式为:glColor3f(red,green,blue),每种颜色值在(0.0, 1.0)之间。函数glVertex2f(x, y)设置二维顶点。函数glBegin(UINT State)、glEnd()是最基本的作图函数,下面对它作一介绍。 如上所述,OpenGL是一个状态机,glBegin(UINT State)可以设定如下状态: GL_POINTS GL_LINES 画点 画线,每两个顶点(Vertex)为一组 画线,把若干个顶点顺次连成封闭折线 画三角形,每三个顶点为一组 画多边形 GL_LINE_STRIP 画线,把若干个顶点顺次连成折线 GL_LINE_LOOP GL_TRIANGLES GL_QUADS GL_POLYGON 画四边形,每四个顶点为一组 还有GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS_STRIP 等等。大家把每一种状态都试一试。另外,程序可以有多组glBegin()、glEnd()并列的形式,如: ......数字图像处理实验指导书 glBegin(GL_LINES); glBegin(GL_QUADS);......glEnd();......除了上述的基本图元外,函数glRectf(x1, y1, x2, y2)可以画一个矩形,但这个函数不能放在glBegin()和glEnd()之间,下面的两句程序是画一个蓝色的矩形。 glColor3f(0.0f, 0.0f, 1.0f);glRectf(10.0f, 10.0f, 50.0f,50.0f);......glEnd();要求:利用OpenGL库函数绘制基本图形,输出绘制结果。 数字图像处理实验指导书 void display(void){ } glColor3f(0.0f, 0.0f, 1.0f);glRectf(10.0f, 10.0f, 50.0f, 50.0f);//画线结束 glFlush();//绘图结束 glClear(GL_COLOR_BUFFER_BIT);//清除缓存 glBegin(GL_LINES); //开始画一根白线 glColor3f(1.0f, 1.0f, 1.0f);//设置颜色为白色 //设置第一根线的两个端点,请注意:OpenGL坐标系的原点是在屏幕左下角 glVertex2f(10.0f, 50.0f);glVertex2f(110.0f, 50.0f);glColor3f(1.0f, 0.0f, 0.0f);//设置颜色为红色 //设置第二根线的两个端点 glVertex2f(110.0f, 50.0f);glVertex2f(110.0f, 150.0f);glEnd(); 2、创建一个新的OpenGL工程,在源文件上输入如下代码: //DDA算法 # include 数字图像处理实验指导书 void LineDDA(int x0, int y0, int x1, int y1){ } void myDisplay(void){ glPointSize(5);glBegin(GL_POINTS);glColor3f(0.0f,1.0f,0.0f);glVertex2f(0.0f,0.0f);int x, dy, dx, y;float m;dx=x1-x0;dy=y1-y0;m=dy/dx;y=y0;glColor3f(1.0f, 1.0f, 0.0f);glPointSize(1);for(x=x0;x<=x1;x++){ } glBegin(GL_POINTS);glVertex2i(x,(int)(y+0.5));glEnd();y+=m;glClear(GL_COLOR_BUFFER_BIT);glColor3f(1.0f,0.0f,0.0f);glRectf(25.0,25.0,75.0,75.0); 数字图像处理实验指导书 } glEnd();LineDDA(0,0,200,300);glBegin(GL_LINES);glColor3f(1.0f,0.0f,0.0f);glVertex2f(100.0f,0.0f);glColor3f(0.0f,1.0f,0.0f);glVertex2f(180.0f,240.0f);glEnd();glFlush();void Init(){ } void Reshape(int w, int h){ } int main(int argc, char * argv[])glClearColor(0.0,0.0,0.0,0.0);glShadeModel(GL_FLAT);glViewport(0,0,(GLsizei)w,(GLsizei)h);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(0.0,(GLdouble)w,0.0,(GLdouble)h); 数字图像处理实验指导书 { } 介绍一下glutReshapeFunc()函数: 首次打开窗口、移动窗口和改变窗口大小时,窗口系统都将发送一个事件,以通知程序员。如果使用的是GLUT,通知将自动完成,并调用向glutReshapeFunc()注册的函数。该函数必须完成下列工作: a. 重新建立用作新渲染画布的矩形区域; b. 定义绘制物体时使用的坐标系。如: void Reshape(int w, int h){ glViewport(0, 0,(GLsizei)w,(GLsizei)h);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(0.0,(GLdouble)w, 0.0,(GLdouble)h); } 在GLUT内部,将给该函数传递两个参数:窗口被移动或修改大小后的宽度和高度,单位为像素。glViewport()调整像素矩形,用于绘制整个窗口。接下来三个函数调整绘图坐标系,使左下角位置为(0,0),右上角为(w,h)。glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);glutInitWindowPosition(100,100);glutInitWindowSize(400,400);glutCreateWindow(“Hello World!”);Init();glutDisplayFunc(myDisplay);glutReshapeFunc(Reshape);glutMainLoop();return 0;要求: 1)根据所给的直线光栅化的示范源程序,在计算机上编译运行,输出正确结果(示范代码有错误,指出并改正); 数字图像处理实验指导书 示范代码展示: void LineDDA(intx0, inty0, intx1, inty1){ } float x, dy, dx, y;float m;dx = x1y0;m = dy / dx;y = y0;glColor3f(1.0f, 1.0f, 0.0f);glPointSize(1);for(x = x0;x <= x1;x++){ } glBegin(GL_POINTS);glVertex2i(x,(int)(y + 0.5));glEnd();y += m; 数字图像处理实验指导书 2)根据示范程序采用的算法,以此为基础将其改造为Bresenham算法,写入实验报告; void Bresenhamline(intx0, inty0, intx1, inty1, intcolor){ int x, y, dx, dy;float k, e;dx = x1y0, k = dy / dx;e =-0.5, x = x0, y = y0;for(int i= 0;i <= dx;i++){ glPointSize(2.0f);glBegin(GL_POINTS);glColor3f(1.0f, 1.0f, 1.0f);glVertex2f(x, y);glEnd(); 数字图像处理实验指导书 } } x = x + 1,e = e + k;if(e >= 0){ } y++, e = e4 * radius;float deltaE = 12;float deltaSE = 20-8 * radius; glPointSize(3);glBegin(GL_POINT);glColor3f(0.0, 1.0, 0.0);glVertex2i(x, y);glEnd();while(y>x){ if(d <= 0){ d += deltaE; 数字图像处理实验指导书 deltaSE += 8; } else { d += deltaSE; deltaSE += 16; y--; } deltaE += 8; x++; glBegin(GL_POINTS); glVertex2i(x, y); glVertex2i(x,-y); glVertex2i(-x, y); glVertex2i(-x,-y); glVertex2i(y, x); glVertex2i(y,-x); glVertex2i(-y, x); glVertex2i(-y,-x); glEnd();} } 学号:姓名:班级: 计算机图形学 实验报告 2016年6月日 实验名称:三维交互式图形程序设计 一、实验目的: 1)掌握真实感图形生成的基本原理,如消隐、光照、材质等知识; 2)学习使用OpenGL、GLUT等生成基本图形,如球体、正方体、茶壶等; 3)学习使用OpenGL生成各种光源:点光源、平行光、聚光灯等; 4)学习使用OpenGL设置绘制对象的颜色、材质; 5)进一步熟悉OpenGL图形变换技术:几何变换、视图变换、观察变换等: (1)对模型实现各种几何变换(测试代码保留在程序中),包括对 glLoadMatrix、glMultMatrix、glTranslate、glRotate、glScale 等的使用; (2)视口变换,测试不同大小的视口 glViewport ; (3)投影变换,要对对平行投影与透视投影分别测试,使用不同的观察体参数,观察效果 二、实验任务: 1)使用MFC AppWizard 建立一个SDI 程序,根据参考资料2 和3 中的步骤设置OpenGL 图形绘制环境。增加绘制如下表图形(选择其中任意一个,注意模型坐标系的选择和顶点坐标的计算)、球体或茶壶等(使用glut 函数)。环境中创建点光源、平行光、聚光灯(可利用对话框输入参数创建)、设置所绘制对象的材质,呈现出塑料、金属等材质特性。对光源的位置、方向、类型进行控制,改变材质参数,观察效果(测试代码保留在程序中); 三.实验过程: 1.观察参数的设置 glViewport(0.0, 0.0, width, height)//视口大小设置 gluLookAt(0,0,10,0,0,0,0,1,0);//观察点设置,参数分别为观察点位置,向哪点(何处)观察,上方向的定义 2.投影变换 glOrtho(-15.0*aspect, 15.0*aspect,-15.0,15.0,1,100);//平行投影,参数是投影面大小和投影的物体距离范围 gluPerspective(45,aspect, 1, 100.0);//透视投影,参数为视角,长宽比,投影距离范围 3.几何变换 glLoadMatrixf(a);//设置当前矩阵为a glTranslatef(2,0,0);//当前矩阵*偏移矩阵 glRotatef(45, 0.0, 0.0, 0.0);//旋转 glScalef(m_scalX,m_scalY,m_scalZ);//当前矩阵*比例矩阵 4.光源参数的设置 glLightfv(GL_LIGHT0, Type, vlight);//light为float数组 glLightf(GL_LIGHT0, Type, light);//light为float Type=GL_LIGHT0, GL_AMBIENT //环境光 Type=GL_LIGHT0, GL_DIFFUSE //漫反射 Type=GL_LIGHT0, GL_SPECULAR //镜面光 Type=GL_POSITION //光源位置 Type=GL_CONSTANT_ATTENUATION //常数衰减因 Type=GL_LINEAR_ATTENUATION //线性衰减因子 Type=GL_QUADRATIC_ATTENUATION //二次衰减因子 Type=GL_SPOT_DIRECTION //聚光方向矢量 Type=GL_SPOT_EXPONENT //聚光指数 Type=GL_SPOT_CUTOFF //聚光截止角 光源类型控制 平行光源::设置AMBIENT,DIFFUSE,SPECULAR,GL_POSITION4个参数。其中GL_POSITION第4个参数为0,表示距离无限远 点光源:平行光源基础上添加常数衰减,因线性衰减因子,二次衰减因,其中GL_POSITION第4个参数为1 表示光源位置为确切位置,聚光灯光源 :在点光源基础上添加聚光方向矢量,聚光截止角,聚光指数 本实验使用了3个光源,分别作为平行光源,点光源和聚光灯光源,通过不同菜单选择在输入参数的同时启用不同的光源,以达到光源控制的效果 四.实验结果 原图: 材质选择: 光源基本参数设置 设置聚光灯光源(位置,方向,聚光指数,聚光截止角,衰减因子) 聚光衰减属性(聚光灯) 五.实验体会 通过此次实验,基本掌握了真实图形生成的基本原理,对于3D图新的材质变换,光源变换等有了更深层次的理解。初步学习并掌握了几何变换、视图变换、观察变换等。对于对话框、组建的设置更是理解的透彻。深入掌握了课堂上的些许知识。但对于之后的图形学设计,还是有很多的不足之处。所以,需要继续努力,掌握更多的图形设计学习。 吉林大学 计算机科学与技术学院 《计算机图形学》实验报告 班级: 211923班 学号: 21190928 姓名: 林星宇 2021-2022学年第1学期 实验项目1 边标志算法的实现 实验性质 □演示性实验 验证性实验 □操作性实验 综合性实验 实验地点 计算机楼B212 机器编号 一、实现的功能 编写应用程序,采用鼠标输入顶点的方法确定待填充多边形(多边形最后一点双击);实现边标志算法完成对该多边形的填充,要求 完成使用自己学号的后四位数字对多边形内部进行填充。 二、采用的图形学算法及实现 (算法的实现函数是什么(函数名,参数,返回值,函数功能等)以及采用了哪些数据结构(数组,链表等)) 要求使用边标志算法的原理和实 现方法,所以使用了EdgeMarkFill函数,即边标志算法: void CMFCDrawTestView::EdgeMarkFill(CDC* pDC, CArray pDC为设备环境变量指针,plist为多边形点表,color为传入的RGB()值。 int zima[16][32]为学号后4位二维数组。 X1,x2,y1,y2分别为多边形上的最小最小大,y值 三、采用的交互方式及实现 (采用了哪些交互方式来完成绘制,这些交互方式应用到了哪些系统消息,是如何实现的) 边填充的实现:编写应用程序,采用鼠标输入顶点的方法确定待填充多边形(多边形最后一点双击);实现边标志算法完成对该多边形的填充,要求 完成使用自己学号的后四位数字对多边形内部进行填充。 易知,在画完多边形后,即双击左键(OnLButtonUp)后,使用EdgeMarkFill函数。 Type=2时,在OnLButtonUp中,调用EdgeMarkFill(pDC,&(obj->points), RGB(r, 0, 0)); 四、实验结果 (程序的运行结果) 应用程序运行后,标志算法完成对该多边形的填充的图形结果如下: 五、遇到的问题及解决办法 问题1:(在实现过程中遇到了什么样的问题,及采用了何种解决办法) 在获取下x1,x2,y1,y2时,因为Dos界面x、y大小颠倒的原因,获取时出现了问题。 首先,通过for(int i = 1;i < plist->GetSize();i++){ CPoint p = plist->GetAt(i); if(x1 > p.x)x1 = p.x; if(x2 < p.x)x2 = p.x; if(y1 > p.y)y1 = p.y; if(y2 < p.y)y2 = p.y; } 获取x1,x2,y1,y2.在遍历多边形过程中: int count = plist->GetSize(); for(int i = 0;i < count;i++){ CPoint p1 = plist->GetAt(i); CPoint p2 = plist->GetAt((i + 1)% count); if(p1.y == p2.y) continue; if(p1.y > p2.y) { CPoint p;p = p1;p1 = p2;p2 = p; } xs = p1.x; dxs =(p2.x-p1.x)/(double)(p2.y-p1.y); //dys = abs(p2.y-p1.y)/(p2.y-p1.y); for(ys = p1.y;ys!= p2.y;ys += 1) { Ixs = int(xs + 0.5); MARK[ys][Ixs] =!MARK[ys][Ixs]; xs = xs + dxs; } 黄线处即为处理x1,x2,y1,y2的大小。 问题2:通过数组zima[][]来确定多边形区域填充学号后4位时,zima[y ][x ]未%其字长,即zima[y % 16][x % 32]。后改为: for(y = y1;y <= y2;y++) { bool inside = false; for(x = x1;x <= x2;x++) { if(MARK[y][x]) inside =!inside; if(inside) { if(zima[y % 16][x % 32]) pDC->SetPixel(x, y, RGB(255, 0, 0)); } } } 实验项目2 立方体的比例、平移、旋转变换及投影显示 实验性质 □演示性实验 验证性实验 □操作性实验 综合性实验 实验地点 计算机楼B212 机器编号 一、实现的功能 建立立方体的数据模型;编写应用程序,利用菜单和键盘结合的方式完成对立方体的移动、比例和旋转变换,并显示透视或斜二测投影结果。要求应用程序具有如下功能: 1、通过菜单选择的方式,选择对三维空间中的立方体作斜二测投 影或透视投影; 2、通过键盘按键或鼠标移动的方式,完成对三维空间中的立方体 进行平移变换(上下左右前后),比例变换(放大或缩小)以及 旋转变换(绕 x,y,z 轴),并同时显示变换后的投影结果 3、创建对话框,通过对话框设置透视投影时候的投影中心,以及旋转变换时候的旋转轴(可以设置成分别绕 x 轴,y 轴,z 轴进 行旋转) 二、采用的图形学算法及实现 (算法的实现函数是什么(函数名,参数,返回值,函数功能等)以及采用了哪些数据结构(数组,链表等)) 题目要求实现立方体的移动、比例和旋转变换,并显示透视或斜二测投影结果。 对要求1:在菜单选TY项中选择斜二测投影(斜二=1)或透视投影(透视=1)。然后在OnDraw中调用Draw_Cubic(CDC* pDC)画出立方体。 对要求2:在OnKeyDown中调用函数,即在键盘上按“S”使立方体变小,“B”使立方体变大,“←”“→”“↑”“↓”使立方体左右上下移动。 对要求3:在菜单XYZ中选择旋转的x,y,z轴,即x=1或y=1或z=1,然后在OnKeyDown中调用函数,即按键盘上的“T”或“P”. 三、采用的交互方式及实现 (采用了哪些交互方式来完成绘制,这些交互方式应用到了哪些系统消息,是如何实现的) 由题目要求1,易知需要一个函数Draw_Cubic(CDC* pDC)画出立方体的斜二测投影或透视投影并且建立一个菜单栏TY(投影)。即在菜单选TY项中选择斜二测投影(斜二=1)或透视投影(透视=1)。然后在OnDraw中调用Draw_Cubic(CDC* pDC)画出立方体。 由题目要求2:易知直接在OnKeyDown函数上添加使立方体变大变小,前后左右平移的功能。即即在键盘上按“S”使立方体变小,“B”使立方体变大,“←”“→”“↑”“↓”使立方体左右上下移动。 由题目要求3:建立一个菜单XYZ决定旋转的轴。 四、实验结果 (程序的运行结果) 斜二测投影: 斜二测投影平移到左上角: 斜二测投影平移到右下角: 斜二测投影变大: 斜二测投影变小: 斜二测投影变为透视投影: 斜二测投影绕z轴旋转: 五、遇到的问题及解决办法 (在实现过程中遇到了什么样的问题,及采用了何种解决办法) 问题1:一开始建立立方体时,没有建立边表,导致投影困难。 后来建立了点表和对应的边表。 问题2:一开始Draw_Cubic中x1, y1,z1, x2, y2,z2定义为了int型。 实验项目3 用矩形窗口对多边形进行裁剪 实验性质 □演示性实验 验证性实验 □操作性实验 综合性实验 实验地点 计算机楼B212 机器编号 一、实现的功能 编写应用程序实现多边形裁剪。要求首先采用鼠标确定裁剪区域(矩形区域),然 后用鼠标输入待裁剪的多边形(可分别使用鼠标左键和右键来确定裁剪区域和待裁剪 的多边形)。多边形绘制完毕后进行裁剪,以不同颜色显示被裁剪对象位于窗口内(此 部分应保证多边形的完整性)及外部的部分。 二、采用的图形学算法及实现 (算法的实现函数是什么(函数名,参数,返回值,函数功能等)以及采用了哪些数据结构(数组,链表等)) 因为要编写应用程序实现多边形裁剪。要求首先采用鼠标确定裁剪区域(矩形区域),然 后用鼠标输入待裁剪的多边形(可分别使用鼠标左键和右键来确定裁剪区域和待裁剪 的多边形)。所以要使用多边形裁剪算法,即Cut_Top(),Cut_Bottom(),Cut_Left(),Cut_Right()四个函数。 Cut()函数为用绿色显示被裁剪对象位于窗口内部分。 存在int type的变量; 当type=1时,在OnLButtonUp中画出矩形框。 当type=2时,画出多边形,在左键双击后,在OnLButtonDblClk中调用如下函数:Cut_Top();Cut_Right();Cut_Bottom();Cut_Left();Cut(); 裁剪多边形在,并标出在矩形内部的部分。 三、采用的交互方式及实现 (采用了哪些交互方式来完成绘制,这些交互方式应用到了哪些系统消息,是如何实现的) 编写应用程序实现多边形裁剪。要求首先采用鼠标确定裁剪区域(矩形区域),然 后用鼠标输入待裁剪的多边形(可分别使用鼠标左键和右键来确定裁剪区域和待裁剪 的多边形)。多边形绘制完毕后进行裁剪,以不同颜色显示被裁剪对象位于窗口内(此 部分应保证多边形的完整性)及外部的部分。 根据以上绘制方法,可知需要处理WM_OnLButtonDblClk(左键双击)及WM_LButtonUp(左键抬起)消息,为了绘制橡皮线,还需处理调用WM_MouseMove(鼠标移动)消息。 因为可以用鼠标画出矩形和多边形,所以这么规定,当type=1时画矩形,即: DDALine(pDC,lx,by,lx,ty,RGB(r, g, b)); DDALine(pDC, lx, by, rx, by, RGB(r, g, b)); DDALine(pDC, rx, by, rx, ty, RGB(r, g, b)); DDALine(pDC, lx, ty, rx, ty, RGB(r, g, b)); 当type=2时画多边形,而后裁剪,即: for(int i = 0;i < pointList.GetSize();i++) { p1 = pointList.GetAt(i); p2 = pointList.GetAt((i+1)% count); DDALine(pDC, p1.x, p1.y, p2.x, p2.y, RGB(0,255,0)); } 四、实验结果 (程序的运行结果) 裁剪结果如下图所示,黑色为裁剪窗口,红色为多边形被裁剪的部分,绿色为多边形裁剪后的部分: 五、遇到的问题及解决办法 (在实现过程中遇到了什么样的问题,及采用了何种解决办法) 问题1:我在裁剪使一开始对多边形做上下左右裁剪时,这四个步骤是分别对原图形裁剪,而不是对图形接连进行裁剪。后来在裁剪函数上先除去之前图形,然后把已裁剪多边形重新构建。如下: pointList.RemoveAll(); for(int i = 0;i < m;i++) pointList.Add(CP[i]); 问题2:在多边形被矩形裁剪的部分显现不同颜色花费了挺多时间,后来我直接让裁剪的部分颜色被覆盖就可以了。如下: for(int i = 0;i < pointList.GetSize();i++) { p1 = pointList.GetAt(i); p2 = pointList.GetAt((i+1)% count); DDALine(pDC, p1.x, p1.y, p2.x, p2.y, RGB(0,255,0)); }第二篇:计算机图形学实验
第三篇:3D游戏图形学实验一
第四篇:计算机图形学 实验六-西南交大
第五篇:《计算机图形学》实验报告