那个unityshader入门 unity support怎么用的

这是关于渲染基础的系列教程的苐二部分这个渲染基础的系列教程的第一部分是有关矩阵的内容。在这篇文章中我们将编写我们的第一个着色器代码并导入纹理

这个系列教程是使用Unity 5.4.0开发的,这个版本目前还是开放测试版本我使用的是build 5.4.0b10版本。

当你在Unity中创建新的场景的时候你将使用默认的相机和定向嘚光源。  通过GameObject / 3D Object / Sphere这个菜单项来创建一个简单的球体然后把这个简单的球体放在原点,并把相机放在它的前面

默认场景中的默认球体。

这昰一个非常简单的场景但是已经有很多复杂的渲染内容了。为了更好地抓住渲染过程摆脱所有奇怪的东西是非常有帮助的,首先让我們只关心渲染的基础部分

1.1 剥离那些和渲染无关的内容

通过菜单项Window / Lighting来查看场景的光照设置。 这将打开一个具有三个选项卡的光照窗口我們只对默认情况下处于激活状态的“场景”选项卡感兴趣。

这是一个关于环境光照的部分你可以在其中选择天空盒。这个天空盒目前用於场景背景、环境光照和反射让我们将其设置为none进行关闭。

在你进行设置的时候你还可以关闭预计算和实时全局光照的面板。我们不會很快使用到这些东西

在没有天空盒的情况下,环境光源会自动切换为纯色环境光源的默认颜色为深灰色,具有非常浅的蓝色色调洏反射变为纯黑色,如警告框所示

正如你可能期望的那样,球体会变得更暗背景会是纯色。但是得到的结果却是背景是深蓝色。那麼这个颜色来自哪里

背景颜色是根据摄像机来定义的。它在默认情况下会渲染天空盒但是它也会回落到纯色状态。

为什么背景颜色的透明通道值为5而不是255

要进一步简化渲染的话,请取消方向光源对象的激活或将其删除这将摆脱场景中的直接光照,以及由直接光照所投射的阴影剩下的就是背景,会用环境颜色显示球体的轮廓

2. 从物体到二维图像

我们这个非常简单的场景是用两个步骤绘制出来的。 首先图像用相机的背景颜色进行填充。然后将我们的球体的轮廓绘制在填充颜色的上面

Unity怎么知道它必须画一个球体? 我们有一个球体对潒这个对象有一个网格渲染器组件。如果此对象位于相机的视图内那么就应该出现在最终的图像中。 Unity通过检查对象的包围盒否与相机嘚视锥体相交来验证这一点

变换组件用于改变网格和包围盒的位置、方向和大小。实际上整个变换层次都会被用到,正如第1部分“矩陣”中所描述的那样如果对象会出现在相机的视图中,则这个物体会被安排进行渲染

最后,图形处理器负责渲染对象的网格 特定的渲染指令由对象的材质定义。 材质引用了着色器 - 这是一个图形处理器程序加上它可能有的任何设置。

每个组件控制着渲染哪些内容

我們的对象目前有默认材质,它使用Unity的标准着色器我们要用我们自己的着色器来代替它,我们将从头开始构建它

2.1 你的第一个着色器程序

伱的第一个着色器程序。

打开着色器文件并删除其内容所以我们可以从头开始。

着色器代码用unityshader入门关键字定义它后面是一个字符串,描述可用于选择此着色器的着色器菜单项它不需要匹配文件名。然后跟着的是填充了着色器内容的块

保存文件。 你将收到不支持这个著色器的警告因为它没有子着色器或是备选着色器。这是因为它是空的缘故

虽然这个着色器没有什么功能,但是我们可以将它分配给┅个材质因此,通过Assets / Create / Material来创建一个新材质并从着色器菜单中选择我们的着色器。

使用了你的着色器的材质

更改我们的球体对象,使我們的球体对象使用我们自己的材质而不是默认材质。 球体将变为洋红色发生这种情况是因为Unity会切换到一个错误的着色器,它使用这种顏色来引起你对问题的注意

使用了你的着色器的材质。

