向PBR中添加透明度

向PBR中添加透明度 透明度是游戏中很重要的一个方面,标准着色器支持三种不同的实现方式。 如果你想让你的材质获得很逼真的透明或者半透明属性,这个知识点非常有用。玻璃,玻璃瓶子,玻璃窗和各种结晶体都很适合PBR透明着色器。这是因为你依然可以获得PBR带来的包含透明和半透明的效果的逼真效果。 如果你想让UI或者像素艺术这样的不同的东西也具有半透明效果,这里由更加高效的可选方法,就是在 第二章 表面着色器和纹理贴图 这一章节中,创建一个带透明度的材质 这个知识点。 注意 为了能获得一个有透明度的标准材质,仅仅是修改它的 Albedo 颜色属性的alpha通道是不够的。除非你把 Rendering Mode 设置成transparent,否则你的材质是不会产生透明度的。 始前准备 这个知识点将会使用 标准着色器(Standard Shader) ,所以我们没有必要创建新的着色器了: 创建一个新的材质球 在材质球的 检查器(Inspector) 面板确保 Shader 这个属性设置为了 Standard 或者 Standard (Specular setup) 。 然后把这个新创建好的材质球应邀到你想要实现透明度的3D模型上。 操作步骤 标准着色器提供了三种不同类型的透明度。尽管非常的相似,但是它们仍然又细微的差别,并且适用的情形也是不同的。 半透明材质 像干净的塑料,晶体和玻璃这些材料是半透明的。 这就意味着它们都需要PBR这种逼真的效果(比如高光高光,菲涅尔折射和反射)而且允许几何体后面也能被看见的话。如果这是你想要的效果,就按照下面的步骤走: 在材质球的 检查器(Inspector) 面板,把 Rendering Mode 这个属性设置为 Transparent 。 透明度的数值由 Albedo 颜色或者 Albedo 贴图(如果有)的alpha通道来决定。 下图展示的是Unity5校准场景中四种高度抛光过的塑料球。从左到右,它们的透明度逐渐增高。最后的一个球是完全透明的,但依然保有PBR中添加的效果: Transparent 这个渲染模式非常适合窗户,瓶子,宝石和头戴式耳机 注意 你需要注意的是大部分的透明材质不会投射阴影。 除此之外,材质的 Metallic 和 Smoothness 也会影响到透明效果。镜子似的表面可以通过把alpha设置为0获得,但是如果它反射所有入射光的话,它就不会表现出透明效果。 渐隐的游戏对象 有时候,你想用渐隐效果让一个游戏对象完全消失。在这个例子中,高光反射,菲涅尔折射 和反射等效果也会消失。当一个渐隐的游戏对象完全透明,它应该是看不见的。为了完成这些,按照下面的步骤操作: 在材质的 检查器(Inspector) 面板,把 Rendering Mode 设置为 Fade 。 如前面一样,用 Albedo 的颜色或者贴图的alpha通道来决定最终的透明度。 下图展示了一个渐隐的球体。从图中明显可以看出PBR效果也随着渐隐效果逐渐消失。正如你从下面图见到的那样,往右最后的那个球近乎消失不见了: ...

March 15, 2021 · 1 min · 120 words · Link

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

创建各向异性类型的高光反射着色器 各向异性(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.Normal, lightDir)); fixed HdotA = dot(normalize(s.Normal + s.AnisoDirection),halfVector); float aniso = max(0, sin(radians((HdotA + _AnisoOffset) * 180))); float spec = saturate(pow(aniso, s.Gloss * 128) * s.Specular); fixed4 c; c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL) + (_LightColor0.rgb * _SpecularColor.rgb * spec)) * atten; c.a = s.Alpha; return c; } 为了使用我们新的光照函数,我们需要修改**#pragma**指示,让Unity去使用我们的光照函数,而不是Unity内建的光照函数。我们同时还要告诉着色器目标着色器模式是3.0,这样我们可以让我们的程序拥有更多的空间用来存放纹理: CGPROGRAM #pragma surface surf Anisotropic #pragma target 3.0 为了让各向异性法线贴图能使用它自己的UV数据,我们需要再 输入(Input) 结构中添加如下定义代码。 我们其实并不是说完全需要这样做,因为我们也可以使用主帖图上的UV数据,但是如果有它自己单独UV数据的话我们就能单独控制金属抛光效果的截取,这样我们就可以把它进行缩放任何我们想要的大小: { float2 uv_MainTex; float2 uv_AnisoDir; }; 我们还需要定义 SurfaceAnisoOutput 这个输出结构体: struct SurfaceAnisoOutput { fixed3 Albedo; fixed3 Normal; fixed3 Emission; fixed3 AnisoDirection; half Specular; fixed Gloss; fixed Alpha; }; 最后,我们需要通过表面函数 surf() 给我们的光照函数传递正确的数据。这样,我们将会获得我们各向异性法线贴图的每个像素信息,然后像下面的代码一样设置我们的高光参数: void surf(Input IN, inout SurfaceAnisoOutput o) { half4 c = tex2D(_MainTex, IN.uv_MainTex) * _MainTint; float3 anisoTex = UnpackNormal(tex2D(_AnisoDir, IN.uv_AnisoDir)); o.AnisoDirection = anisoTex; o.Specular = _Specular; o.Gloss = _SpecPower; o.Albedo = c.rgb; o.Alpha = c.a; } 各向异性法线贴图允许我们给出表面的方向并且能帮助我们分散物体表面周围的高光效果。下图就是我们的各向异性着色器的效果: ...

