opengl 红宝书_OpenGL-从文件中加载纹理

周一到周五,每天一篇,北京时间早上7点准时更新~跟着我们一起一天天进步和成长吧

点评:总体来说,这些书是给学会了OpenGL的人闲来没事看看的,新手看的话不太合适,相信学完了我们课程的学员也会这么认为,因为我们知道不少我们的学员是关注了我们的专栏了的。学完了OpenGL的人,你们看应该问题不大, 但依然不会非常深刻。我们相信在学习完毕引擎课程后,你再回过头来看这本书,你还会有不一样的感受,你会知道OpenGL甚至是泛图形底层API那些功能是为什么被发明出来的。知识就是这样,你的层次不一样,一本书读十遍有十遍感受。 那些给新手推荐这些宝书的要摸摸自己的良心了,是你没看过乱给人指路,还是看过了乱给人指路。我们也没看过这书,我们也是闲来没事听闻了这本书的传说,什么红宝书、蓝宝书,所以才好奇来看看它写了些什么玩意的。 总而言之,我们想表达一个意思,新手如果看这本书看不懂不是你的问题,是书的问题,加油,你是最胖的!

In our simple example, we generated the texture data directly in our application. However, this clearly isn’t practical in a real-world application where you most likely have images stored on disk or on the other end of a network connection. Your options are either to convert your textures into hard-coded arrays (yes, there are utilities that will do this for you) or to load them from files within your application.

在我们的例子中,我们直接在程序中生成了纹理数据。很明显,这么做是不现实的,大部分情况下,你会从网络或者磁盘上读取图片。你可以选择把纹理数据变成硬编码的数据或者是 从文件加载纹理数据。

There are lots of image file formats that store pictures with or without compression, some of which are more suited to photographs and some of which are more suited to line drawings or text. However, very few image formats exist that can properly store all of the formats supported by OpenGL or represent advanced features such as mipmaps, cube maps, and so on. One such format is .KTX, or the Khronos TeXture format, which was specifically designed for the storage of pretty much anything that can be represented as an OpenGL texture. In fact, the .KTX file format includes most of the parameters you need to pass to texturing functions such as glTextureStorage2D() and glTextureSubImage2D() to load the texture directly in the file. The structure of a .KTX file header is shown in Listing 5.38.

图片格式是多种多样的,有的压缩了,有的没压缩,其中有一些适合做图像,另一些则适合画线或者是文字。但是基本上没有什么图片格式可以存储所有的OpenGL支持的那些纹理类型以及特性的, 比如mipmap、cubemap等等。一个由khronos发明的后缀为.ktx的文件格式就是专门为OpenGL而设计的。.ktx格式包含了很多你从文件加载纹理在调用glTextureSubImage2D是需要的信息。在清单5.38中 我们展示.ktx的文件头

struct header
{
	unsigned char identifier[12];
	unsigned int endianness;
	unsigned int gltype;
	unsigned int gltypesize;
	unsigned int glformat;
	unsigned int glinternalformat;
	unsigned int glbaseinternalformat;
	unsigned int pixelwidth;
	unsigned int pixelheight;
	unsigned int pixeldepth;
	unsigned int arrayelements;
	unsigned int faces;
	unsigned int miplevels;
	unsigned int keypairbytes;
};

Listing 5.38: The header of a .KTX file

In this header, identifier contains a series of bytes that allow the application to verify that this is a legal. KTX file and endianness contains a known value that will be different depending on whether a little-endian or big-endian machine created the file. The gltype, glformat, glinternalformat, and glbaseinternalformat fields are actually the raw values of the GLenum types that will be used to load the texture. The gltypesize field stores the size, in bytes, of one element of data in the gltype type, and is used in case the endianness of the file does not match the native endianness of the machine loading the file, in which case each element of the texture must be byte swapped as it is loaded. The remaining fields —pixelwidth, pixelheight, pixeldepth, arrayelements, faces, and miplevels—store information about the dimensions of the texture. Finally, the keypairbytes field is used to allow applications to store additional information after the header and before the texture data. After this information, the raw texture data begins.

这个文件头里,identifier是一个字节数组,可以让应用程序去验证是否该文件有效。endianness变量则包含了大端还是小端编码的问题。gltype、glformat、glinternalformat以及glbaseinternalformat 则是直接可以使用的OpenGL中的那些类型的值。gltypesize存储了gltype所指示的类型的每一个元素占用多少个字节,这个参数是在图片编码机器与图片使用机器的CPU架构不一样的时候,进行数据交换用的。 剩下的数据(pixelwidth, pixelheight, pixeldepth, arrayelements, faces,miplevels)存储了纹理的维度信息。最后keypairbytes可以允许应用程序任何其他想存储在图片中的信息,这些信息会被塞到 文件头的后面图片数据的前面。

Because the .KTX file format was designed specifically for use in OpenGL-based applications, writing the code to load a .KTX file is actually pretty straightforward. Even so, a basic loader for .KTX files is included in this book’s source code. To use the loader, you can simply reserve a new name for a texture using glGenTextures(), and then pass it, along with the name of the .KTX file, to the loader. If you wish, you can even omit the OpenGL name for the texture (or pass 0) and the loader will call glCreateTextures() for you. If the .KTX file is recognized, the loader will bind the texture to the appropriate target and load it with the data from the .KTX file. An example is shown in Listing 5.39.