着色器的错误提示信息中提到子着色器你可以使用这些子着色器将多个着色器變量组合在一起。这允许你为不同的构建平台或者LOD值不同的情况下提供不同的子着色器让我们举个简单的例子来说,你可以为桌面电脑仩运行的那个应用使用一个子着色器而为移动设备上运行的应用使用另一个子着色器。

子着色器里面必须包含至少一个通道着色器通噵是对象实际被渲染的地方。 我们将使用一个通道但着色器里面可以有多个。具有多个通道意味着对象被多次渲染这是很多效果所需偠的。

我们的球体现在可能变成白色因为我们使用的是一个空通道的默认行为。如果发生这种情况这意味着我们不再收到任何着色器錯误的提示信息。但是你可能仍然在控制台中看到旧的错误提示信息。编辑器倾向于坚持提示错误信息因为当着色器重新编译而没有錯误的时候,这些错误提示信息是不会被清除的

现在是时候来编写我们自己的着色器程序了。我们用Unity的着色语言来做这个功能这是HLSL和CG著色语言的变体。我们必须用CGPROGRAM关键字指示我们的代码开始我们必须以ENDCG关键字来指示我们的代码终止。

着色器编译器现在会发出警告警告我们的着色器里面没有顶点程序和片段程序。着色器由两个程序组成也就是顶点程序和片段程序。顶点程序负责处理网格的顶点数据 这包括从对象空间到显示空间的转换,就像我们在第1部分“矩阵”中所做的那样片段程序负责对位于网格三角形内的单个像素进行渲染。

我们必须通过pragma指令告诉编译器使用哪些程序

编译器会再次发出警告,这次因为它找不到我们指定的程序这是因为我们还没有定义這些程序的缘故。

定义顶点程序和片段程序就像定义方法一样非常像C#里面的做法,虽然它们通常被称为函数让我们简单地创建两个涳的返回void的方法,并给它们适当的名称

此时,着色器将编译球体将消失。或者你仍然会得到错误信息提示这取决于你的编辑器使用嘚是哪个渲染平台。如果你使用的是Direct3D 9渲染平台你可能会得到错误信息提示。

Unity的着色器编译器接受我们的代码并将其转换为不同的程序,具体如何转换取决于目标平台不同的平台需要不同的解决方案。 例如如果是Windows 平台的话,需要的是Direct3D如果是Mac平台的话,需要的是OpenGL 如果是移动平台的话,需要的是OpenGL ES等等。 我们不是在这里处理单个编译器而是处理多个编译器。

你最终使用哪个编译器取决于你的定位甴于这些编译器并不相同,因此每个平台可能会产生不同的结果举个简单的例子来说,我们的空程序使用OpenGL和Direct3D 11的话就能正常工作,但在洳果使用的是Direct3D 9就会失败。

在编辑器中选择着色器并查看检查器窗口。它会显示有关着色器的一些信息包括当前的编译器错误。还有┅个带有“编译和显示代码”按钮和下拉菜单的“编译代码”项 如果单击“编译和显示代码”按钮,Unity将编译着色器代码并在编辑器中打開着色器代码的输出因此你可以检查生成的代码具体是什么。

着色器检查器会显示在所有平台上的错误信息。

你可以通过下拉菜单手動选择编译着色器的平台默认情况下是使用编辑器所使用的图形设备进行编译。你可以手动选择其他平台进行编译无论是你当前的构建平台,还是你有许可证的所有平台或是其他的自定义选择。这使你能够快速确保你的着色器在多个平台上能够正常的编译而不必进荇完整的构建。

要编译所选的程序请关闭弹出窗口,然后单击“编译并显示代码”按钮单击弹出窗口中的小的“显示”按钮将显示使鼡的着色器变量,但是这在现在没有用

举个简单的例子来说明,这里当我们的着色器是为OpenGlCore平台编译的时候得到的代码

生成的代码被分割为两个块,vp和fp分别用于顶点程序和片段程序。然而在OpenGL的情况下,两个程序都在vp块中两个主要函数对应于两个我们的空方法。 所以讓我们关注这两个主要函数并忽略其他代码

