《opengl计算机三维图形程序设计》纹理.pptx
- 格式:pptx
- 大小:12.38 MB
- 文档页数:52
OpenGL纹理目录12.1 基本步骤12.2 纹理定义12.3 纹理控制12.4 映射方式12.5 纹理坐标在三维图形中,纹理映射(Texture Mapping)的方法运用得很广,尤其描述具有真实感的物体。
比如绘制一面砖墙,就可以用一幅真实的砖墙图像或照片作为纹理贴到一个矩形上,这样,一面逼真的砖墙就画好了。
如果不用纹理映射的方法,则墙上的每一块砖都必须作为一个独立的多边形来画。
另外,纹理映射能够保证在变换多边形时,多边形上的纹理图案也随之变化。
例如,以透视投影方式观察墙面时,离视点远的砖块的尺寸就会缩小,而离视点较近的就会大些。
此外,纹理映射也常常运用在其他一些领域,如飞行仿真中常把一大片植被的图像映射到一些大多边形上用以表示地面,或用大理石、木材、布匹等自然物质的图像作为纹理映射到多边形上表示相应的物体。
纹理映射有许多种情况。
例如,任意一块纹理可以映射到平面或曲面上,且对光亮的物体进行纹理映射,其表面可以映射出周围环境的景象;纹理还可按不同的方式映射到曲面上,一是可以直接画上去(或称移画印花法),二是可以调整曲面颜色或把纹理颜色与曲面颜色混合;纹理不仅可以是二维的,也可以是一维或其它维的。
本章将详细介绍OpenGL纹理映射有关的内容:基本步骤、纹理定义、纹理控制、映射方式和纹理坐标等。
12.1 基本步骤纹理映射是一个相当复杂的过程,这节只简单地叙述一下最基本的执行纹理映射所需的步骤。
基本步骤如下:1)定义纹理、2)控制滤波、3)说明映射方式、4)绘制场景,给出顶点的纹理坐标和几何坐标。
注意:纹理映射只能在RGBA方式下执行,不能运用于颜色表方式。
下面举出一个最简单的纹理映射应用例子:例12-1 简单纹理映射应用例程(texsmpl.c)#include "glos.h"#include <GL/gl.h>#include <GL/glu.h>#include <GL/glaux.h>void myinit(void);void makeImage(void);void CALLBACK myReshape(GLsizei w, GLsizei h);void CALLBACK display(void);/* 创建纹理 */#define ImageWidth 64#define ImageHeight 64GLubyte Image[ImageWidth][ImageHeight][3];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(GL_TEXTURE_2D, 0, 3, ImageWidth,ImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, &Image[0][0][0]);/* 控制滤波 */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 CALLBACK display(void){glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);/* 设置纹理坐标和物体几何坐标 */glBegin(GL_QUADS);glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0);glTexCoord2f(0.0, 1.0); glVertex3f(-2.0, 1.0, 0.0);glTexCoord2f(1.0, 1.0); glVertex3f(0.0, 1.0, 0.0);glTexCoord2f(1.0, 0.0); glVertex3f(0.0, -1.0, 0.0);glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0);glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 0.0);glTexCoord2f(1.0, 1.0); glVertex3f(2.41421, 1.0, -1.41421); glTexCoord2f(1.0, 0.0); glVertex3f(2.41421, -1.0, -1.41421); glEnd();glFlush();}void CALLBACK myReshape(GLsizei w, GLsizei h){glViewport(0, 0, w, h);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(60.0, 1.0*(GLfloat)w/(GLfloat)h, 1.0, 30.0); glMatrixMode(GL_MODELVIEW);glLoadIdentity();glTranslatef(0.0, 0.0, -3.6);}void main(void){auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);auxInitPosition (0, 0, 500, 500);auxInitWindow ("Simple Texture Map");myinit();auxReshapeFunc (myReshape);auxMainLoop(display);}图12-1 简单纹理映射以上程序运行结果是将一块纹理映射到两个正方形上去。
第八章 OpenGL程序设计基础一、通过GLUT使用窗口系统二、几何图元与场景表达三、OpenGL中的变换四、OpenGL中的照明五、在OpenGL中使用纹理一、通过GLUT使用窗口系统OpenGL本身不涉及具体的窗口系统,但一个完整的图形程序又离不开窗口系统。
GLUT(The OpenGL Utility Toolkit)是一个独立于窗口系统的工具包。
使用它可以摆脱对具体窗口系统的依赖,直接学习OpenGL的核心内容。
1、本课程需要用到的GLUT函数void glutInit(int *argc, char **argv);void glutInitDisplayMode(unsigned int mode);void glutInitWindowSize(int width, int height);void glutInitWindowPosition(int x, int y);int glutCreateWindow(char *name); //返回值用于多窗口,本课程不涉及void glutDisplayFunc(void (*func)(void));void glutReshapeFunc(void (*func)(int width, int height));一、通过GLUT使用窗口系统void glutMouseFunc(void (*func)(int button, int state, int x, int y));void glutMotionFunc(void (*func)(int x, int y));void glutKeyboardFunc(void (*func)(unsigned int key, int x, int y);void glutIdleFunc(void (*func)(void));void glutMainLoop(void);void glutSwapBuffers(void);void glutPostRedisplay(void);注:这些函数都非常简单,在例子中看到即会,在红宝书中也有说明。
OpenGL中的三维纹理操作#define _CRT_SECURE_NO_WARNINGS#include <gl/glut.h>#include <stdio.h>#include <stdlib.h>#define WindowWidth 400#define WindowHeight 400#define WindowTitle "OpenGL纹理测试"/* 函数grab* 抓取窗⼝中的像素* 假设窗⼝宽度为WindowWidth,⾼度为WindowHeight*/#define BMP_Header_Length 54void grab(void){FILE* pDummyFile;FILE* pWritingFile;GLubyte* pPixelData;GLubyte BMP_Header[BMP_Header_Length];GLint i, j;GLint PixelDataLength;// 计算像素数据的实际长度i = WindowWidth * 3; // 得到每⼀⾏的像素数据长度while( i%4 != 0 ) // 补充数据,直到i是的倍数++i; // 本来还有更快的算法,// 但这⾥仅追求直观,对速度没有太⾼要求PixelDataLength = i * WindowHeight;// 分配内存和打开⽂件pPixelData = (GLubyte*)malloc(PixelDataLength);if( pPixelData == 0 )exit(0);pDummyFile = fopen("dummy.bmp", "rb");if( pDummyFile == 0 )exit(0);pWritingFile = fopen("grab.bmp", "wb");if( pWritingFile == 0 )exit(0);// 把dummy.bmp的⽂件头复制为新⽂件的⽂件头fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile);fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);fseek(pWritingFile, 0x0012, SEEK_SET);i = WindowWidth;j = WindowHeight;fwrite(&i, sizeof(i), 1, pWritingFile);fwrite(&j, sizeof(j), 1, pWritingFile);// 读取像素,写⼊像素数据glPixelStorei(GL_UNPACK_ALIGNMENT, 4);glReadPixels(0, 0, WindowWidth, WindowHeight,GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);fseek(pWritingFile, 0, SEEK_END);fwrite(pPixelData, PixelDataLength, 1, pWritingFile);// 释放内存和关闭⽂件fclose(pDummyFile);fclose(pWritingFile);free(pPixelData);}// 判断是否为偶数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;GLint last_texture_ID;GLuint 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中、如果图像的宽度和⾼度不是的整数次⽅,则需要进⾏缩放// 这⾥并没有检查OpenGL版本,出于对版本兼容性的考虑,按旧版本处理// 另外,⽆论是旧版本还是新版本,当图像的宽度和⾼度超过当前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和heightfree(pixels);pixels = new_pixels;width = new_width;height = new_height;}// 分配⼀个新的纹理编号glGenTextures(1, &texture_ID);if( texture_ID == 0 ){free(pixels);fclose(pFile);return 0;}// 绑定新的纹理,载⼊纹理并设置纹理参数.glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture_ID);//在绑定前,先获得原来绑定的纹理编号,以便在最后进⾏恢复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_REPLACE);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);glBindTexture(GL_TEXTURE_2D, last_texture_ID);// 之前为pixels分配的内存可在使⽤glTexImage2D以后释放// 因为此时像素数据已经被OpenGL另⾏保存了⼀份(可能被保存到专门的图形硬件中)free(pixels);return texture_ID;}/* 两个纹理对象的编号*/GLuint texGround;GLuint texWall;void display(void){// 清除屏幕glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 设置视⾓glMatrixMode(GL_PROJECTION);glLoadIdentity();//在进⾏变换前,将当前矩阵变为单位矩阵gluPerspective(75, 1, 1, 21);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(1, 5, 5, 0, 0, 0, 0, 0, 1);//前三个参数表⽰了观察点的位置,中间三个参数表⽰了观察⽬标的位置,//最后三个参数代表从(0,0,0)到(x,y,z)的直线,它表⽰了观察者认为的“上”⽅向// 使⽤“地”纹理绘制⼟地glBindTexture(GL_TEXTURE_2D, texGround);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f); glVertex3f(-8.0f, -8.0f, 0.0f);glTexCoord2f(0.0f, 5.0f); glVertex3f(-8.0f, 8.0f, 0.0f);glTexCoord2f(5.0f, 5.0f); glVertex3f(8.0f, 8.0f, 0.0f);glTexCoord2f(5.0f, 0.0f); glVertex3f(8.0f, -8.0f, 0.0f);glEnd();// 使⽤“墙”纹理绘制栅栏glBindTexture(GL_TEXTURE_2D, texWall);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f); glVertex3f(-6.0f, -3.0f, 0.0f);glTexCoord2f(0.0f, 1.0f); glVertex3f(-6.0f, -3.0f, 1.5f);glTexCoord2f(5.0f, 1.0f); glVertex3f(6.0f, -3.0f, 1.5f);glTexCoord2f(5.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f);glEnd();// 旋转后再绘制⼀个glRotatef(-90, 0, 0, 1);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f); glVertex3f(-6.0f, -3.0f, 0.0f);glTexCoord2f(0.0f, 1.0f); glVertex3f(-6.0f, -3.0f, 1.5f);glTexCoord2f(5.0f, 1.0f); glVertex3f(6.0f, -3.0f, 1.5f);glTexCoord2f(5.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f);glEnd();// 交换缓冲区,并保存像素数据到⽂件glutSwapBuffers();grab();}int main(int argc, char* argv[]){// GLUT初始化glutInit(&argc, argv);glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);glutInitWindowPosition(100, 100);glutInitWindowSize(WindowWidth, WindowHeight); glutCreateWindow(WindowTitle);glutDisplayFunc(&display);// 在这⾥做⼀些初始化glEnable(GL_DEPTH_TEST);glEnable(GL_TEXTURE_2D);texGround = load_texture("ground.bmp");texWall = load_texture("wall.bmp");// 开始显⽰glutMainLoop();return 0;}搬家于CSDN 2014-01-24的⽂章。