Unity Shader入门精要 第十章

  1. 1. 高级纹理
  2. 2. 10.1 立方体纹理
    1. 2.0.1. 10.1.0
    2. 2.0.2. 10.1.1 天空盒子
    3. 2.0.3. 10.1.2 创建用于环境映射的立方体纹理
    4. 2.0.4. 10.1.3 反射
    5. 2.0.5. 10.1.4 折射
    6. 2.0.6. 10.1.5 菲涅尔反射
  • 3. 10.2 渲染纹理
    1. 3.0.1. 10.2.0
    2. 3.0.2. 10.2.1 镜子效果
    3. 3.0.3. 10.2.2 玻璃效果
  • 4. 10.3 程序纹理
    1. 4.0.1. 10.3.1 在Unity中实现简单的程序纹理
    2. 4.0.2. 10.3.2 Unity中的程序材质

  • 高级纹理


    10.1 立方体纹理

    10.1.0

    • 立方体纹理是环境映射的一种实现方法

      • 环境映射可以模拟物体周围的环境
      • 使用了环境映射的物体,看起来像镀了一层金属一样,可以反射出周围的环境
    • 立方体纹理一共包含了6张图像,对应了一个立方体的6个面,立方体纹理的名称也由此而来

    • 立方体的每个面,表示沿着世界空间下的轴向(也就是上下左右前后)观察所得的图像

    • 对这种纹理进行采样,需要提供一个三维的纹理坐标,这个坐标表示了在世界空间下的一个3D方向,这个方向矢量是从立方体的中心出发,当它向外部延伸时会和立方体纹理之一发生相交,而采样所得的结果就是由该交点计算而来的

    • 使用立方体纹理的好处在于,它的实现简单快速,得到的效果也比较好

    • 但它也有一些缺点:

      • 例如当场景中引入了新的物体、光源,或者物体发生移动时,我们就需要重新生成立方体纹理
      • 立方体纹理也仅可以反射环境,不能反射使用了该立方体纹理的物体本身。这是因为立方体纹理不能模拟多次反射的结果,因此应该尽量对凸面体而不是凹面体使用立方体纹理,凹面体会反射自身

    10.1.1 天空盒子

    • 天空盒子这个名字包含了两个信息:它是用来模拟天空的,它是一个盒子
    • 当场景中使用了天空盒子时,整个场景就被包围在一个立方体内
    • 如何创建一个Skybox材质
      • 新建一个材质,假设命名为SkyboxMat
      • 在SkyboxMat的UnityShader下拉菜单中选择Unity自带的Skybox/6 Sided,该材质需要6张纹理
      • 对SkyboxMat的6张纹理进行赋值,需要把6张纹理的Wrap Mode设置为Clamp,以防止在接缝处出现不匹配的现象
      • 在Window –> Lighting菜单中,把SkyboxMat赋给Skybox选项

    • 保证渲染场景的摄像机的Clear Flags要设置为Skybox
    • 如果想让某些摄像机不渲染该天空盒,可以设置其他的Clear Flags
    • 如果想让某些摄像机使用不同的天空盒,可以Add Component下选择Skybox组件

    • 在Unity中,天空盒是在所有不透明物体之后渲染的
    • 背后使用的网格是一个立方体或一个细分后的球体

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

    • 除了天空盒外,立方体纹理的最强用处就是用于环境映射,通过这种方法模拟出金属的材质
    • 在Unity5中,创建用于环境映射的立方体纹理的方法有三种
      • 第一种是直接由一些特殊布局的纹理创建
      • 第二种是手动创建一个CubeMap资源,再把6张图赋给它
      • 第三种是由脚本生成
    • 第一种方法需要一张特殊布局的纹理,例如立方体展开图的交叉布局、全景布局等等,然后只需把该纹理的Texture Type设置为CubeMap即可
    • 在基于物理的渲染中,通常会使用一张HDR图像来生成高质量的CubeMap,之后再介绍
    • 第二种方法是Unity5之前的版本使用的方法,首先在项目资源中创建一个CubeMap,然后把6张纹理图拖到它的面板中
    • 在Unitry5中,官方推荐使用第一种方法创建立方体纹理,这是因为第一种方法可以对纹理数据进行压缩,而且可以支持边缘修正、光滑反射和DFR的功能
    • 前面两种方法都需要提前准备好立方体纹理的图像,它们得到的立方体纹理往往是被场景中的物体所共用的,但在理想情况下,我们希望根据物体在场景中位置的不同,生成它们各自不同的立方体纹理,这是我们就可以在Unity中使用脚本来创建
    • 这是利用Unity提供的Camera.RenderToCubemap函数实现的,这个函数可以把从任意位置观察到的场景图像存储到6张图像中,从而创建出该位置上对应的立方体纹理
    • 示例用法(脚本放Editor目录):
    • 它是利用Camera.RenderToCubemap来创建立方体纹理的代码,在renderFromPosition处动态创建了一个摄像机,名字就叫CubemapCemera,调用RenderToCubemap把从当前位置观察到的图像渲染到用户指定的立方体纹理Cubemap中,完成后销毁临时摄像机
    • 在场景中新建一个GameObject用来表示位置,再新建一个用于存储的立方体纹理Create–>Legacy–>Cubemap,勾选Readable可读的,再使用菜单选项创建即可,Face size越大,渲染出的立方体纹理分辨率就越大,效果越好,但内存也越大
    • 准备好了立方体纹理后,就可以对该物体使用环境映射技术
    • 而环境映射最常用常见的应用就是反射和折射

    10.1.3 反射

    • 使用了反射效果的物体通常看起来就像是镀了层金属
    • 只需要通过入射光线的方向和表面方向来计算反射方向,再利用反射方向对立方体纹理进行采样就可以了
    • 反射shader:
    • _ReflectColor用于控制反射的颜色
    • _ReflectAmount用于控制这个材质的反射程度
    • _Cubemap就是用于模拟反射的环境纹理映射

    10.1.4 折射

    • 折射遵循公式:
    • η为各介质的折射率
    • 在实时渲染中通常只模拟第一次折射
    • 折射Shader:
    • _RefractRatio就是需要使用该属性得到不同介质的一个透射比,以此来计算折射方向

    10.1.5 菲涅尔反射

    • 在实时渲染中,经常使用菲涅尔反射来根据视角方向控制反射程度
    • 通俗来讲,菲涅尔反射描述了一种光学现象,即当光线照射到物体表面时,一部分发生了反射,一部分则进入物体内部发生折射或散射,被反射的光和入射光之间会存在一定的比例关系,这个比例关系可以用菲涅尔等式来进行计算
    • 一个简单的例子,当我们站在湖边时,看近处的湖面是透明的,可以直接看到水底的石子,但当我们看远处的水面时,会发现几乎看不到水下的情景,而只能看到水面反射的环境。这就是所谓的菲涅尔效果
    • 真实世界的菲涅尔等式是非常复杂的,但在实时渲染中,我们可以把它简化一下
    • F0是反射系数,用于控制菲涅尔反射的强度
    • 可以更广泛一下:
      F = max(0,min(1,bias + scale X (1-v·n)^power))
    • bias就是F0
    • 使用上面的菲涅尔近似等式可以在边界处模拟反射光强和折射光强以及漫反射光强之间的变化,在许多车漆、水面等材质的渲染中,我们会经常使用菲涅尔反射来模拟更加真实的反射效果
    • 菲涅尔Shader:


    10.2 渲染纹理

    10.2.0

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

    10.2.1 镜子效果

    • 使用Render Texture实现

    10.2.2 玻璃效果

    • 玻璃Shader:
    • _MainTex是该玻璃的材质
    • _BumpMap是玻璃的法线纹理
    • _Cubemap是用于模拟反射的环境纹理
    • _Distortion是用于控制模拟折射时图像的扭曲程度
    • _RefractAmount用于控制折射的程度,为0时该玻璃只包含反射效果,为1时只包含折射效果
    • 当Queue设置为Transparent可以确保该物体渲染时,会在其他所有不透明的物体都已经被渲染到屏幕上了,否则将无法正确看到透过玻璃看到图像的效果
    • 设置RenderType是为了在使用着色器替换时,该物体可以在需要时能被正确的渲染,这通常会发生在我们需要得到摄像机的深度和法线纹理时
    • 随后在GrabPass定义了一个抓取屏幕图像的一个Pass,该Pass中定义了一个字符串,该字符串内的名称决定了抓取得到的屏幕图像将会被存储在哪个纹理中
    • 我们可以省略该字符串,但直接声明纹理名称的方法往往可以得到更高的性能

    10.3 程序纹理

    10.3.1 在Unity中实现简单的程序纹理

    • 程序纹理指的是那些由计算机生成的图像,我们通常使用一些特定的算法来创建个性化的图案或者非常真实的自然元素,例如木头或石子等等
    • 使用程序纹理的好处就在于我们可以使用各种参数来控制纹理的外观,这些属性不仅仅是颜色属性,甚至可以是完全不同类型的图案属性,这使得我们可以得到更加丰富的动画和视觉效果
    • 程序纹理脚本:
    • 加上[ExecuteInEditMode]使脚本能在编辑器模式下运行
    • 声明使用的各种属性,这里包括材质宽度、背景颜色、园的颜色、模糊因子等等
    • 纹理大小通常是2的整数幂,模糊因子用于模糊圆形边界
    • 在Start中获取材质,更新材质,需要有一个_MainTex的变量
    • 生成程序纹理函数
    • 首先初始化一张二维纹理
    • 计算生成纹理时所需要的一些变量(圆的间隔、半径、模糊因子)
    • 利用双重循环遍历纹理上每个像素,并在纹理上绘制9个圆形
    • 最后调用Tex2D的Apply()强制把像素写入到纹理中并返回

    10.3.2 Unity中的程序材质

    • 在Unity中,有一类专门使用程序纹理的材质,叫做程序材质(Procedural Material)
    • 程序材质和之前的其他材质在本质上是一样的,不同的是它们使用的不是普通的纹理,而是程序纹理
    • 同时程序材质和它使用的程序纹理并不是在Unity中创建的,而是使用Substance Designer软件在Unity外部生成的
    • Unity5升级到高版本后,Substance Designer材质已经从Unity中移除,为了继续使用Substance Designer材质,需要从Asset Store中找出并安装Allegorithmic的资源包