February 26, 2021 · 2 min · 333 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.Normal, halfVector)); float spec = pow(NdotH, _SpecPower) * _SpecularColor; float4 c; c.rgb = (s.Albedo * _LightColor0.rgb * NdotL) + (_LightColor0.rgb * _SpecularColor.rgb * spec) * atten; c.a = s.Alpha; return c; } 为了完成我们的着色器,我们还需要告知着色器的 CGPROGRAM 块使用我们自定义的光照模型而不是Unity内建的,修改**#pragma**指示,改成如下代码所示: CPROGRAM #pragma surface surf CustomBlinnPhong 下图演示了我们自己的 BlinnPhong 光照模型的效果: ...

February 25, 2021 · 1 min · 180 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.0 * s.Normal * NdotL - lightDir); // Specular float spec = pow(max(0, dot(reflectionVector, viewDir)), _SpecPower); float3 finalSpec = _SpecularColor.rgb * spec; // Final effect fixed4 c; c.rgb = (s.Albedo * _LightColor0.rgb * max(0,NdotL) * atten) + (_LightColor0.rgb * finalSpec); c.a = s.Alpha; return c; } 最后,我们还要告诉 CGPROGRAM 块,需要用我们自定义的光照模型而不是Unity内建的光照模型。 按照下面的步骤修改 #pragma 指示: CPROGRAM #pragma surface surf Phong 下图演示了我们自定义的 Phong 光照模型的效果,里面的反射算法也是我们自己的: ...

February 25, 2021 · 2 min · 262 words · Link

创建一个Toon风格的着色器

创建一个Toon风格的着色器 Toon着色器(toon shading) 是游戏中最常使用的效果之一,也被称作(AKA) cel shading (cel是celluloid的缩写[中文也叫 赛璐珞])。它是一种非真实渲染技术,可以让3D模型呈现一种平面效果。许多游戏中用这种着色器把3D的物体渲染成一种手绘物体的效果。下图中你能看到这两者的区别,右边的是标准着色器,左边的是Toon着色器: 如果只使用 表面函数(surface functions) 虽然也能获得这样的效果,但是花费性能和时间的代价太大了。表面函数仅仅对材质的属性起作用,而对材质的具体光照环境无能为力。因为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 贴图是如何纠正光的强度的: ...

February 23, 2021 · 1 min · 149 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.rgb = s.Albedo * _LightColor0.rgb * (NdotL*atten*1); c.a = s.Alpha; return c; } 原理介绍 ...

February 22, 2021 · 2 min · 223 words · Link

在地形的表面绘制一个圆

在地形的表面绘制一个圆 在很多的RTS类型的游戏中,通过围绕被选中的单位绘制一个圆圈来表示距离(攻击范围,可移动距离,视野等等)。如果地面是平坦的,那么可以用通过对一张绘制有圆圈的矩形纹理进行缩放就能简单的做到。但是如果地面并不平坦,那么这个矩形的纹理就很有可能被高出的山丘或者其他几何物体遮住。接下来的知识点将展示如何编写一个着色器,让你可以在任何复杂的物体表面绘制一个圆圈[且不会被遮住]。如果你想对这个圆圈移动或者执行动画,那么我们就需要有着色器和C#代码才行。下图展示了用着色器在一个丘陵地形中绘制一个圆圈的例子: 始前准备 这里的着色器主要是用于地形的,对于其他的游戏对象不适用。所以我们首先要做的是在Unity中创建好一个地形。 我们先分别建立一个着色器和材质,名字分别是 RadiusShader 和 RadiusMaterial 。 当你的角色物体准备好后;我们会绕着它绘制一个圆圈。 在Unity的菜单[这里的菜单操作我就不翻译了,翻译了感觉反而不好,怕翻译错了,大家找不到],选择 GameObject | 3D Object | Terrain 来创建一个新的地形。 为地形创建一些几何面。你可以导入一个已存在的地形或者自己用地形工具画一个新的。( Raise/Lower Terrain , Paint Height , SmoothHeight )[括号里面这些都是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.worldPos); if (d > _Radius && d < _Radius + _RadiusWidth) o.Albedo = _RadiusColor; else o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb; } 通过上面的步骤,你就可以在地形中绘制一个圆。你可以通过材质的检查器面板Inspector tab去改变这个圆的位置,半径和颜色。 ...

January 16, 2021 · 1 min · 204 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{} 块中创建一些变量,记住要跟上一步的属性块对应。 CGPROGRAM #pragma surface surf Lambert float4 _MainTint; float4 _ColorA; float4 _ColorB; sampler2D _RTexture; sampler2D _GTexture; sampler2D _BTexture; sampler2D _BlendTex; sampler2D _ATexture; 我们现在获得了纹理属性后把它们传递给 SubShader{} 函数。为了能够让使用者可以控制每个纹理的截取比例,我们需要修改 输入结构体Input struct 。这样我们就可以使用每个纹理的截取和偏移量等参数: ...

January 14, 2021 · 2 min · 336 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.viewDir, IN.worldNormal))); float alpha = (border * (1 - _DotProduct) + _DotProduct); o.Alpha = c.a * alpha; } 现在你可以使用 Rim effect 这个滑动条来选择全息投影效果的强度。 ...

January 13, 2021 · 1 min · 165 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; } 原理介绍 ...

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