因为.ktx文件格式是专门为OpenGL程序设计的,读取.ktx文件格式的代码会非常直接。我们的课程代码里包含了相关的加载函数,在使用该加载API之前,你先要创建一个纹理对象,然后把.ktx的文件路径 和纹理对象一起传给这个API。如果你不想自己创建纹理对象,你这里直接传一个0过去就可以了,加载API会帮你创建纹理对象。清单5.39展示了如何使用加载API,如果.ktx文件有效的话,加载函数会把它绑定 到合适的纹理目标上,然后从文件中把纹理数据加载进去。

// Generate a name for the texture
glGenTextures(1, &texture);
// Load texture from file
sb7::ktx::file::load("media/textures/icemoon.ktx", texture);

Listing 5.39: Loading a .KTX file

If you think that Listing 5.39 looks simple, you’re right! The .KTX loader takes care of almost all the details for you. If the loader is successful in loading and allocating the texture, it will return the name of the texture you passed in (or the one it generated for you); if it fails for some reason, it will return 0. If you want to use the texture, you’ll need to bind it to one of the texture units. Don’t forget to delete the texture when you’re done with it by calling glDeleteTextures() on the name returned by the .KTX loader. Applying the texture loaded in Listing 5.39 to the whole viewport produces the image shown in Figure 5.5.

如果你觉得清单5.39的代码很简单,那就对了!因为.ktx文件的加载函数帮你处理了所有的麻烦事。如果成功创建了纹理,那么这个API会返回你传入的纹理对象。如果失败了,它会返回0. 如果你想使用这个纹理,那么你先要把它绑定到某个纹理单元上去。当你使用完了纹理后别忘了调用glDeleteTextures,传入加载API返回的那个纹理对象。当我们在程序中使用了清单5.39加载的纹理后, 我们将得到图5.5这样的一个画面。

0d127f4219f933a34e7ee57dd9b9d723.png

Texture Coordinates(纹理坐标)

In the simple example shown earlier in this chapter, we used the current fragment’s window-space coordinate as the position at which to read from the texture. You can actually use any values you want, though in a fragment shader, they will usually be derived from one of the inputs that are smoothly interpolated from across each primitive by OpenGL. It is then the vertex (or geometry or tessellation evaluation) shader’s responsibility to produce the values of these coordinates. The vertex shader will generally pull the texture coordinates from a per-vertex input and pass them through unmodified. When you use multiple textures in your fragment shader, there is nothing to stop you from using a unique set of texture coordinates for each texture, but for many applications a single set of texture coordinates will be used for every texture. A simple vertex shader that accepts a single texture coordinate and passes it through to the fragment shader is shown in Listing 5.40; the corresponding fragment shader is shown in Listing 5.41.

在这个简单的例子中,我们使用了像素在窗口坐标系下的坐标去采样纹理。这里你可以使用你想使用的任意值来做这个纹理坐标,尽管这些值是从vertex shader那边平滑的插值得到的。 所以我们的vertex shader需要输出这样的值。vertex shader通常情况下会从属性组里直接取出纹理坐标来,然后直接把它传给fragment shader。当你使用多张纹理的时候,你也可以为每一个纹理传一个 纹理坐标。清单5.40展示了一个只使用了一个纹理坐标的vertex shader,它将纹理坐标直接传递给了fragment shader。对应的fragment shader如清单5.41所示

#version 450 core
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (location = 0) in vec4 position;
layout (location = 4) in vec2 tc;
out VS_OUT
{
	vec2 tc;
} vs_out;
void main(void)
{
	// Calculate the position of each vertex
	vec4 pos_vs = mv_matrix * position;
	// Pass the texture coordinate through unmodified
	vs_out.tc = tc;
	gl_Position = proj_matrix * pos_vs;
}

Listing 5.40: Vertex shader with a single texture coordinate

The shader shown in Listing 5.41 not only takes as input the texture coordinate produced by the vertex shader, but also scales it non-uniformly. The texture’s wrapping mode is set to GL_REPEAT, which means that the texture will be repeated several times across the object.

清单5.41中展示的shader不仅将vertex shader输出的纹理坐标作为输入,并且把它缩放成了一个非单位化的状态。纹理的采样模式被设置成了GL_REPEAT,这就意味着纹理会重复很多次。

#version 450 core
layout (binding = 0) uniform sampler2D tex_object;
// Input from vertex shader
in VS_OUT
{
	vec2 tc;
} fs_in;
// Output to framebuffer
out vec4 color;
void main(void)
{
	// Simply read from the texture at the (scaled) coordinates and
	// assign the result to the shader's output.
	color = texture(tex_object, fs_in.tc * vec2(3.0, 1.0));
}

Listing 5.41: Fragment shader with a single texture coordinate

By passing a texture coordinate with each vertex, we can wrap a texture around an object. Texture coordinates can then be generated offline procedurally or assigned by hand by an artist using a modeling program and stored in an object file. If we load a simple checkerboard pattern into a texture and apply it to an object, we can see how the texture is wrapped around it. Such an example is shown in Figure 5.6. On the left is the object with a checkerboard pattern wrapped around it. On the right is the same object using a texture loaded from a file.

我们通过vertex shader传递过来的纹理坐标,来使用纹理去覆盖到物体上。纹理坐标可以是离线生成或者是在建模师在建模软件里干出来,然后存储到文件里。如果我们加载一个checkboard模式的纹理,然后把它 用到物体上,我们将能够看到纹理是如何覆盖到物体上的。图5.6展示了这样的一个例子。左边的物体是一个checkboard纹理,右边的是从文件里加载的纹理。

f6e6540a18eebb8561b455b28889e31c.png

本日的翻译就到这里,明天见,拜拜~~

第一时间获取最新桥段,请关注东汉书院以及图形之心公众号

东汉书院,等你来玩哦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值