创建各向异性类型的高光反射着色器

创建各向异性类型的高光反射着色器 **各向异性(Anisotropic)**类型可以用来模拟高光或者反射,常用于表面凹槽的方向和高光在垂直方向上的扭曲形变。当你想模拟金属抛光表面的时候这种类型的着色器就会非常有用,因为这种表面并不干净,光滑和明亮。 你可以想象当你看CD或者VCD光盘的数据那面时的高光或者底面被打磨过的金属盘子和盆子。 当你仔细检查这些表面的时候你会发现,表面的这些沟纹是有方向的,通常就是被抛光的方向。当你对这样的表面应用高光时,在垂直方向上会被扭曲。 这个知识点中我们会向你介绍不同的抛光表面高光概念。在将来的一些知识点中,我们会探索如何使用这个知识点介绍的一些概念来获得一些类似于头发的扭曲反射效果,但是在这里我们还是要先学习这个技术的一些原理知识。我们自定义的各向异性着色器将使用下面这个着色器作为参考: http://wiki.unity3d.com/index.php?title=Anisotropic_Highlight_Shader 下图展示了使用**各向异性(Anisotropic)**着色器能获得的不同类型高光效果: 始前准备 让我们开始学习新的知识点吧,先按照下面的步骤在场景中创建一个着色器,材质和一些光源: 创建一个新的场景,在场景中添加一些游戏对象,添加一个平行光源,这样我们好可视化的调试我们的着色器。 创建一个新的着色器和材质,它们都应用到刚刚创建的游戏对象上去。 最后,我们需要某种法线贴图,它能指出我们各向异性类型高光的方向。 下图展示的就是这个知识点要用的各向异性类型的法线贴图。在本书的支持网页中可以获得[就在本书附带的工程代码中,获取方法前面有介绍]: https://www.packtpub.com/books/content/support 操作步骤 为了获得各向异性效果,我们需要对我们前面创建的着色器进行如下的修改: 首先得再着色器中添加我们需要用到的一些属性。这些属性允许我们控制很多中艺术效果从而最终决定表面的效果呈现: { _MainTint ("Diffuse Tint", Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} _SpecularColor ("specular Color", Color) = (1,1,1,1) _Specular ("Specular Amount", Range(0,1)) = 0.5 _SpecPower ("Specular Power", Range(0,1)) = 0.5 _AnisoDir ("Anisotropic Direction", 2D) = "" {} _AnisoOffset ("Anisotropic Offset", Range(-1,1)) = -0.2 } 接着我们要让我们的**SubShader{}代码块跟我们的属性(Properties)块关联起来,这样才能让我们使用属性(Properties)**中的数据: sampler2D _MainTex; sampler2D _AnisoDir; float4 _MainTint; float4 _SpecularColor; float _AnisoOffset; float _Specular; float _SpecPower; 接下来我们要创建我们自己的光照函数,用来处理物体表面的各向异性效果。我们的代码如下: fixed4 LightingAnisotropic(SurfaceAnisoOutput s, fixed3 lightDir, half3 viewDir, fixed atten) { fixed3 halfVector = normalize(normalize(lightDir) + normalize(viewDir)); float NdotL = saturate(dot(s....

February 26, 2021 · 2 min · 285 words · Link

创建 BlinnPhong 类型的高光反射着色器

