第五课:带纹理的立方体

转载自:http://www.opengl-tutorial.org/cn/beginners-tutorials/tutorial-5-a-textured-cube/

第五课:带纹理的立方体

本课学习如下几点:

  • 什么是UV坐标
  • 怎样自行加载纹理
  • 怎样在OpenGL中使用纹理
  • 什么是过滤?什么是mipmap?怎样使用?
  • 怎样利用GLFW更加鲁棒地加载纹理?
  • 什么是alpha通道?

关于UV坐标

给模型贴纹理时,我们需要通过UV坐标来告诉OpenGL用哪块图像填充三角形。

每个顶点除了位置坐标外还有两个浮点数坐标:U和V。这两个坐标用于访问纹理,如下图所示:

注意观察纹理是怎样在三角形上扭曲的。

自行加载.BMP图片

不用花太多心思了解BMP文件格式:很多库可以帮你加载BMP文件。但BMP格式极为简单,可以帮助你理解那些库的工作原理。所以,我们从头开始写一个BMP文件加载器,不过千万别在实际工程中使用这个实验品

如下是加载函数的声明:

1 GLuint loadBMP_custom(const char * imagepath);

使用方式如下:

1 GLuint image = loadBMP_custom("./my_texture.bmp");

接下来看看如何读取BMP文件。

首先需要一些数据。读取文件时将设置这些变量。

1 // Data read from the header of the BMP file
2 unsigned char header[54]; // Each BMP file begins by a 54-bytes header
3 unsigned int dataPos;     // Position in the file where the actual data begins
4 unsigned int width, height;
5 unsigned int imageSize;   // = width*height*3
6 // Actual RGB data
7 unsigned char * data;

现在正式开始打开文件。

1 // Open the file
2 FILE * file = fopen(imagepath,"rb");
3 if (!file)
4 {
5     printf("Image could not be openedn");
6     return 0;
7 }

文件一开始是54字节长的文件头,用于标识”这是不是一个BMP文件”、图像大小、像素位等等。来读取文件头吧:

1 if ( fread(header, 1, 54, file)!=54 ){ // If not 54 bytes read : problem
2     printf("Not a correct BMP filen");
3     return false;
4 }

文件头总是以”BM”开头。实际上,如果用十六进制编辑器打开BMP文件,您会看到如下情形:

因此得检查一下头两个字节是否确为‘B’和‘M’:

1 if ( header[0]!='B' || header[1]!='M' ){
2     printf("Not a correct BMP filen");
3     return 0;
4 }

现在可以读取文件中图像大小、数据位置等信息了:

1 // Read ints from the byte array
2 dataPos    = *(int*)&(header[0x0A]);
3 imageSize  = *(int*)&(header[0x22]);
4 width      = *(int*)&(header[0x12]);
5 height     = *(int*)&(header[0x16]);

如果这些信息缺失,您得手动补齐:

1 // Some BMP files are misformatted, guess missing information
2 if (imageSize==0)    imageSize=width*height*3; // 3 : one byte for each Red, Green and Blue component
3 if (dataPos==0)      dataPos=54; // The BMP header is done that way

现在我们知道了图像的大小,可以为之分配一些内存,把图像读进去:

1 // Create a buffer
2 data = new unsigned char [imageSize];
3 
4 // Read the actual data from the file into the buffer
5 fread(data,1,imageSize,file);
6 
7 //Everything is in memory now, the file can be closed
8 fclose(file);

到了真正的OpenGL部分了。创建纹理和创建顶点缓冲差不多:创建一个纹理、绑定、填充、配置。

在glTexImage2D函数中,GL_RGB表示颜色由三个分量构成,GL_BGR则说明了颜色在内存中的存储格式。实际上,BMP存储的并不是RGB,而是BGR,因此得把这个告诉OpenGL。

 1 // Create one OpenGL texture
 2 GLuint textureID;
 3 glGenTextures(1, &textureID);
 4 
 5 // "Bind" the newly created texture : all future texture functions will modify this texture
 6 glBindTexture(GL_TEXTURE_2D, textureID);
 7 
 8 // Give the image to OpenGL
 9 glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
10 
11 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
12 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

稍后再解释最后两行代码。同时,得在C++代码中使用刚写好的函数加载一个纹理:

1 GLuint Texture = loadBMP_custom("uvtemplate.bmp");

另外十分重要的一点:** 使用2次幂(power-of-two)的纹理!**

  • 优质纹理: 128128, 256256, 10241024, 2*2…
  • 劣质纹理: 127128, 35, …
  • 勉强可以但很怪异的纹理: 128*256

