Exercise 2: 纹理
要求:
1. 在练习1的基础上,绘制一个带纹理的三角形;
2. 体现使用不同的纹理滤波方式(GL_NEAREST,GL_LINEAR)对结果的影响;
3. 使用超出1的纹理坐标,体现使用不同wrap参数(GL_REPEAT,GL_CLAMP)对结果的影响;
考察目的:
1. 纹理图像的载入;
2. OpenGL中基本纹理的使用;
3. 理解OpenGL中纹理滤波方式;
4. 理解OpenGL中的wrap参数;
5. 理解纹理映射中的走样问题(为什么会产生走样?怎样减少走样?);
by/scu xx
#include <windows.h>
#include <gl/gl.h>
#define GLUT_DISABLE_ATEXIT_HACK
#include <glut.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
// 两种不同的纹理过滤
GLuint texture[2];
// 判断使用哪种纹理两种纹理分别代表1
// 由键盘l,n控制分别代表LINEAR滤波和Nearest滤波
int selection = 0;
// 读取bmp图片部分
// 图片的类型包含宽高数据
struct Image
{
unsigned long sizeX;
unsigned long sizeY;
char *data;
};
typedef struct Image Image;
//旋转参数
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
//确定多边形绕法的方向
BOOL bWinding = TRUE;
int nStep = 0;
static int g_mousePos_x = 0,g_mousePos_y = 0, g_tmpX, g_tmpY;
bool g_rotate=false;//鼠标移动时的判断变量
//初始化窗口
void SetupRC(void)
{
//设置窗口背景颜色为灰色
glClearColor(0.7f,0.7f, 0.7f, 1.0f );
}
void ChangeSize(int w,int h)
{
if(h == 0) h =1;
glViewport(0, 0, w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
/*
glOrtho(left,right, bottom, top, near, far),
left表示视景体左面的坐标,right表示右面的坐标,bottom表示下面的,top表示上面的。
*/
//建立修剪空间的范围
if (w <= h)
glOrtho (-100.0f,100.0f, -100.0f*h/w, 100.0f*h/w, -100.0f, 100.0f);
else
glOrtho(-100.0f*w/h, 100.0f*w/h, -100.0f, 100.0f, -100.0f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//在窗口中绘制图形
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT);
GLfloat sizes[2]; //保存绘制点的尺寸范围
GLfloat step; //保存绘制点尺寸的步长
GLfloat curSize; //当前绘制的点的大小
glGetFloatv(GL_POINT_SIZE_RANGE,sizes);//获得点的尺寸范围
glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step);//获得点尺寸的步长
//旋转图形
glPushMatrix();
glRotatef(xRot, 1.0f,0.0f, 0.0f);
glRotatef(yRot, 0.0f,1.0f, 0.0f);
//设置多边形绕法的方向是顺时针还是逆时针
if(bWinding)
glFrontFace(GL_CW);
else
glFrontFace(GL_CCW);
switch(nStep) {
case 0:
//绘制不同大小的点
curSize=sizes[0];
for (inti=0;i<25;i++)
{
glRotatef((GLfloat)g_mousePos_x, -1.0, 0.0, 0.0);
glRotatef((GLfloat)g_mousePos_y, 0.0, -1.0, 0.0);
glPointSize(curSize);//设置点的大小
glTexCoord2f(0.5f,0.5f);
glBegin(GL_POINTS);
glVertex3f(-80.0+i*8,0.0f, 0.0f);
glEnd();
curSize+=step*2;
}
break;
case 1:
glRotatef((GLfloat)g_mousePos_x, -1.0, 0.0, 0.0);
glRotatef((GLfloat)g_mousePos_y, 0.0, -1.0, 0.0);
//绘制一条宽度为的直线
glLineWidth(5); //设置线宽
glBegin(GL_LINES);
glTexCoord2f(0.0f,0.0f); glVertex3f(-70.0f,60.0f,0.0f);
glTexCoord2f(1.0f,1.0f); glVertex3f(70.0f,60.0f,0.0f);
glEnd();
//绘制一条宽度为的直线
glLineWidth(3); //设置线宽
glBegin(GL_LINES);
glTexCoord2f(0.0f,0.0f); glVertex3f(-70.0f,0.0f,0.0f);
glTexCoord2f(1.0f,1.0f); glVertex3f(70.0f,0.0f,0.0f);
glEnd();
//绘制一条宽度为的直线
glLineWidth(1); //设置线宽
glBegin(GL_LINES);
glTexCoord2f(0.0f,0.0f); glVertex3f(-70.0f,-60.0f,0.0f);
glTexCoord2f(1.0f,1.0f); glVertex3f(70.0f,-60.0f,0.0f);
glEnd();
break;
case 2:
//绘制三角形
glRotatef((GLfloat)g_mousePos_x, -1.0, 0.0, 0.0);
glRotatef((GLfloat)g_mousePos_y, 0.0, -1.0, 0.0);
glBindTexture(GL_TEXTURE_2D,texture[selection]);
glBegin(GL_TRIANGLES);
glTexCoord2f(0.0f,0.0f); glVertex3f(0, 60, 0);
glTexCoord2f(1.0f,0.0f); glVertex3f(-60, -60, 0);
glTexCoord2f(0.5f,1.0f); glVertex3f(60, -60, 0);
glEnd();
break;
case 3:
//绘制矩形
glRotatef((GLfloat)g_mousePos_x, -1.0, 0.0, 0.0);
glRotatef((GLfloat)g_mousePos_y, 0.0, -1.0, 0.0);
glBindTexture(GL_TEXTURE_2D,texture[selection]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f); glVertex3f(60, 60, 0);
glTexCoord2f(0.0f,1.0f); glVertex3f(60, -60, 0);
glTexCoord2f(1.0f,1.0f); glVertex3f(-60, -60, 0);
glTexCoord2f(1.0f,0.0f); glVertex3f(-60, 60, 0);
glEnd();
break;
}
glPopMatrix();
glutSwapBuffers(); //刷新命令缓冲区
}
void SpecialKeys(int key,int x,int y)
{
if(key == GLUT_KEY_UP) xRot-= 5.0f;
if(key == GLUT_KEY_DOWN) xRot += 5.0f;
if(key == GLUT_KEY_LEFT) yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT) yRot += 5.0f;
if(xRot > 356.0f) xRot = 0.0f;
if(xRot < -1.0f) xRot = 355.0f;
if(yRot > 356.0f) yRot = 0.0f;
if(yRot < -1.0f) yRot = 355.0f;
//刷新窗口
glutPostRedisplay();
}
//键盘事件
void KeyPressFunc(unsignedchar key,int x, int y)
{
if(key == 32)
{
nStep++;
if(nStep > 3)
nStep = 0;
}
switch(nStep)
{
case 0:
glutSetWindowTitle("Points");
break;
case 1:
glutSetWindowTitle("Lines");
break;
case 2:
glutSetWindowTitle("Triangle");
break;
case 3:
glutSetWindowTitle("Rectangle");
break;
}
switch(key)
{
case 'l':
case 'L':
selection = 0;
break;
case 'n':
case 'N':
selection = 1;
break;
}
glutPostRedisplay();
}
int ImageLoad(char*filename, Image *image)
{
FILE *file;
unsigned long size;//图形的大小(字节数)
unsigned long i; //标准计数器
unsigned shortint planes;//图形的平面数(必须为)
unsigned shortint bpp;//每个像素的位数(必须为)
char temp; //临时的颜色保存,bgr -rgb变换
// 确保文件存在。只读打开一个二进制文件,允许读数据
if ((file = fopen(filename, "rb"))== NULL)
{
printf("File Not Found: %s\n", filename);
return 0;
}
// 用来设定文件的当前读取位置
// 意思是把文件的读写位置向后移动个字节,跳过文件的头部,准备读取宽度和高度
fseek(file, 18,SEEK_CUR);
// 读取宽度
//fread(buffer,size,count,fp)
//buffer:是一个指针,对fread来说,它是读入数据的存放地址
//size:要读写的字节数
//count:要进行读写多少个size字节的数据项
//fp:文件型指针
if ((i = fread(&image->sizeX, 4, 1, file)) !=1)
{
printf("Error reading width from %s. \n",filename);
return 0;
}
printf("Width of %s: %lu\n", filename,image->sizeX);
// 读取高度
if ((i = fread(&image->sizeY, 4, 1, file)) !=1)
{
printf("Error reading height from %s. \n",filename);
return 0;
}
printf("Height of %s: %lu\n", filename,image->sizeY);
// 计算图形的大小(每个像素bits或bytes)
size =image->sizeX * image->sizeY * 3;
// planes = getshort(file);
if ((fread(&planes, 2, 1, file)) != 1)
{
printf("Error reading planes from %s. \n",filename);
return 0;
}
if (planes != 1)
{
printf("Planes from %s is not 1: %u\n",filename, planes);
return 0;
}
// bpp = getshort(file);
if ((i = fread(&bpp, 2, 1, file)) != 1)
{
printf("Error reading bpp from %s. \n",filename);
return 0;
}
if (bpp != 24)
{
printf("Bpp from %s is not 24: %u\n", filename,bpp);
return 0;
}
// 跳过余下的头文件数据
fseek(file, 24,SEEK_CUR);
// 读取数据
image->data = (char*) malloc (size);
if (image->data == NULL)
{
printf("Error allocating memory for color-corrected imagedata");
return 0;
}
if ((i = fread(image->data, size, 1, file)) != 1)
{
printf("Error reading image data from %s. \n",filename);
return 0;
}
// 交换颜色bgr -> rgb
for (i = 0; i < size; i += 3)
{
temp =image->data[i];
image->data[i] = image->data[i+2];
image->data[i+2] = temp;
}
// 完成
return 1;
}
// 读取bitmaps并转化成纹理
void LoadGLTextures()
{
Image *image1;
image1 = (Image*)malloc (sizeof(Image));
if (image1 == NULL)
{
printf("Error allocating space for image");
exit(0);
}
if (!ImageLoad("green.bmp",image1))
exit(1);
// 线性滤波
// 指定纹理对象的数量和一个指针,这个指针指向一个无符号整型数组
glGenTextures(1,&texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]); // 绑定D纹理
// 当图片大于当前纹理时线性缩放
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 当图片小于当前纹理时线性缩放
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//void glTexImage2D(GLenum target, GLint level, GLintinternalformat, GLsizei width, GLsizei height, GLint border, GLenum format,GLenum type, void *data)
//target变量用来指定一维、二维和三维的纹理贴图
//level参数指定了这些函数所加载的mip贴图层次。对于非mip贴图的纹理,总是可以把这个参数设置为
//internalformat参数会告诉OpenGL我们希望在每个纹理单元中储存多少颜色成分
//width、height指定了被加载纹理的宽度和高度,如果是三维则还有深度。这些值必须是的整数次方
//border参数允许为纹理贴图指定一个边界宽度。目前可以把这个值设置为
//最后个参数format、type和data与用于把图像数据放入颜色缓冲区的glDrawPixels函数的对应参数相同
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY,
0, GL_RGB,GL_UNSIGNED_BYTE, image1->data);
// Nearest 滤波
glGenTextures(1,&texture[1]);
glBindTexture(GL_TEXTURE_2D, texture[1]); // 绑定D纹理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY,
0, GL_RGB,GL_UNSIGNED_BYTE, image1->data);
}
// 初始化opengl的一些参数
void init()
{
// 读取一张纹理
LoadGLTextures();
glEnable(GL_TEXTURE_2D); // 我们要使用纹理需要打开这个
}
void spinDisplay(void){
yRot -= 5.0f;
if(yRot > 356.0f) yRot = 0.0f;
if(yRot < -1.0f) yRot = 355.0f;
glutPostRedisplay();
}
void mouse(int button,int state,int x,int z)
{
switch(button)
{
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN)
{
g_tmpX = x;
g_tmpY = z;
g_rotate = true; //鼠标移动时需要用到的判断变量
}
else
{
g_rotate = false;
}
break;
}
}
void motion(int x,int z)
{
if(g_rotate)
{
g_mousePos_y =(g_mousePos_y + (x - g_tmpX))%360;
g_mousePos_x =(g_mousePos_x + (z - g_tmpY))%360;
g_tmpX = x;
g_tmpY = z;
glutPostRedisplay();
}
}
int main(int argc,char* argv[])
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE| GLUT_RGB );
glutInitWindowSize(600,600);//这两个对窗口初始化的函数必须放在glutCreateWindow函数之前才会起效果
glutInitWindowPosition(100,100);
glutCreateWindow("Points");
init();
glutReshapeFunc(ChangeSize);
glutKeyboardFunc(KeyPressFunc);//对于当前的窗口设置keyboard的回调
glutSpecialFunc(SpecialKeys);//设置特殊键响应回调函数
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;
}
运行结果: