Shader学习笔记(五)

UnityShader 中级篇(二)

高级纹理

在图形学中,立方体纹理(Cubemap)环境映射(Environment Mapping) 的一种实现方法。
在这里插入图片描述

天空盒子

天空盒子(Skybox) 是一个立方体,每个面使用的技术就是立方体纹理映射技术

  1. 新建天空盒材质,设置为Skybox/6 Sided,注意这6张纹理的Wrap Mode设置为Clamp,以防止在接缝处出现不匹配的现象。
    在这里插入图片描述

创建用于环境映射的立方体纹理

第一种方法:直接由一些特殊布局的纹理创建,需把该纹理的TextureType设置为Cubemap即可。
在基于物理的渲染中,通常用一张HDR图像来生成高质量的Cubemap。可参考相关的官方文档Cubemap
第二种方法:创建Cubemap,把6张纹理拖拽到面板中,官方推荐使用第一种方法创建立方体纹理,这是因为第一种方法可对纹理数据进行压缩,而可支持边缘修正、光滑反射(glossy reflection)和HDR等功能。
依据官方文档创建立方体纹理

  1. 在 Editor 文件夹上才能正确执行。
    在这里插入图片描述
    其中,Face size 值越大,渲染出来的立方体纹理分辨率越大,效果可能更好,但需要占用的内存也越大,可有面板下方显示的内存大小得到。
    在这里插入图片描述

反射

使用反射效果的物体通常看起来像镀了层金属。只需通过入射方向和表面法线方向来计算反射方向,再利用反射方向对立方体纹理采样即可。

  1. 声明3个新的属性:
    在这里插入图片描述
    其中,_ReflectColor 用于控制反射颜色,_ReflectAmount 用于控制这个材质的反射程度,而**_Cubemap** 就是用来模拟反射的环境映射纹理。
  2. 在顶点着色器中计算了该点的反射方向,用CG的reflect函数来实现的:
    在这里插入图片描述
    物体反射到摄像机中的光线方向,可由光路可逆的原则来反向求得。可计算视角方向关于顶点法线的反射方向来求得入射光线的方向。
  3. 在片元着色器中,利用反射方向来对立方体纹理采样:
    在这里插入图片描述
    对立方体纹理的采样需使用CG的texCUBE函数。注意到,在采样时并没有对i.worldRef1进行归一化操作。这是因为,用于采样的参数仅仅作为方向变量传递给texCUBE函数的,因此没有必要进行一次归一化的操作。然后,使用_ReflectAmount来混合漫反射颜色和反射颜色,并和环境光照相加后返回。
    出于性能方面的考虑,选择在顶点着色器中计算反射方向。

最终效果

在这里插入图片描述

折射

可使用斯涅尔定律(Snell’s Law)来计算反射角。当光从介质1沿着和表面法线夹角为θ1的方向斜射入介质2时,可使用如下公式计算折射光线与法线的夹角θ2
在这里插入图片描述
其中,n1和n2分别是两个介质的折射率(index of refraction)。折射率是一项重要的物理常数,例如真空的折射率是1,而玻璃的折射率一般是1.5,如下图
在这里插入图片描述

  1. 声明4个新属性:
    在这里插入图片描述
    其中,_RefractColor_RefractAmount和**_Cubemap**与上节中控制反射时使用的属性类似,还使用了一个属性_RefractRatio,使用该属性得到不同介质的透射比,以此来计算折射方向。
  2. 在顶点着色器中,计算折射方向:
    在这里插入图片描述

使用了CG的 refract 函数来计算折射方向,第一个参数即为入射光线的方向,它必须是归一化后的矢量;第二个参数是表面法线,法线方向同样需要是归一化后的;第三个参数是入射光线所在介质的折射率和折射光线所在介质的折射率之间的比值。例如光从空气射到玻璃表面,那么这个参数应该是空气的折射率和玻璃的折射率之间的比值,即1/1.5。它的返回值就是计算而得的折射方向,它的模则等于入射光线的模。
3. 在片元着色器中使用折射方向对立方体纹理进行采样:
在这里插入图片描述

最终效果

在这里插入图片描述

菲涅耳反射

在实时渲染中,常使用菲涅耳反射(Fresnel reflection) 来根据视角方向控制反射程度。菲涅耳反射描述了一种光学现象,即当光线照射到物体表面上时,一部分发生反射,一部分进入物体内部,发生折射或散射。被反射的光和入射光之间存在一定的比率关系,这个比率关系可以通过菲涅耳等式进行计算。事实上,不仅仅是水、玻璃这样的反光物体具有菲涅耳效果,几乎任何物体都或多或少包含了菲涅耳效果,这是基于物体的渲染中非常重要的一项高光反射计算因子。
需要使用菲涅耳等式。真实世界的菲涅耳等式是非常复杂的,但在实时渲染中,通常会使用一些近似公式就是Schlick菲涅耳近似等式:
在这里插入图片描述
其中,Fo是一个反射系数,用于控制菲涅耳反射的强度,v是视角方向,n是表面法线。另一个应用比较广泛的等式是Empricial菲涅耳近似等式:
在这里插入图片描述
在许多车漆、水面等材质的渲染中,会经常使用菲涅耳反射来模拟更加真实的反射效果。

  1. 声明了用于调整菲涅耳反射的属性以及反射使用的Cubemap:
    在这里插入图片描述
  2. 在顶点着色器中计算世界空间下的法线方向、视角方向和反射方向:
    在这里插入图片描述
  3. 在片元着色器中计算菲涅耳反射,并使用结果值混合漫反射光照和反射光照:
    在这里插入图片描述
    使用Schlick菲涅耳近似等式来计算fresnel变量,并使用它来混合漫反射光照和反射光照。一些实现也会直接把fresnel和反射光照相乘后叠加到漫反射光照上,模拟边缘光照的效果。

最终效果

在这里插入图片描述

渲染纹理

现代的GPU允许把整个三维场景渲染到一个中间缓冲中,即渲染目标纹理(Render Target Texture,RTT),而不是传统的帧缓冲或后备缓冲(back buffer)。与之相关的是多重渲染目标(Multiple Render Target,MRT),这种技术指的是GPU允许把场景同时渲染到多个渲染目标纹理中,而不再需要为每个渲染目标纹理单独渲染完整的场景。延迟渲染就是使用多重渲染目标的一个应用。
Unity为渲染目标纹理定义了一种专门的纹理类型——渲染纹理(Render Texture)。在Unity中使用渲染纹理通常有两种方式:一种方式是在Project目录下创建一个渲染纹理,然后把某个摄像机的渲染目标设置成该渲染纹理,这样一来该摄像机的渲染结果会实时更新到渲染纹理中,而不会显示在屏幕上,使用该方法,可选择渲染纹理的分辨率、滤波模式等纹理属性。另一种方式是在屏幕后处理时使用 GrabPass 命令或 OnRenderImage 函数来获取当前屏幕图像,Unity会把这个屏幕图像放到一张和屏幕分辨率等同的渲染纹理中,下面可在自定义Pass中把它们当成普通的纹理来处理,从而实现各种屏幕特效。

镜子效果

首先需创建Render Texture 拖拽到该摄像机的Target Texture上,如图
在这里插入图片描述
在这里插入图片描述
镜子原理很简单,使用一个渲染纹理作为输入属性,把该渲染纹理在水平方向上翻转后直接显示到物体上即可。

  1. 对应了由镜子摄像机渲染得到的渲染纹理:
    在这里插入图片描述
  2. 在顶点着色器中计算纹理坐标:
    在这里插入图片描述
    翻转x分量的纹理坐标是因为镜子里显示的图像都是左右相反的
  3. 在片元着色器里对渲染纹理进行采样和输出:
    在这里插入图片描述

最终效果

在这里插入图片描述

玻璃效果