这里是为Direct3D 11生成的代码,让我们只剥离出那些有趣的部分它看起来很不同,但很明显代码沒有做太多的工作。

当我们处理我们的程序时我会经常显示OpenGL Core和D3D11平台的编译代码,所以就可以对具体内部发生了什么有一个比较明确的认識

要生成具有功能的着色器代码,你会需要很多模板代码比如定义公共变量、函数和其他东西的代码。 如果这是一个C#程序的话我們会将这些代码放在其他的类中。但是着色器没有类的概念它们只是一个包含所有代码的大文件,没有类或命名空间提供的分组功能

圉运的是,我们可以将代码拆分成多个文件你可以使用#include指令将不同文件的内容加载到当前文件中。一个典型的文件包括UnityCG.cginc所以让我们这樣做一下看看效果。

UnityCG.cginc是与Unity捆绑在一起的着色器导入文件之一它包括一些其他必要的文件,并包含一些通用的功能

导入文件的层次结构,从UnityCG开始

Unityunityshader入门Variables.cginc 定义了渲染所需要的一大堆着色器变量,例如变换、相机和光照数据 这些都是在需要的情况下,在Unity编辑器里面进行设置嘚

HLSLSupport.cginc 设置的是那些无论你的目标平台是什么,你都可以使用相同的代码的东西所以你不需要担心使用特定于某个平台的数据类型等事情。

UnityInstancing.cginc 是专门用于实例化支持的这是一种减少绘制调用的特定渲染技术。虽然它不直接导入文件它取决于Unityunityshader入门Variables的信息。

请注意这些文件嘚内容被有效地复制到你自己的文件中,替换了导入指令这个过程发生在预处理步骤的期间,预处理步骤会执行所有的预处理指示这些指令都是以哈希开头的语句,例如#include和#pragma在预处理步骤完成之后,着色器代码被再次处理并且被实际编译。

为了渲染某些东西我们的著色器程序必须能够输出结果。顶点程序必须返回顶点的最终坐标 一共会有多少个坐标? 四个因为我们使用的是4×4变换矩阵,正如这個系列的第1部分《矩阵》中所描述的那样

将函数的类型从void更改为float4。float4只是四个浮点数的集合但是现在,让我们只返回0

我们现在得到的錯误信息提示是关于缺少语义。着色器的编译器看到我们返回一个四个浮点数的集合但是它不知道这个四个浮点数的集合代表着什么。所以它不知道图形处理器应该用它做什么我们必须非常具体地了解我们的程序的输出。

在这种情况下我们试图输出顶点的位置。 我们必须通过将SV_POSITION语义附加到我们的方法来指明这一点 SV表示系统值,而POSITION表示最终顶点位置

片段程序应该输出一个像素的RGBA颜色值。 我们可以使鼡float4 类型返回0将为这个像素使用一个固定的颜色。

片段程序也需要语义 在这种情况下,我们必须指出最终的颜色应该写在哪里 我们使鼡SV_TARGET,它是默认的着色器目标也就是帧缓冲区,其中包含着我们正在生成的图像

但是,等等顶点程序的输出会被用作片段程序的输入。这表明片段程序应该得到一个与顶点程序的输出相匹配的参数

不管我们给参数起什么名字,我们都必须确保使用正确的语义

我们的著色器再次编译,没有错误信息提示但是球体消失了。这不应该令人感到惊讶的因为我们将球体所有的顶点折叠到了一个点。

如果你看下编译后的OpenGL核心程序你会看到他们现在在写入输出值。我们的单颜色值确实已被四分量向量所代替

D3D11程序也是如此,尽管语法是不同嘚

为了能够让我们的球再次显示出来,我们的顶点程序必须产生一个正确的顶点位置 为此,我们需要知道顶点在物体空间中的位置峩们可以通过向我们的函数添加一个带POSITION语义的变量来访问它。然后将该位置提供为齐次坐标的形式  所以它的类型是float4。

让我们从直接返回這个位置信息开始

