使用glEnable(GL_TEXTURE_2D)开启2D纹理功能,glDisable(GL_TEXTURE_2D)关闭纹理,默认情况下纹理是关闭的。
1) BMP文件数据前54位是文件头和信息头数据,偏移54位之后才是要读取的图像数据。
2) 要检测图像宽度的位数是否是4的整数倍,如果不是,需要补齐,并且以补齐后的内存带下分配内存。
3) 出于兼容较低版本OpenGL的目的,一般需要检测图像的长宽是否是2的整数次幂,以及长宽是否超过了当前版本所支持的最大长宽数值,若条件不符合,需要进行相应的调整,可以使用gluScaleImage函数对图像进行缩放。
使用函数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。
第四、五个参数是二维纹理像素的宽度和高度。
第六个参数是纹理边框的大小,不使用纹理边框设置为零。
第七个核第八个参数是数据格式和数据保存形式。
第九个参数是保存了纹理图像的内参块地址。
在分配纹理之前最好先获取一下程序当前所使用的纹理编号,并且在生成本次纹理之后恢复之前保存的纹理编号,从而保证不对当前程序产生影响。
通过gluPerspective和gluLookAt设置“视角”和“观察点”的相关参数。
纹理映射跟颜色的绘制一样,需要指定每一个顶点在纹理图像中所对应的位置,OpenGL会自动计算出顶点之间的其他点在纹理图像中应该对应的位置。这里注意纹理图像的坐标范围是从(0,0)到(1,1),左下角为(0,0),右上角为(1,1),鉴于之前已经在第3步中设置了坐标点大于1或者坐标点小雨0的情况的处理方法,所以不考虑显示效果的话,坐标可以任意指定。
#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取消纹理映射