在Unity Shader中使用一种特殊的Pass来完成获取屏幕图像的目的,这就是GrassPass。当在Shader中定义了一个GrabPass后,Unity会把当前屏幕的图像绘制在一张纹理中,以便在后续的Pass中访问它。通常会使用GrassPass来实现诸如玻璃等透明材质的模拟,与使用简单的透明混合不同,使用GrabPass可让对该物体后面的图像进行更复杂的处理,例如使用法线来模拟折射效果,而不再是简单的和原屏幕颜色进行混合。
本例会通过Cubemap来模拟玻璃的反射,而在模拟折射时,则使用了GrabPass获取玻璃后面的屏幕图像,并使用切线空间下的法线对屏幕纹理坐标偏移后,再对屏幕图像进行采样来模拟近似的折射效果。

  1. 声明该Shader使用的各个属性:
    在这里插入图片描述
  2. 定义相关的渲染队列,并使用GrabPass来获取屏幕图像:
    在这里插入图片描述
    Queue设置成Transparent可确保该物体渲染时,其他所有不透明物体都已被渲染到屏幕上,否则就可能无法正确得到”透过玻璃看到的图像“。而设置RenderType 则是为了在使用着色器替换(Shader Replacement)时,该物体可在需要时被正确渲染。随后,通过关键词GrabPass定义了一个抓取屏幕图像的**Pass.**在该Pass中定义了一个字符串,该字符串内部的名称决定了抓取得到的屏幕图像将会被存入哪个纹理中
  3. 定义渲染玻璃所需的Pass。为了在Shader中访问各个属性,首先需要定义它们对应的变量:
    在这里插入图片描述
  4. 定义顶点着色器:
    在这里插入图片描述
    通过调用内置的ComputeGrabScreenPos函数来得到对应被抓取的屏幕图像的采样坐标。可在UnityCG.cginc文件中找到它的声明,它的主要代码和ComputeScreenPos基本类似,最大的不同是针对平台差异造成的采样坐标问题进行了处理。接着,对Cubemap进行采样,之后方法参照前面几节的内容不再赘述
  5. 定义片元着色器:
    在这里插入图片描述
    其中,得到切线空间下的法线方向,使用该值和**_Distortion** 属性以及_RefractionTex_TexekSize 来对屏幕图像的采样坐标进行偏移,模拟折射效果。随后,对scrPos 透视除法得到真正的屏幕坐标,再使用该坐标对抓取的屏幕图像_RefractionTex进行采样,得到模拟的折射颜色。
    之后,得到视角方向相对于法线方向的反射方向。随后,使用反射方向对Cubemap进行采样,并把结果和主纹理颜色相乘后得到反射颜色。
    最后,使用_RefractAmount属性对反射和折射颜色进行混合,作为最终的输出颜色。

最终效果

在这里插入图片描述

渲染纹理 vs. GrabPass

从效率上来讲,使用渲染纹理的效率往往好于GrabPass。经过需要把部分场景再次渲染一遍,但可通过调整摄像机的渲染层来减少二次渲染时的场景大小,或使用其他方法来控制摄像机是否开启。而使用GrabPass获取到的图像分辨率和显示屏幕是一致的,这意味着在一些高分辨率的设备上可能会造成严重的带宽影响,GrabPass虽然不会重新渲染场景,但它往往需要GPU直接读取后备缓冲(back buffer)中的数据,破坏了CPU和GPU之间的并行性,这是比较耗时的,甚至在一些移动设备上是不支持的。
Unity引入了**命令缓冲(Command Buffers)**来允许扩展Unity的渲染流水线。使用命令缓冲可得到类似抓屏的效果,它可在不透明物体渲染后把当前的图像复制到一个临时的渲染目标纹理中,然后在那里进行一些额外的操作,例如模糊等,最后把图像传递给需要使用它的物体进行处理和显示,除此之外,命令缓冲还允许实现很多特殊的效果,具体可参考图像命令缓冲

程序纹理

使用单张纹理SingleTexture的shader创建材质并赋给Cube物体

  1. 声明该程序纹理使用的各种参数
    在这里插入图片描述
  2. 在Start 赋值,_UpdateMaterial()
    在这里插入图片描述在这里插入图片描述
  3. _GeneralProceduralTexture函数的代码如下:
    在这里插入图片描述

最终效果

在这里插入图片描述

Unity的程序材质

Unity 中,有一类专门使用程序纹理的材质,叫做程序材质(Procedural Materials)。使用了一个名为 Substance Designer 的软件在Unity 外部生成的。
Substance Designer是一个非常出色的纹理生成工具,可从Unity的资源商店或网格中获取到很多Substance材质,参考资源来源

猜你喜欢

转载自blog.csdn.net/weixin_42050609/article/details/124897172