编译后的顶点程序现在有一个顶点的输入信息并将其复制到其输出里面去。

黑色球体将变得可见但它的位置被扭曲。这是因为我们使用的是物体空间的位置我们把球在物体空间的位置当做了球的显示位置。因此移动球体将在视觉上不会产生差别。

峩们必须将原始的顶点位置乘以模型-视图-投影矩阵模型-视图-投影矩阵将对象的变换层次与相机变换和投影相结合,就像我们在这个系列嘚第1部分《矩阵》中做的那样

4 x 4的模型-视图-投影矩阵在Unityunityshader入门Variables中被定义为UNITY_MATRIX_MVP。我们可以使用mul函数将它与顶点的位置相乘这将把我们的球体正確地投影到显示器上去。你还可以移动、旋转和缩放它并且图像将按照预期改变。

如果你检查OpenGLCore平台上编译出来的顶点程序你会注意到┅个统一的变量突然出现在代码里面。即使它们没有被代码使用并且将被忽略访问矩阵这个事情触发了编译器将整个块都导入进来了。

伱还将看到矩阵乘法被编码为一堆乘法和加法

D3D11的编译器不会包含未使用的变量。 它用一个mul和三个mad指令对矩阵乘法进行编码mad指令表示加法之后紧跟着乘法。

现在我们得到了正确的形状让我们来添加一些颜色。最简单的是使用固定的颜色例如黄色。

当然你不总是想要黃色的对象。在理想情况下我们的着色器将支持任何的颜色。然后你可以使用该材质配置你要应用的颜色。这是通过着色器的属性完荿的

着色器属性在单独的块中声明。让我们将它添加到着色器代码的顶部

在新的块中放入一个名为_Tint的属性。你可以给它任何名称但通常的约定是以下划线开始,后面跟一个大写字母然后是小写字母。这么做是确保没有什么别的地方会使用这个名字以防止意外的重複名称。

属性的名称后面必须跟着一个字符串和一个类型放在圆括号中,就像调用一个方法一样该字符串用于标记材质检查器中的属性。 在这种情况下类型是isColor。

属性声明的最后一部分是给一个默认值赋值让我们将这个默认值设置为白色。

我们的tint属性不应该出现在我們着色器检查器的属性部分

当你选择材质的时候,你将看到新的Tint属性被设置为白色。你可以将Tint属性更改为任何你喜欢的颜色比如说昰绿色。

要实际使用属性我们向着色器代码添加了一个变量。它的名称必须完全匹配属性名称因此它的名称将是_Tint。然后我们可以在我們的片段程序中简单地返回这个变量

请注意,变量必须在使用之前进行定义虽然你可以改变C#类中的字段和方法的顺序,这在C#中没囿问题但是对于着色器不是这样的。着色器的编译器是从上到下工作的它不会往后看一下。

编译号的片段程序现在包括tint变量

3.3 从顶点程序传递到片段程序

到目前为止,我们给了所有的像素相同的颜色但是这是相当受限的情况。通常情况下顶点数据起着很大的作用。舉个例子来说我们可以将位置解释为颜色。然而变换后的位置不是非常有用的。因此让我们使用网格中的局部位置信息作为颜色。峩们该如何将额外的数据从顶点程序传递给片段程序

GPU通过光栅化三角形来创建图像。它需要三个经过处理的顶点并在它们之间进行插值对于由三角形覆盖的每个像素,它会调用片段程序并传递内插值后的数据。

因此顶点程序的输出并不是直接用作片段程序的输入。插值过程位于两者之间 在这个图里面SV_POSITION数据被进行内插值,但是其他数据也可以进行内插值

要访问插值后的局部位置信息,请向片段程序中添加参数 因为我们只需要X、Y和Z组件,我们用float3就足够了然后我们可以输出位置信息,就像它是一种颜色一样我们必须提供第四个顏色分量,可以简单的只保留为1

再次提醒下,我们必须使用语义来告诉编译器该如何解释这些数据这一次我们将使用TEXCOORD0。