在OpenGL中使用纹理

先来看看片段着色器。大部分代码一目了然:

 1 #version 330 core
 2 
 3 // Interpolated values from the vertex shaders
 4 in vec2 UV;
 5 
 6 // Ouput data
 7 out vec3 color;
 8 
 9 // Values that stay constant for the whole mesh.
10 uniform sampler2D myTextureSampler;
11 
12 void main(){
13 
14     // Output color = color of the texture at the specified UV
15     color = texture( myTextureSampler, UV ).rgb;
16 }

注意三点:

  • 片段着色器需要UV坐标。看似合情合理。
  • 同时也需要一个”Sampler2D”来获知要加载哪一个纹理(同一个着色器中可以访问多个纹理)
  • 最后一点,用texture()访问纹理,该方法返回一个(R,G,B,A)的vec4变量。马上就会了解到分量A。

顶点着色器也很简单,只需把UV坐标传给片段着色器:

 1 #version 330 core
 2 
 3 // Input vertex data, different for all executions of this shader.
 4 layout(location = 0) in vec3 vertexPosition_modelspace;
 5 layout(location = 1) in vec2 vertexUV;
 6 
 7 // Output data ; will be interpolated for each fragment.
 8 out vec2 UV;
 9 
10 // Values that stay constant for the whole mesh.
11 uniform mat4 MVP;
12 
13 void main(){
14 
15     // Output position of the vertex, in clip space : MVP * position
16     gl_Position =  MVP * vec4(vertexPosition_modelspace,1);
17 
18     // UV of the vertex. No special space for this one.
19     UV = vertexUV;
20 }

还记得第四课中的”layout(location = 1) in vec2 vertexUV”吗?我们得在这儿把相同的事情再做一遍,但这次的缓冲中放的不是(R,G,B)三元组,而是(U,V)数对。

 1 // Two UV coordinatesfor each vertex. They were created with Blender. You'll learn shortly how to do this yourself.
 2 static const GLfloat g_uv_buffer_data[] = {
 3     0.000059f, 1.0f-0.000004f,
 4     0.000103f, 1.0f-0.336048f,
 5     0.335973f, 1.0f-0.335903f,
 6     1.000023f, 1.0f-0.000013f,
 7     0.667979f, 1.0f-0.335851f,
 8     0.999958f, 1.0f-0.336064f,
 9     0.667979f, 1.0f-0.335851f,
10     0.336024f, 1.0f-0.671877f,
11     0.667969f, 1.0f-0.671889f,
12     1.000023f, 1.0f-0.000013f,
13     0.668104f, 1.0f-0.000013f,
14     0.667979f, 1.0f-0.335851f,
15     0.000059f, 1.0f-0.000004f,
16     0.335973f, 1.0f-0.335903f,
17     0.336098f, 1.0f-0.000071f,
18     0.667979f, 1.0f-0.335851f,
19     0.335973f, 1.0f-0.335903f,
20     0.336024f, 1.0f-0.671877f,
21     1.000004f, 1.0f-0.671847f,
22     0.999958f, 1.0f-0.336064f,
23     0.667979f, 1.0f-0.335851f,
24     0.668104f, 1.0f-0.000013f,
25     0.335973f, 1.0f-0.335903f,
26     0.667979f, 1.0f-0.335851f,
27     0.335973f, 1.0f-0.335903f,
28     0.668104f, 1.0f-0.000013f,
29     0.336098f, 1.0f-0.000071f,
30     0.000103f, 1.0f-0.336048f,
31     0.000004f, 1.0f-0.671870f,
32     0.336024f, 1.0f-0.671877f,
33     0.000103f, 1.0f-0.336048f,
34     0.336024f, 1.0f-0.671877f,
35     0.335973f, 1.0f-0.335903f,
36     0.667969f, 1.0f-0.671889f,
37     1.000004f, 1.0f-0.671847f,
38     0.667979f, 1.0f-0.335851f
39 };

上述UV坐标对应于下面的模型:

其余的就很清楚了。创建一个缓冲、绑定、填充、配置,像往常一样绘制顶点缓冲对象。要注意把glVertexAttribPointer的第二个参数(大小)3改成2。

结果如下:

放大后:

什么是过滤和mipmap?怎样使用?

正如在上面截图中看到的,纹理质量不是很好。这是因为在loadBMP_custom函数中,有如下两行代码:

1 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

这意味着在片段着色器中,texture()将直接提取位于(U,V)坐标的纹素(texel)。