创建 BlinnPhong 类型的高光反射着色器 Blinn是另一种计算和模拟高光的更有效的方法。它只要视角方向和光线方向向量的中间向量就可以计算出来。这个高光是Jim Blinn带入到Cg世界中的。 他发现比起计算反射向量来,只要中间向量的效率更好。它减少了代码量和处理时间。 如果你在UnityCG.cginc文件中查看了Unity内建的BlinnPhong光照模型的话,它也是用了中间向量,因此它被命名为BlinnPhong 。它只是完整Phong光照计算中的一种简单的版本。 始前准备 让我们按照下面的步骤开始学习这个知识点: 这次我们不创建新的场景,就用原来场景和场景内的对象就好,然后我们需要创建一个新的着色器和材质,并且把名字都叫BlinnPhong。 当我们创建好着色器后,双击它,开始编辑。 操作步骤 按照下面的步骤走,我们来创建BlinnPhong光照模型: 首先,我们需要在着色器的**属性(Properties)**块中添加我们需要用到的属性,这样好让我们控制高光效果: Properties { _MainTint ("Diffuse Tint", Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} _SpecularColor ("Specular Color", Color) = (1,1,1,1) _SpecPower ("Specular Power", Range(0.1,60)) = 3 } 接下来在CGPROGRAM块中添加与属性对应的变量,这样我们就可以获得来自**属性(Properties)**中的数据: sampler2D _MainTex; float4 _MainTint; float4 _SpecularColor; float _SpecPower; 接下来就是要创建我们自定义的光照模型了,用来处理漫反射和高光的计算,代码如下所示: fixed4 LightingCustomBlinnPhong (SurfaceOutput s, fixed3 lightDir,half3 viewDir, fixed atten) { float NdotL = max(0,dot(s.Normal, lightDir)); float3 halfVector = normalize(lightDir + viewDir); float NdotH = max(0, dot(s....

February 25, 2021 · 1 min · 204 words · Link

创建一个Phong镜面类型的高光反射着色器

创建一个Phong类型类型的高光反射着色器 一个物体表面的高光,可以简单的理解为它表面的亮度。这种类型的着色器常用于视野特效(view-dependent effects)。这是因为 为了在着色器中获得贴近现实的高光效果,你需要考虑到摄像机和人的朝向因素。而Phong高光效果是最基础和性能较好的一种着色器效果。它根据人的朝向和光的反射方向,通过计算获得一个有方向的反射。 在应用程序中,这种高光模型非常常见,涵盖游戏行业到电影等产业。虽然它在高光反射模型的精确度上不是最接近现实的,但是在大多数情况下,它大致都能满足而且性能不赖。此外,如果你的游戏对象离摄像机很远,就没有必要在提供准确的高光,这对你的高光效果着色器来说是好事。 在这个知识点中,我们会涉及到如何使用表面着色器的**输入(Input)**结构体中的一些新参数进行逐顶点和逐像素的操作。我们会了解它们之间的区别,而且还会讨论什么时候以及为什么要用这两种不同的实现方式,来应对不同的情况。 始前准备 我们按照下面的步骤来学习这次的知识点: 分别创建一个新的着色器,材质和游戏对象,为了在后面你容易照到它们,请给它们恰当命名。 创建一个新场景,在创建一个新的游戏对象,把着色器应用到材质,然后再把材质应用到游戏对象上。 再添加一个平行光源,这是为了让我们方便看我们着色器代码的高光效果。 操作步骤 请按照下面的步骤创建一个Phong类型的光照模型: 此时你可能发现了一个模式,在我们开始写着色器的时候都有的步骤:着色器属性的创建。所以,让我们先在着色器中添加下面的一些属性: Properties { _MainTint ("Diffuse Tint", Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} _SpecularColor ("Specular Color", Color) = (1,1,1,1) _SpecPower ("Specular Power", Range(0,30)) = 1 } 接下来在CGPROGRAM块中的**SubShader{}**块中,添加与之对应的一些变量: float4 _SpecularColor; sampler2D _MainTex; float4 _MainTint; float _SpecPower; 现在我们要添加我们自定义的光照模型,因为我们要计算自己的Phong高光。如果你现在还不能理解下面的代码也不用担心;在原理介绍的部分我们会解释每一行代码的作用。在着色器的**SubShader{}**中添加下述代码: fixed4 LightingPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir,fixed atten) { // Reflection float NdotL = dot(s.Normal, lightDir); float3 reflectionVector = normalize(2....

February 25, 2021 · 1 min · 181 words · Link

创建一个Toon风格的着色器

创建一个Toon风格的着色器 **Toon着色器(toon shading)**是游戏中最常使用的效果之一,也被称作(AKA)cel shading(cel是celluloid的缩写[中文也叫 赛璐珞])。它是一种非真实渲染技术,可以让3D模型呈现一种平面效果。许多游戏中用这种着色器把3D的物体渲染成一种手绘物体的效果。下图中你能看到这两者的区别,右边的是标准着色器,左边的是Toon着色器: 始前准备 开始学习这个知识点之前,我们先创建一个着色器和对应的材质球,而且需要导入一个特殊的纹理,步骤如下: 创建一个新的着色器;在这例子中,我们会用上一个知识点的着色器进行扩展 为着色器创建一个新的材质,并且把它应用到3D模型中。 拥有曲面的模型对于toon着色器来说最好。 这个知识点需要一张额外的纹理,叫做ramp贴图[如果要全译,我把它叫梯度贴图]。有一点很重要,在导入的时候把Wrap Mode改为Clamp,如果你想让颜色的边缘变得灵敏,就把Filter Mode设置为Point : 操作步骤 通过下面的步骤我们可以获得toon风格的特殊审美呈现: 添加一个新的叫**_RampTex**纹理属性: _RampTex ("Ramp", 2D) = "white" {} 同时在CGPROGRAM块中添加对应的变量: sampler2D _RampTex; 修改**#pragma指示 ,从而让着色器使用一个叫LightingToon()**的函数: #pragma surface surf Toon 使用下面这个光照模型: fixed4 LightingToon(SurfaceOutput s ,fixed3 lightDir,fixed atten) { half NdotL = dot(s.Normal,lightDir); NdotL = tex2D(_RampTex,fixed2(NdotL,0.5)); fixed4 c; c.rgb = s.Albedo * _LightColor0.rgb*NdotL*atten; c.a = s.Alpha; return c; } 原理介绍 toon风格着色器的主要特征是它的光的渲染方式;表面并非均匀的着色。为了能达这种效果,我们需要一张ramp贴图。他的作用是对Lambertian的光线强度NdotL重新映射,然后把值赋值给另一个值。我们使用一张ramp贴图而不是一个梯度值,是为了强制光线按照步骤渲染。下图展示了ramp贴图是如何纠正光的强度的: 额外内容 我们有很多不同方式来获得toon着色器效果。 我们可以使用不同的ramp贴图来让我们的模型看起来更有吸引力,这就需要你们自己去试试了,然后找到一张你认为最好的。 还有另一种可选方法对纹理进行梯度采样,就是通过对光强度NdotL进行截断取值,这样就只能在0到1的范围内给它赋值特定的值: fixed4 LightingToon(SurfaceOutput s ,fixed3 lightDir,fixed atten) { half NdotL = dot(s....

February 23, 2021 · 1 min · 100 words · Link

理解光照模型

第三章 理解光照模型 在前面的那些章节中,我们介绍了表面着色器并且还理解了如何修改一些物理属性(比如Albedo和Specular)来模拟不同的材质。这些到底是如何工作的呢?每个表面着色器中最重要的部分一一光照模型lighting model。它的功能就是接受这些参数然后计算每一个像素点的最终着色。Unity通常会对开发者隐藏这部分,因为如果想要编写一个光照模型的话,你就必须要去理解光在物体表面是如何反射和折射的。这个章节中我们会毫无保留的向你展示光照模型是如何工作的,并且给你介绍一些你自己创建光照模型所需要的一些基础知识。 这一章中,我们会学习下面所列的知识点: 创建一个自定义的漫反射光照模型 创建一个Toon风格的着色器 创建一个Phong类型类型的高光反射着色器 创建 BlinnPhong 类型的高光反射着色器 创建各向异性类型的高光反射着色器 介绍 想要模拟光的工作方式是一项非常具有挑战性的工作,同时也非常消耗计算资源。在之前的很早一段时间内,游戏中使用的都是一些非常简单的光照模型,效果看起来差到难以置信。尽管现在的3D游戏引擎已经使用了基于物理原理的渲染器,但是有些更简单的光照模型技术还是值得我们去探索的。但有时,我们不得不面对资源紧张的现实,没有办法在这些资源有限的设备上完整实现光照模型,比如我们的移动设备。所以你想在这上面实现自己的光照模型,那么你就很有必要了解这些简单的光照模型。 创建一个自定义的漫反射光照模型 如果你对Unity4很了解的话,你应该知道Unity提供的默认的着色器是基于一个叫Lambertian reflectance的光照模型。我们会在这个知识点向你展示如何创建一个自定义的光照模型,并且解释它后面的数学原理和实现方式。下面的两张图分别展示了标准着色器Standard Shader (右)和diffuse Lambert着色器对同一个几何体进行渲染后,不同的显示效果: 基于Lambertian reflectance光照模型的着色器是典型的非真实渲染着色器;我们现实生活中没有物体会看起来像那样。然而Lambert 着色器依然能在一些低多边形风格的游戏中经常看到,因为跟一些复杂的几何体比起来,它们的三角面数量对比非常明显。用于计算Lambertian反射的光照模型非常高效,这特别适合移动端的游戏。 Unity其实已经提供了光照函数给我们,好让我们能在着色器中使用。它就是Lambertian光照模型。它是光反射模拟的一种更基础更有效率的形式,你能在当今的很多游戏中看到它的存在。 因为它们已经内建在了Unity的表面着色器语言中,所以我从这个开始和基于它开始构建也不失为一个好的选择。你也可以在Unity用户手册中找到一个例子,但我们还是会更深入学习,从而向你解释这些数据是从哪里来的以及为什么它是那样工作的。这些可以为设置光照模型打下一个很好的基础,这些知识将来也能在后面的章节中对我们有帮助。 始前准备 让我们从实现下面几个步骤开始: 创建一个新的着色器并且给它命名好。 创建一个新的材质球,命名好,并且把上一步新建的着色器应用于该材质。 接下来,创建一个球形对象,并且把它大致放在场景中间的位置。 最后,我们创建一个方向光源,让光找到游戏对象上。 当你在Unity中设置好这些资源后, 你就会有一个类似于下图的场景: 操作步骤 Lambertian反射可以在着色器中修改下面的代码实现: 首先在着色器的属性Properties块中添加下面的属性: _MainTex("Texture", 2D) = "white" 修改着色器的**#pragma指示符,从而让着色器使用我们自定义的光照模型,而不是标准Standard**: #pragma surface surf SimpleLambert 使用一个非常简单的表面函数surface function,这个函数仅仅通过它的UV数据对纹理进行采样: void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb; } 添加一个叫做LightingSimpleLambert()的函数,这个函数包含了下面实现Lambertian反射的代码: half4 LightingSimpleLambert(SurfaceOutput s,half3 lightDir,half atten) { half NdotL = dot(s.Normal,lightDir); half4 c; c....

February 22, 2021 · 1 min · 125 words · Link

在地形的表面绘制一个圆

在地形的表面绘制一个圆 在很多的RTS类型的游戏中,通过围绕被选中的单位绘制一个圆圈来表示距离(攻击范围,可移动距离,视野等等)。如果地面是平坦的,那么可以用通过对一张绘制有圆圈的矩形纹理进行缩放就能简单的做到。但是如果地面并不平坦,那么这个矩形的纹理就很有可能被高出的山丘或者其他几何物体遮住。接下来的知识点将展示如何编写一个着色器,让你可以在任何复杂的物体表面绘制一个圆圈[且不会被遮住]。如果你想对这个圆圈移动或者执行动画,那么我们就需要有着色器和C#代码才行。下图展示了用着色器在一个丘陵地形中绘制一个圆圈的例子: 始前准备 这里的着色器主要是用于地形的,对于其他的游戏对象不适用。所以我们首先要做的是在Unity中创建好一个地形。 我们先分别建立一个着色器和材质,名字分别是RadiusShader和RadiusMaterial。 当你的角色物体准备好后;我们会绕着它绘制一个圆圈。 在Unity的菜单[这里的菜单操作我就不翻译了,翻译了感觉反而不好,怕翻译错了,大家找不到],选择 GameObject | 3D Object | Terrain 来创建一个新的地形。 为地形创建一些几何面。你可以导入一个已存在的地形或者自己用地形工具画一个新的。(Raise/Lower Terrain, Paint Height, Smooth Height )[括号里面这些都是Unity地形编辑器中的工具] 地形是Unity中特殊的游戏对象,它表面的纹理映射方式跟传统的3D模型不一样。你不能通过在着色器定义一个**_MainTex来提供纹理,因为在地形中需要直接由地形自己提供。这一步骤可以通过在地形编辑器中选择Paint Texture** 然后点击Add Texture…: 完成上面步骤后,纹理就设置好了。你必须修改地形的材质这样就可以在地形中使用我们提供的自定义的着色器。在 Terrain Settings中, 把Material一栏的属性改为Custom,接着把我们的Radius material材质拖拽到Custom Material栏上。 接下来就要准备你自己的着色器了。 操作步骤 让我们开始编辑着色器RadiusShader 的代码: 在新的着色器中, 添加下面四个属性: _Center("Center", Vector) = (0,0,0,0) _Radius("Radius", Float) = 0.5 _RadiusColor("Radius Color", Color) = (1,0,0,1) _RadiusWidth("Radius Width", Float) = 2 然后在CGPROGRAM块中添加它们各自的变量与之对应: float3 _Center; float _Radius; fixed4 _RadiusColor; float _RadiusWidth; 所以现在输入表面函数的数据不仅仅是纹理的UV数据了,还包括地形的中每一个点的位置(这个位置是基于世界坐标的)。 为了拿到这个参数我们需要修改输入结构体 Input struct,如下所示: struct Input { float2 uv_MainTex; // The UV of the terrain texture float3 worldPos; // The in-world position }; 最后我们在表面函数中使用这个参数: void surf(Input IN, inout SurfaceOutputStandard o) { float d = distance(_Center, IN....

January 16, 2021 · 1 min · 165 words · Link

纹理的压缩和混合

纹理的压缩和混合 纹理的作用并不仅仅只是我们通常认为的保存加载的数据或者像素颜色,同时还有像素点在x和y方向以及RGBA通道的各种设置。我们能把多张图片压缩进一张单独的RGBA纹理中并且使用它们各自的R,G,B和A元素,因为我们可以在着色器中把它们各自纹理中的这些元素分别解压出来。 将各自的灰度图压缩进一张单独的RGBA纹理的结果可以通过下图看出来: 为什么说这会有用呢?在你的应用程序实际消耗的大部分中内存当中,贴图占了很大的一部分。所以如果你想要减少应用程序的大小的话,我们能在着色器中查看所有使用的图片并且想想我们是否能将这些纹理合并到一张单独的纹理中。 任何灰度的纹理都可以压缩进另一张有RGBA通道的纹理。第一次听起来可能有点怪怪的,但我们接下来会用这个知识点来演示纹理压缩的用法并且在我们的着色器中使用这张压缩过的纹理。 举其中一个纹理压缩用法例子,比如你想把一套纹理[有好几张]混合进一张单独的纹理中。这在地形类着色器中很常见,在我们的例子中,我们会用一些排好序的控制纹理或者压缩过的纹理,很好的混合进另一张纹理中。这个知识点会讲到这个技术的,同时还会告诉你如何开始编写好这样一个混合四张纹理的着色器。 始前准备 在我们Unity的着色器文件夹中创建一个新的着色器同时创建一个新的材质与之对应。这两者的命名怎么方便怎么来,不过尽量保证组织和引用方便吧。 建好着色器和材质后,再创建一个新的场景,好给后面做测试。 收集好四张你打算混合在一起的纹理。我们直接用它们展示这几张纹理是如何放到物体表面的。 我们可以用一些非常复杂的混合纹理在地形网格上创建一个非常真实的地形分布纹理,如下所示: 操作步骤 我们通过下面代码来学习如何使用压缩纹理。 我们在着色器的属性Properties块中添加一些属性。我们需要5个sampler2D类型的游戏对象或纹理,2个颜色属性: Properties {_ MainTint ("Diffuse Tint", Color) = (1,1,1,1) //Add the properties below so we can input all of our textures _ColorA ("Terrain Color A", Color) = (1,1,1,1) _ColorB ("Terrain Color B", Color) = (1,1,1,1) _RTexture ("Red Channel Texture", 2D) = ""{} _GTexture ("Green Channel Texture", 2D) = ""{} _BTexture ("Blue Channel Texture", 2D) = ""{} _ATexture ("Alpha Channel Texture", 2D) = ""{} _BlendTex ("Blend Texture", 2D) = ""{} } 接下来我们在**SubShader{}**块中创建一些变量,记住要跟上一步的属性块对应。...

January 14, 2021 · 2 min · 297 words · Link

创建一个有全息效果的着色器

创建一个有全息效果的着色器 近些年来太空主题的发行的越来越多。科幻游戏中很重要的一个部分就是在游戏中集合了来自未来的各种技术。全息投影就是其中的典型代表。全息投影尽管有很多种形式,但是通常用一种半透明,看起来很薄的投影来呈现。这次的这个知识点将会向你展示如何创建一个这样的着色器来模拟这样的效果。我们首先想到:要创建一个优秀的全息投影特效,你需要能够添加噪音,扫描动画和和震动。下图就展示了一个全息投影效果的列子: 始前准备 正如全息投影效果展示的知识物体的轮廓,所以我们可以把我们的这个着色器命名成Silhouette[轮廓的意思]。把它跟材质关联起来并且把它应用到你的3D模型中去。 操作步骤 根据下面的步骤可以将我们的当前的着色器修改为有全息投影效果的着色器: 在着色器中添加下面的属性: _DotProduct("Rim effect", Range(-1,1)) = 0.25 并且添加跟属性对应的变量到CGPROGRAM块中去: float _DotProduct; 因为这个材质是有透明度的,所以需要添加下面的标签: Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } 注意 根据你将会使用的游戏对象类型,你可能想要它的背面也能看到。如果是这种情况,那么我们就需要在代码中添加Cull Off,从而让模型的背面不会被剔除。 这个着色器并不会尝试去模拟真实世界的材质,所以这里就没有必要再使用PBR关照模型了。我们将会用性能消耗更少的Lambertian 反射来代替它。另外,我们应该使用nolighting来关闭所有的光线并且用alpha:fade来告诉Cg我们得着色器是一个有透明度的着色器: #pragma surface surf Lambert alpha:fade nolighting 修改输入结构体从而能让Unity输入当前的视口方向和世界的法线方向: struct Input { float2 uv_MainTex; float3 worldNormal; float3 viewDir; }; 修改你的表面函数surface function成下面的样子。请记住因为这个着色器使用Lambertian反射作为光照函数,所以表面输出结构体的名字也要相应改成SurfaeOutput,这是SurfaceOutputStandard类型的实例。 void surf(Input IN, inout SurfaceOutput o) { float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; float border = 1 - (abs(dot(IN....

January 13, 2021 · 1 min · 126 words · Link

创建一个带透明度的材质

创建一个带透明度的材质 到目前为止我们所看到的着色器似乎都有一个共同点——它们都用于了固体材质。如果你想要的提升你的游戏的视觉效果,那么带透明度的材质通常是一个好的开始。它们用途广泛,从火焰效果到窗户的玻璃都会用到它们。但稍微麻烦的是,它们用起来要复杂一点点。在渲染固体模型之前,Unity会根据它们离摄像机的距离(这个叫Z ordering)进行排序,并且跳过渲染所有背朝摄像机的三角面(这个是剔除技术culling)。当渲染带透明度的几何物体时,这些含有两个面的几何物体就会产生问题。这次的知识点将会为你展示:当我们创建有透明度的表面着色器时,我们如何解决这个过程中产生的这些问题。我们在**第六章,片元着色器和抓取通道**我们还会着重回顾这个话题,真实渲染中的玻璃和水体着色器都会涉及到。 始前准备 这个知识点需要一个新的着色器,我们就叫它Transparent吧。同时也需要一个新的材质,这样才能把着色器应用于游戏物体。因为这个物体需要成为一个透明的玻璃窗,那么我们最好用Unity中的quad或者plane来做。我们当然也需要一些不透明的其他游戏对象来对比测试效果。在这个例子中,我们会使用一张PNG图片作为玻璃纹理。这张图片的alpha通道将会用于控制玻璃的透明度。这样的PNG图片大家自行自作,软件不限。但是需要遵守下面的这些步骤: 找一张要用于你窗户玻璃的图片。 用照片编辑软件打开这样图片,比如GIMP或者Photoshop。 选择图片中你想要变成半透明的部分。 在这张图片上创建一个空白(full opacity[抱歉我不懂PS,不知道这个参数的含义])图层 选择上一步创建的图片,以黑色来填充这个图层。 保存图片然后导入到Unity中。 这个知识点中,我们用来试验的图片是一张来自圣斯德望主教座堂 ***Meaux Cathedral in France(https://en.wikipedia.org/wiki/Stained_glass)***的花窗玻璃。 如果遵循了上面的图片制作步骤,那么你也会获得如下类似的一张图片(RGB通道在左图,A通道在右图): 操作步骤 正如我们前面提醒的,我们在使用透明度着色器时需要注意几个方面: 在着色器的SubShader{}代码块中,添加下面的标签告知着色器这是用于透明度的: Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } 由于这个着色器是为2D 材料设计的,所以确保你的模型的背面不会被绘制,通过添加下面的这个代码: Cull Back 告诉着色器这个材料是半透明的并且需要跟屏幕中绘制的什么内容混合: #pragma surface surf Standard alpha:fade 使用这个表面着色器来决定玻璃的颜色和透明度: void surf(Input IN, inout SurfaceOutputStandard o) { float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; } 原理介绍 这个着色器中介绍了几个新的概念。首先,标签Tags用于添加一些关于游戏对象是如何渲染的信息。但是这次这里真正让人感兴趣的是Queue。Unity默认会根据物体距离摄像机的距离来对游戏对象进行排序。所以当一个物体距离摄像机越近,那么它就会比所有离摄像机更远的物体先绘制。在大多数情况下,它对游戏来说都没有问题,但是有些情况下你可能想要自己控制游戏场景中物体的渲染顺序。Unity已经有提供给我们一些默认的渲染队列,每一个队列都有一个单独的值好让Unity按照这个顺序在屏幕中绘制游戏物体。这些内建的渲染队列分别叫做**[这些参数就不翻译了]** Background,Geometry,AlphaTest,Transparent, 和 Overlay。 这些队列并不是随意创建的;创造它们是为了让我们能更容易的编写着色器代码和跟实时渲染进行交互。下表描述了每一个不同的渲染队列的作用:...

January 12, 2021 · 1 min · 98 words · Link

法线贴图

法线贴图 3D模型的每一个三角面都有一个朝向facing direction,这个朝向就是它向前的指向。它通常用一个垂直并且放置在三角面正中心的一个箭头来表示。这个朝向对于光在表面反射中来说非常的重要。如果相邻的两个三角面朝向不同的方向,那么它们对光的反射也会朝向不同的角度,也就是在着色器中它们的处理方式会不一样。对于曲面物体来说,这里有个疑问:很显然这些拥有曲面的几何体仍然是由平面三角形构成的,那光线改如何处理? 为了避免这个问题,对应三角面的光的反射计算方式此时不根据它的朝向计算,而是根据它的法线方向normal direction方向计算。前面向着色器添加纹理那个知识点讲到,顶点是保存有数据的。法线方向信息也是继UV信息之后,保存在顶点中最有用的信息。这是一个单位长度的向量,并且它表示了顶点的朝向。不考虑朝向的话,那么三角面内的每一个顶点都有它自己的法线方向,只不过这个法线方向是一个存储在顶点中的线性插值。这给了我们用低模模拟高模的能力。下面示例图展示了同一个几何形状在不同的顶点插值密度下的表现。在左边,法线垂直于由顶点表示的面;很明显每个面之间有明显的的割裂感。再看看最右边的几何体,它的法线是通过他的面线性插值得到的,可以看出来的是,尽管它的表面看起来还是很粗糙,但是光线的反射看起来却似乎很光滑。很容易看出来尽管这三个物体的几何体都相同,但是它们的光线反射却不一样。尽管都是由平面三角形构成,但是右边物体的光线反射似乎看起来像曲面反射。 一个有着粗糙的边的光滑物体很明显的表示单位顶点的法线肯定进行了线性插值。如果我们对保存在每个顶点的法线按其方向进行绘制,我们就能够看到它们,正如下图所展示的那样。你应该注意的是每个三角形仅有三条法线,但是相连的三角形有相同的顶点,会看到不止有一条法线从中绘制出来。 法线贴图在计算3D模型的法线技术中脱颖而出。跟纹理贴图类似,法线方向也可以用一张额外的纹理表示,我们把它们叫做法线贴图normal map或者凹凸贴图bump map。 法线贴图通常是一张RGB图片,里面的RGB通常分别用来表示法线方向中的X,Y,Z方向。现在有很多种技术方法来创建一张法线贴图。比如这些应用程序,CrazyBump(http://www.crazybump.com/)跟NDO Painter(http://www.crazybump.com/)可以把2D数据转换成法线数据。其他的应用程序比如Zbrush 4R7(http://www.pixologic.com/)和AUTODESK(http://usa.autodesk.com)可以把雕刻数据转换成法线贴图。如何创建法线贴图完全超出了本书的范畴,但上面的内容对你了解相关相应的知识还是有好处的。 在Unity中向着色器添加法线的过程很简单,因为在表面着色器中有着**UnpackNormals()**这样的方法给你调用。就让我们看看这是怎样的一个过程。 始前准备 分别创建一个新的材质和着色器,并且把它设置到场景视图Scene view中的游戏对象中去。这样的话,我们的项目非常简单,好让我们仅仅是观察法线贴图这项技术。 这个知识点中你需要一张法线贴图,但是我们这本书附带的Unity工程中包含了一张。[当然,你也可以从我这里把这张图片下载下来,如下图] 操作步骤 下面就是创建法线贴图着色器的步骤了: 让我们设置好我们的属性块,从而可以获得颜色和贴图: Properties { _MainTint ("Diffuse Tint", Color) = (1,1,1,1) _NormalTex ("Normal Map", 2D) = "bump" {} } 注意 因为用的是bump来初始化了属性的贴图类型,这等于是告诉了Unity**_NormalTex包含了法线贴图。如果这个贴图没有被设置, 那么会默认给它设置一张灰色的贴图。颜色值会用(0.5,0.5,0.5,1)**,然后看不出一点凹凸感。 在CGPROGRAM下面的**SubShader{}**块中声明下面两个变量,让这两个变量跟属性块中的两个属性关联起来: CPROGRAM #pragma surface surf Lambert // Link the property to the CG program sampler2D _NormalTex; float4 _MainTint; 我们需要修改输入结构体Input struct的名字,从而让我们可以让我们通过模型的UV来访问法线贴图: // Make sure you get the UVs for the texture in the struct struct Input { float2 uv_NormalTex; } 最后,我们通过内建的**UnpackNormal()**函数从法线贴图中提取出我们需要的法线信息。接着,你只要把这些新的法线应用到表面着色器的输出上即可:...

January 9, 2021 · 1 min · 148 words · Link