编译好的片段著色器现在将使用内插值后的数据而不是使用统一的颜色

当然顶点程序必须输出局部位置信息才能正常工作。我们可以通过向它添加一個输出参数使用相同的TEXCOORD0语义来做到这一点。 顶点和片段函数的参数名称不需要匹配这是通过语义进行匹配的。

要通过顶点程序传递数據从位置数据localPosition里面复制X、Y和Z分量。

额外的顶点程序输出包含在编译器着色器中这样我们将看到我们的球体被正确的渲染。

将局部位置信息解释为颜色

你是否认为我们的程序的参数列表看起来很乱?其实它只会变得更糟因为我们要在顶点程序和片段程序之间传递越来樾多的数据。因为顶点程序的输出应该匹配片段程序的输入所以如果我们可以在一个地方定义参数列表将是非常方便的。 幸运的是我們可以这样做。

我们可以定义数据结构它们只是变量的集合。除了语法有点不同以外它们类似于C#中的结构。这里是一个结构体定義了我们正在内插值的数据。请注意要在定义后使用分号

使用这个结构来让我们的代码更整洁。

因为颜色值为负的情况被修正为零我們的球体在渲染的时候看起来相当的暗。 因为默认球体在物体空间中的半径为1/2所以颜色通道的值最终位于-1/2和1/2之间。 我们想将它们移动到0-1范围我们可以通过给所有通道的值加上1/2来改变这一点。

被重新着色的局部位置信息

局部位置信息有红色色调,所以只有X变量剩下了

洳果要向网格添加更多明显的细节和多样性,而不想添加更多的三角形的话那么就可以使用纹理。然后将图像投影到网格三角形上

纹悝坐标用于控制投影。纹理坐标取值范围在一个标准单位大小的二维坐标对覆盖了整个图像,而与纹理的实际长宽比无关水平坐标被稱为U,垂直坐标被称为V因此,它们通常被称为UV坐标

覆盖整个图像的UV坐标。

U坐标的值是从左到右增长的因此,在图像的左侧为0在图潒的中间为1/2,在图像的右侧为1V坐标的工作方式与U坐标相同,只是在垂直方向上作用它是从底部到顶部增长,除了Direct3D是个例外它从顶部箌底部增长。你几乎不需要担心这种差异

Unity的默认网格具有适合纹理映射的UV坐标。 顶点程序可以通过具有TEXCOORD0语义的参数来访问它们

我们的頂点程序现在使用多个输入参数。再次说明下我们可以使用一个结构来对它们进行分组。

让我们直接将UV坐标传递给片段程序来替换局蔀位置信息。

我们可以通过将它们解释为颜色通道来使得UV坐标可见就像对局部位置信息一样的处理一样。 举个简单的例子来说让我们紦U变为红色,让我们把V变为绿色而蓝色始终为1。

你会看到编译后的顶点程序现在将UV坐标从顶点数据复制到插值器的输出里面。

Unity围绕它嘚球体来生成UV坐标这样的话会在图像的两级也就是顶部和底部进行折叠。你会看到一个从北极到南极的缝图像的左侧和右侧在这条缝這里连接起来。所以沿着那个接缝你的U坐标值都是0和1。这是通过沿着接缝有重复的顶点实现的除非它们的U坐标是相同的。

UV作为颜色從正面和上方看到的效果。

要添加纹理的话你需要导入图像文件。这里是我将用于测试目的纹理

你可以通过将图像拖动到项目视图中來将图像添加到项目中。你也可以通过Asset / Import New Asset...菜单项来将图像添加到项目中在默认设置下,图像将作为二维纹理导入这很好。

使用默认设置導入纹理

要使用纹理,我们必须添加另一个着色器属性常规纹理属性的类型是二维,因为还有其他类型的纹理默认值是一个字符串,它会引用一张Unity的默认纹理(白色黑色或灰色)。

常见的一个约定是命名主要纹理为_MainTex所以我们将使用这个名字。 这也使你在需要的时候能够使用方便的Material.mainTexture属性通过脚本来访问它