有几种方法可以改善这一状况。

线性过滤(Linear filtering)

若采用线性过滤。texture()会查看周围的纹素,然后根据UV坐标距离各纹素中心的距离来混合颜色。这就避免了前面看到的锯齿状边缘。

线性过滤可以显著改善纹理质量,应用的也很多。但若想获得更高质量的纹理,可以采用各向异性过滤,不过速度有些慢。

各向异性过滤(Anisotropic filtering)

这种方法逼近了真正片断中的纹素区块。例如下图中稍稍旋转了的纹理,各向异性过滤将沿蓝色矩形框的主方向,作一定数量的采样(即所谓的”各向异性层级”),计算出其内的颜色。

Mipmaps

线性过滤和各向异性过滤都存在一个共同的问题。那就是如果从远处观察纹理,只对4个纹素作混合显得不够。实际上,如果3D模型位于很远的地方,屏幕上只看得见一个片断(像素),那计算平均值得出最终颜色值时,图像所有的纹素都应该考虑在内。很显然,这种做法没有考虑性能问题。撇开两种过滤方法不谈,这里要介绍的是mipmap技术:

  • 一开始,把图像缩小到原来的1/2,然后依次缩小,直到图像只有1x1大小(应该是图像所有纹素的平均值)
  • 绘制模型时,根据纹素大小选择合适的mipmap。
  • 可以选用nearest、linear、anisotropic等任意一种滤波方式来对mipmap采样。
  • 要想效果更好,可以对两个mipmap采样然后混合,得出结果。

好在这个比较简单,OpenGL都帮我们做好了,只需一个简单的调用:

1 // When MAGnifying the image (no bigger mipmap available), use LINEAR filtering
2 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3 // When MINifying the image, use a LINEAR blend of two mipmaps, each filtered LINEARLY too
4 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
5 // Generate mipmaps, by the way.
6 glGenerateMipmap(GL_TEXTURE_2D);

怎样利用GLFW加载纹理?

我们的loadBMP_custom函数很棒,因为这是我们自己写的!不过用专门的库更好。GLFW就可以加载纹理(仅限TGA文件):

 1 GLuint loadTGA_glfw(const char * imagepath){
 2 
 3     // Create one OpenGL texture
 4     GLuint textureID;
 5     glGenTextures(1, &textureID);
 6 
 7     // "Bind" the newly created texture : all future texture functions will modify this texture
 8     glBindTexture(GL_TEXTURE_2D, textureID);
 9 
10     // Read the file, call glTexImage2D with the right parameters
11     glfwLoadTexture2D(imagepath, 0);
12 
13     // Nice trilinear filtering.
14     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
15     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
16     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
17     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
18     glGenerateMipmap(GL_TEXTURE_2D);
19 
20     // Return the ID of the texture we just created
21     return textureID;
22 }

压缩纹理

学到这儿,您可能会问:那JPEG格式的纹理又该怎样加载呢?

简答:用不着考虑这些文件格式,您还有更好的选择。

创建压缩纹理

  • 下载The Compressonator,一款ATI工具
  • 用它加载一个二次幂纹理
  • 将其压缩成DXT1、DXT3或DXT5格式(这些格式之间的差别请参考Wikipedia):

  • 生成mipmap,这样就不用在运行时生成mipmap了。
  • 导出为.DDS文件。

至此,图像已压缩为可被GPU直接使用的格式。在着色中随时调用texture()均可以实时解压。这一过程看似很慢,但由于它节省了很多内存空间,传输的数据量就少了。传输内存数据开销很大;纹理解压缩却几乎不耗时(有专门的硬件负责此事)。一般情况下,采用压缩纹理可使性能提升20%。

使用压缩纹理

