且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

更新时间:2022-10-01 19:03:49

本节书摘来自华章出版社《Unity着色器和屏幕特效开发秘笈》一 书中的第2章,第2.4节,作者:(美)Kenny Lammers,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.4 压缩和混合纹理贴图

纹理也可用于存储大量的数据,但不是一般意义上我们所知道的像素的颜色信息,而是存储在X和Y方向上以及RGBA通道上的多组像素信息。我们可以将多个图像打包存储在单一的RGBA纹理上,然后通过着色器代码提取这些元素,我们就可以使用每个图片的R、G、B和A通道的信息作为一个独立的纹理了。
下图是将单个灰度图像压缩至一个独立的RGBA纹理的效果:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

这么做对我们有什么用处呢?首先,从你的应用程序实际内存占用方面来看,纹理在整个应用程序中占有很大比例。因此,为了减少应用程序的大小,我们可以查看使用着色器时涉及的所有图片,看看我们是否可以将它们合并成一个独立的纹理。
任何灰度的图片都可以压缩至另一个RGBA通道的纹理贴图中。一开始这听起来似乎有点奇怪,但是本节即将揭示压缩纹理的一个用处,而且在我们的着色器中也会使用这种纹理。
使用这种压缩纹理的一个典型例子就是当我们需要将一组纹理混合显示在一个单一的物体表面时。这种情况大多出现在地形着色器当中,因为在这种情况下,你需要使用一种特殊的纹理操作或者纹理压缩的方式来完美地进行纹理混合。本节将主要讲述这种技术,并且教你如何构建一个优秀的四纹理混合地形着色器。

2.4.1 准备工作

首先在你的着色器文件夹中创建一个新的着色器文件,然后为这个着色器新建一个新的材质文件,命名约定完全取决于你的着色器和材质文件,所以请尽量保持它们的统一性方便以后的引用。
准备好你的着色器和材质之后,创建一个新的场景,以便测试我们的着色器。
你还需要准备四张你想混合的纹理。这可以是任何东西,但是为了得到一个效果更好的地形着色器,你***准备一些草地、泥土、岩石泥土或者岩石纹理。
在本节中我们将使用下面四个彩色纹理,这些纹理在本书的帮助页面都可以找到。
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

最后,我们还需要一个填充了灰度图像的混合纹理。它包含了四个混合纹理,这样可以根据它来指导我们如何将颜色纹理映射到物体表面上。
我们可以使用高复杂度的混合纹理来创建一个地形网格上的超现实的纹理分布,如下图所示:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

2.4.2 如何操作

通过输入如下代码来学习如何使用填充纹理:
1.我们需要添加一些属性到我们的Properties块中。共需要5个sampler2D类型的对象或纹理,以及两个颜色属性。
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

2.然后,我们需要创建SubShader变量,这样我们就可以访问Properties块中的数据:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

3.现在,我们已经拥有了纹理特性,并且传递到我们的SubShader函数中。为了让用户可以改变单位基础纹理的裁剪率,我们需要修改Input结构。修改之后允许我们对每个纹理使用裁剪和偏移参数:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

4.在surf函数中,得到了纹理信息并将其存储在私有变量中,这样我们就可以以一种整洁且易于理解的方式与数据进行交互作业了:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

5.然后我们使用lerp()函数(线性插值)对纹理进行混合。它需要三个参数,lerp(纹理值:a,纹理值:b,混合参数:c)。lerp()函数接受两个纹理参数,并且使用最后一个参数赋予的浮点值对它们进行纹理混合运算:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

6.最后,我们将刚才的混合纹理与色调值相乘,并使用红色通道来确定两个不同地形将形成怎样的色调:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

下图显示的是四个地形纹理混合以及建立了一个地形着色技术的效果:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

2.4.3 实现原理

混合纹理似乎需要多行代码,但其实它的背后原理还是很简单的。为了了解该技术是如何工作的,我们必须先从CGFX标准库中的lerp()函数开始。该函数使我们得到参数1和参数2之间的值并使用参数3来计算混合量。
函数 功能描述
lerp(a,b,f) 涉及线性插值(1-f ) a + b f,在这里,a和b可以匹配为一个矢量或标量类型,f可以是一个标量或者与a、b同类型的矢量。
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

所以,假如我们想得到1和2之间的中间值,我们可以将lerp()函数的第三个参数赋值为0.5,它将返回值1.5。这个函数非常适合用于我们的混合纹理,因为在一个RGBA纹理中单个通道的值就是单精度浮点数值,而且通常值的范围在0到1之间。
在着色器中,我们只是简单地利用混合纹理中的一个颜色通道,并将它作为lerp函数的权值参数(f参数),这样就可以影响每个像素的颜色值。例如,我们将提供的草地纹理、泥土纹理,以及混合纹理的红色通道三个值作为lerp()函数的三个参数。这样我们得到物体表面上的每个像素值均为正确的混色颜色。
如下图所示,我们可以使用一种更加直观的方式来展示使用lerp()函数的效果:
《Unity着色器和屏幕特效开发秘笈》—— 2.4 压缩和混合纹理贴图

着色器代码只是简单地使用了混合纹理的四个通道以及提供的几个颜色纹理,创建出了一个最终的混合纹理。这个混合纹理就可以作为我们的颜色纹理,并且用于我们的漫反射光照。