现在我们可以通过拖动或通过”选择”按钮将纹理分配给我们的材质。

分配给我们的材质的纹悝

我们可以通过使用类型为sampler2D的变量来访问着色器中的纹理。

使用tex2D函数在片段程序中对具有UV坐标的纹理进行采样

现在,每个片段程序都偠采样纹理它将显示投影在球体上。纹理被包裹在球体上如预期那样,但它会在极点附近出现非常大的摆动和不稳定为什么会这样呢?

发生纹理失真是因为在三角形之间的插值是线性的Unity的球体在极点附近只有几个三角形,其中UV坐标的扭曲最大 因此,UV坐标在从顶点箌顶点的变化是非线性的但在顶点之间,它们的变化是线性的这样做的结果就是,纹理中的直线在三角形边界处突然改变方向

不同嘚网格具有不同的UV坐标,这会产生不同的映射Unity的默认球体使用经度-纬度的纹理映射,而网格是低分辨率的立方体球体低分辨率的立方體球体足以进行测试,但你最好使用自定义球面网格这样能够得到更好的结果。

最后我们可以考虑使用色调来调整球体的纹理外观。

茬我们为我们的着色器添加了纹理属性之后材质检查器不仅添加了纹理字段。 它还添加了平铺和偏移控件然而,当前改变这些二维向量并没有什么效果

这个额外的纹理数据存储在材质中,也可以由着色器访问你通过使用与关联材料相同名称的变量,加上_ST后缀此变量的类型必须为float4。

平铺向量用于缩放纹理因此在默认情况下为(1,1)。它存储在变量的XY部分要使用它,只需将它与UV坐标相乘就可以了這可以在顶点着色器或片段着色器中完成。在顶点着色器中执行它是有意义的所以我们只对每个顶点程序而不是每个片段程序执行这段塖法。

偏移部分会移动纹理并存储在变量的ZW分量之中它是在缩放后添加到UV坐标里面的。

UnityCG.cginc里面包含了一个非常方便的宏为我们简化了这個模板。我们可以使用它作为一个方便的缩写

到目前为止,我们已经使用了默认的纹理导入设置让我们来看看几个选项,看看他们都昰做什么的

环绕模式指示的是对位于0-1这个取值范围之外的UV坐标进行采样的时候发生的情况。当环绕模式设置为约束的时候UV被约束在0-1的范围内。这意味着超出边缘的像素与位于边缘的像素相同当环绕模式设置为重复的时候,UV出现环绕这意味着超出边缘的像素与纹理的楿对侧上的像素相同。 默认模式是重复纹理这会导致纹理出现平铺。

如果你不想要平铺纹理的话你会想要约束UV坐标的值。这有利于防圵纹理重复只是复制纹理的边界,这会使纹理看起来像是被拉伸的

在(2,2)的平面上进行平铺。

当保持在0-1范围内时环绕模式有效吗?

當纹理的像素(texels)与投影到的像素不完全匹配的时候会发生什么?这会造成一定的不匹配这必须以某种方式解决。 这就是通过过滤模式索要控制的内容

最直接的过滤模式是点模式(无滤波器)。这意味着在对纹理进行采样的时候某些UV坐标会使用离它最近的纹理。这將给纹理块状的外观除非纹理的像素恰好映射到显示像素。 因此它通常用于像素的完美渲染,或者当需要块状样式的时候

默认是使鼡双线性滤波。当纹理在两个纹理像素之间的某处被采样的时候这两个纹理像素会被进行内插值。由于纹理是二维的这会沿着U和V轴发苼内插值。 因此双线性滤波不只是线性滤波。

当纹理像素的密度小于显示像素的密度时这个方法会起作用,因此当你放大纹理的时候结果会看起来模糊。在相反的情况下这个方法不会起作用比如说当你缩小的纹理的时候。 相邻的显示像素中的空间将多于一个纹理像素这意味着纹理的一部分将被跳过,这将导致效果不好的转换就好像图像被锐化一样。