来看看怎样加载压缩纹理。这和加载BMP的代码很相似,只不过文件头的结构不一样:

 1 GLuint loadDDS(const char * imagepath){
 2 
 3     unsigned char header[124];
 4 
 5     FILE *fp;
 6 
 7     /* try to open the file */
 8     fp = fopen(imagepath, "rb");
 9     if (fp == NULL)
10         return 0;
11 
12     /* verify the type of file */
13     char filecode[4];
14     fread(filecode, 1, 4, fp);
15     if (strncmp(filecode, "DDS ", 4) != 0) {
16         fclose(fp);
17         return 0;
18     }
19 
20     /* get the surface desc */
21     fread(&header, 124, 1, fp); 
22 
23     unsigned int height      = *(unsigned int*)&(header[8 ]);
24     unsigned int width         = *(unsigned int*)&(header[12]);
25     unsigned int linearSize     = *(unsigned int*)&(header[16]);
26     unsigned int mipMapCount = *(unsigned int*)&(header[24]);
27     unsigned int fourCC      = *(unsigned int*)&(header[80]);

文件头之后是真正的数据:紧接着是mipmap层级。可以一次性批量地读取:

1 unsigned char * buffer;
2     unsigned int bufsize;
3     /* how big is it going to be including all mipmaps? */
4     bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
5     buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
6     fread(buffer, 1, bufsize, fp);
7     /* close the file pointer */
8     fclose(fp);

这里要处理三种格式:DXT1、DXT3和DXT5。我们得把”fourCC”标识转换成OpenGL能识别的值。

 1 unsigned int components  = (fourCC == FOURCC_DXT1) ? 3 : 4;
 2     unsigned int format;
 3     switch(fourCC)
 4     {
 5     case FOURCC_DXT1:
 6         format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
 7         break;
 8     case FOURCC_DXT3:
 9         format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
10         break;
11     case FOURCC_DXT5:
12         format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
13         break;
14     default:
15         free(buffer);
16         return 0;
17     }

像往常一样创建纹理:

1 // Create one OpenGL texture
2     GLuint textureID;
3     glGenTextures(1, &textureID);
4 
5     // "Bind" the newly created texture : all future texture functions will modify this texture
6     glBindTexture(GL_TEXTURE_2D, textureID);

现在只需逐个填充mipmap:

 1 unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
 2     unsigned int offset = 0;
 3 
 4     /* load the mipmaps */
 5     for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
 6     {
 7         unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
 8         glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 
 9             0, size, buffer + offset);
10 
11         offset += size;
12         width  /= 2;
13         height /= 2;
14     }
15     free(buffer); 
16 
17     return textureID;

反转UV坐标

DXT压缩源自DirectX。和OpenGL相比,DirectX中的V纹理坐标是反过来的。所以使用压缩纹理时,得用(coord.v, 1.0-coord.v)来获取正确的纹素。可以在导出脚本、加载器、着色器等环节中执行这步操作

总结

刚才我们学习了创建、加载以及在OpenGL中使用纹理。

总的来说,压缩纹理体积小、加载迅速、使用便捷,应该只用压缩纹理;主要的缺点是得用The Compressonator来转换图像格式。

练习

  • 源代码中实现了DDS加载器,但没有做纹理坐标的改动(译者注:指文中讲述的反转 UV坐标)。在适当的位置添加该功能,以使正方体正确显示。
  • 试试各种DDS格式。所得结果有何不同?压缩率呢?
  • 试试在The Compressonator不生成mipmap。结果如何?请给出3种方案解决这一问题。