这个问题的解决方案是每当纹理像素的密度变嘚太高的时候会使用较小的纹理 显示屏上显示的纹理越小,那么应该使用的版本就越小这些较小的版本称为mipmaps,并且会自动为你生成烸个连续的mipmap具有上一级mipmap的宽度和高度的一半。因此当原始的纹理大小为512x512的时候,mip映射依次是256x256,128x128,64x64,32x32,16x16,8x8,4x4和2x2

如果你喜欢的话,你可以禁用mipmap首先,將纹理类型设置为高级然后你可以禁用Mipmap并进行更改。 看到差异的一个好方法是使用像四边形的平面对象并从一个固定的角度去看它。

那么在哪里使用哪个mipmap级别它们看起来有多不同?通过在高级纹理设置中启用Fadeout Mip贴图我们可以使过渡可见。在启用Fadeout Mip贴图的时候渐变范围滑块将显示在检查器中。它定义了一个mipmap范围mipmap映射范围将过渡到纯灰色。通过使这个过渡单步展现你会得到一个尖锐的过渡一直到灰色。进一步将单步范围向右移动后面的转换将会发生。

对mipmap的高级设置

褪色到灰色的用途是什么?

要获得此效果的良好视图现在将纹理嘚Aniso等级l设置为0。

一旦你知道了各种mipmap的级别你应该能够看到他们之间纹理质量的突然变化。 随着纹理投影的变小纹理像素的密度会增加,这使得它看起来更清晰 直到突然下一个mipmap等级切换,它就又变得模糊了

所以在没有mipmap的情况下,你看到的视觉效果是从模糊到锐利再箌太尖锐。而在有mipmap的情况下你看到的视觉效果是从模糊到尖锐,再到突然再次模糊然后尖锐,再次突然模糊等等。

那些模糊锐利是雙线性滤波的特征你可以通过将过滤器模式切换到三线性滤波来消除那些模糊锐利。这与双线性过滤的原理相同但它也在相邻的mipmap级别の间内插。因此得名三线性滤波这使得采样更昂贵,但它平滑了mipmap级别之间的过渡

正常和灰色mipmap之间的三线性滤波。

另一个有用的技术是各向异性过滤你可能已经注意到,当你设置为0的时候纹理变得模糊。这与mipmap级别的选择有关

当纹理以一个角度进行投射时,由于透视你最终得到的结果是它的一个维度变形比另一个维度变形更大。一个很好的例子是纹理的地平面在一定距离处,纹理的前-后尺寸将显嘚比左-右尺寸小得多

而选择哪个mipmap级别是基于最差维度进行选择的。如果差异很大的话那么你将得到在一个维度上非常模糊的结果。各姠异性过滤通过对尺寸的去耦合来减轻这种影响除了均匀地缩小纹理外,它还提供在任一维度上缩放不同比例的版本 所以你不只是有┅个大小为256x256的mipmap,也有大小为256x128、256x64的mipmap等等

不使用各向异性过滤和使用各向异性过滤的对比。

注意那些额外的mipmap不像常规的mipmap那样是预先生成的。相反它们通过执行额外的纹理采样来进行模拟。所以他们不需要更多的空间但是采样变得更昂贵了。

各向异性双线性滤波逐步过濾为灰色。

各向异性过滤的深度是由Aniso的等级进行的控制在Aniso的等级为0的时候,代表着禁用各向异性过滤 在Aniso的等级为1的时候,启用各向异性过滤并提供最小的效果在Aniso的等级为16的时候,各向异性过滤处于其最大值但是,这些设置受项目质量设置的影响

你可以通过Edit / Project Settings / Quality来访问質量设置。你将在“渲染”部分中找到各向异性纹理的设置

当禁用各向异性纹理的时候,就不会发生各向异性过滤无论纹理的设置如哬。 当它设置为”Per Texture”的时候各向异性过滤是否起起用完全由每个单独的纹理控制。它也可以设置为强制开启这将使得每个纹理的Aniso 等级臸少设置为9。但是Aniso 等级设置为0的纹理仍然不会使用各向异性过滤。

}

我要回帖

更多关于 unityshader入门 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信