参考文献


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介   本书从实践的角度出发,详细介绍3D游戏开发的高级技术,并具体描述了一个游戏引擎的构建过程。全书着重讨论三个主题:游戏开发的一般过程;实时渲染过程;角色动画。所有主题均围绕一个具体的游戏开发系统Fly3D SDK2.0加以介绍。   本书旨在为当今的三维游戏引擎技术提供一个综合的解决方案,使读者尽快地进入开发者角色,了解整个游戏的开发过程并初步具备游戏引擎开发能力。   本书适合作为高等院校相关专业的教学参考书,同时可供相关技术人员和游戏开发人员阅读。 编辑推荐   本书从实践的角度出发,详细介绍3D游戏开发的高级技术,并具体描述了一个游戏引擎的构建过程。全书着重讨论三个主题:游戏开发的一般过程;实时渲染过程;角色动画。所有主题均围绕一个具体的游戏开发系统Fly3D SDK2.0加以介绍。   本书旨在为当今的三维游戏引擎技术提供一个综合的解决方案,使读者尽快地进入开发者角色,了解整个游戏的开发过程并初步具备游戏引擎开发能力。   本书适合作为高等院校相关专业的教学参考书,同时可供相关技术人员和游戏开发人员阅读。 作者简介   Alan Watt 英国谢菲尔德大学计算机科学系讲师,是该校计算机图表学研究室主任,曾经编写过多本优秀著作,包括《3D计算机图形学》和《The Computer Lmage》。 Fabio Policarpo 工作在里约热内卢的软件开发者,他是Paralelo计算机公司的创始人,目前正致力于三维动作多玩家游戏的研究。 目录 出版者的话 专家指导委员会 译者序 前言 第一部分 高级游戏系统剖析 第1章 高级游戏系统剖析I:构造过程和静态光照 1.1 数据结构 1.1.1 顶点 1.1.2 面 1.1.3 包围盒 1.2 构造过程 1.2.1 从场景几何中创建BSP树 1.2.2 路径规划的凸体和PVS计算 1.2.3 处理复杂的地形 1.2.4 BSP叶节点中的面 1.2.5 寻找叶凸体 1.2.6 凸体和伪人口 1.2.7 潜在可视集 1.3 光照贴图的构造 1.3.1 生成光照贴图的坐标 1.3.2 光照贴图的打包 1.3.3 对光照贴图的解释 1.4 BSP管理 1.5 高级静态光照——辐射度 附录1.1 构造实践 附录1.2 辐射度理论基础 第2章 高级游戏系统剖析Ⅱ:实时处理 2.1 视见和BSP 2.1.1 生成视见约束体的面 2.1.2 远近裁剪面和视见约束体 2.2 照相机控制 2.3 使用BSP的基本碰撞检测和反弹 2.3.1 碰撞和BSP遍历 2.3.2 粒子,场景检测和反弹 2.4 特殊的碰撞检测和反弹 2.4.1 AABB的定义 2.4.2 AABB类的定义和静态成员的定义 2.4.3 碰撞检测和碰撞反弹 2.4.4 使用AABB的伪碰撞反弹 2.4.5 使用AABB的碰撞检测 2.4.6 AABB顶点与场景面相交 2.4.7 场景顶点与AABB面相交 2。4.8 AABB边与场景边相交 2.4.9 更精确的碰撞检测 2.4.10 使用碰撞阈值 2.5 基本的路径规划 附录2.1 实时处理的演示 第3章 高级游戏系统剖析Ⅲ:软件设计与应用编程 3.1 应用的种类 3.1.1 插件 3.1.2 前端 3.1.3 工具 3.2 Fly3D引擎体系结构 3.2.1 FlyMath 3.2.2 FlyDirectX 3.2.3 FlyRender 3.2.4 FlyEngine 附录3.1 编写一个插件 第二部分 实时渲染 第4章 实时渲染 4.1 简介 4.2 顶点、像素和贴图 4.2.1 基本的逐像素着色 4.2.2 着色和坐标空间 4.2.3 25年来主流的插值着色方法和颜色贴图 4.2.4 标量表示 4.3 因式分解法 4.3.1 使用因式分解着色模型的逐像素着色——各向同性模型 4.3.2 使用因式分解着色模型的逐像素着色——各向异性模型 4.4 BRDF和真实材质 4.5 使用BRDF进行逐像素着色 4.6 环境贴图参数化 4.6.1 环境贴图参数化:立方映射 4.6.2 环境贴图参数化:球面映射 4.6.3 环境贴图参数化:对偶抛物面贴图 4.6.4 环境贴图——可比点 4.6.5 立方贴图和向量规范化 4.7 实现BRDF:可分离的近似 4.8 着色语言和着色器 4.8.1 着色语言:简单的历史回顾 4.8.2 RenderMan着色语言 4.8.3 实时渲染的着色语言 第5章 实时渲染:实践 5.1 基本着色器 5.1.1 渲染状态 5.1.2 着色器排序 5.1.3 着色器类的实现 5.2 渲染状态 5.2.1 全局设定 5.2.2 局部设定 5.3 着色器实例 5.3.1 环境映射和铬映射效果——玻璃、金属和铬 5.3.2 移动发光告示牌
实验四 DirectX光照、材质和纹理 实验报告 项目1: DirectX材质和光照实验 在例程Cube的基础上,完成以下步骤: 实现平行光照明。 黄色材质设定。 增加一个蓝色点光源。 在实验过程中注意灯光设置的方向、颜色等参数设定。 实现平行光照明 黄色材质设定 在头文件d3dUtility.h中添加光照的函数等等 在源文件d3dUtility.cpp中添加光源 在源文件cube.cpp中修改和添加相关内容 首先使立方体停止旋转 然后改一串代码 接下来在bool Setup函数中作如下代码 注释与修改两串代码 //Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); mtrl.Emissive = d3d::BLACK; 运行结果如下 然后改变光的方向 增加一个蓝色点光源 项目2: DirectX 纹理实验 在项目1的程序基础上完成一下步骤: 新增一个立方体增加纹理,图片请自己准备。 纹理立方体的实现请才有cube类的方式实现。 *在纹理立方体中能够用键盘来控制立方体的旋转。 以上三点中,"3)"属于自主研究题,有一定的难度。 1) 2) 我调试的时候代码确实是没有错误的,但出现了一个奇怪的梯形,通过查网络、问同 学都没能解决这个问题。希望老师能够谅解。 项目3: 网格模型实验 在项目1的程序基础上完成一下步骤: 用bigship1.x创建一个飞机网格模型。 在场景中增加摄像机类,实现键盘控制的三维控件漫游。 ----------------------- directX实验四报告全文共13页,当前为第1页。 directX实验四报告全文共13页,当前为第2页。 directX实验四报告全文共13页,当前为第3页。 directX实验四报告全文共13页,当前为第4页。 directX实验四报告全文共13页,当前为第5页。 directX实验四报告全文共13页,当前为第6页。 directX实验四报告全文共13页,当前为第7页。 directX实验四报告全文共13页,当前为第8页。 directX实验四报告全文共13页,当前为第9页。 directX实验四报告全文共13页,当前为第10页。 directX实验四报告全文共13页,当前为第11页。 directX实验四报告全文共13页,当前为第12页。 directX实验四报告全文共13页,当前为第13页。
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明
要给立方体的6个面贴不同的纹理,可以按照以下步骤进行操作: 1. 定义立方体的顶点坐标和纹理坐标 首先需要定义立方体的顶点坐标和纹理坐标。对于每个面,都需要指定4个顶点和对应的纹理坐标。可以使用以下代码定义立方体的顶点坐标和纹理坐标: ``` GLfloat vertices[] = { // 前面 -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // 后面 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // 左面 -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // 右面 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // 上面 -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // 下面 -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 1.0f, }; ``` 其中,每个顶点的坐标由3个GLfloat类型的数值组成,分别表示x、y、z三个轴的坐标。每个顶点的纹理坐标由2个GLfloat类型的数值组成,分别表示纹理坐标系下的u、v坐标。 2. 加载纹理图片 然后需要加载6张不同的纹理图片。可以使用SOIL库(Simple OpenGL Image Library)来加载图片。使用以下代码加载纹理图片: ``` GLuint texture[6]; // 加载纹理图片 texture[0] = SOIL_load_OGL_texture("front.jpg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); texture[1] = SOIL_load_OGL_texture("back.jpg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); texture[2] = SOIL_load_OGL_texture("left.jpg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); texture[3] = SOIL_load_OGL_texture("right.jpg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); texture[4] = SOIL_load_OGL_texture("up.jpg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); texture[5] = SOIL_load_OGL_texture("down.jpg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); // 设置纹理参数 for (int i = 0; i < 6; i++) { glBindTexture(GL_TEXTURE_2D, texture[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } ``` 其中,SOIL_load_OGL_texture函数用于加载图片,第一个参数是图片的路径,第二个参数指定图片的格式,第三个参数是纹理的ID,第四个参数是加载选项。加载完图片后,需要对纹理进行一些设置,例如指定纹理的过滤方式和纹理坐标的环绕方式。 3. 绘制立方体 最后,绘制立方体时需要将6个面分别绑定不同的纹理。可以使用以下代码绘制立方体: ``` glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBindTexture(GL_TEXTURE_2D, texture[0]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.5f, 0.5f); glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.5f, 0.5f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.5f, 0.5f); glEnd(); glBindTexture(GL_TEXTURE_2D, texture[1]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f, -0.5f, -0.5f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f, 0.5f, -0.5f); glEnd(); glBindTexture(GL_TEXTURE_2D, texture[2]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5f, 0.5f, 0.5f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); glEnd(); glBindTexture(GL_TEXTURE_2D, texture[3]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(0.5f, -0.5f, 0.5f); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.5f, -0.5f); glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.5f, -0.5f); glTexCoord2f(0.0f, 1.0f); glVertex3f(0.5f, 0.5f, 0.5f); glEnd(); glBindTexture(GL_TEXTURE_2D, texture[4]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, 0.5f, 0.5f); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, 0.5f, 0.5f); glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.5f, -0.5f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); glEnd(); glBindTexture(GL_TEXTURE_2D, texture[5]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, -0.5f, -0.5f); glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, -0.5f, 0.5f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, -0.5f, 0.5f); glEnd(); glDisable(GL_TEXTURE_2D); ``` 其中,glBindTexture函数用于绑定纹理,glTexCoord2f函数用于指定纹理坐标,glBegin和glEnd函数用于定义绘制的几何图形。注意需要在绘制前启用纹理功能,绘制完后再禁用纹理功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值