模型挤压

模型挤压 重复是游戏当中最要命的一个问题之一。在游戏中创建新内容是一项费时的任务,当你要创建成千上万的敌人时,那么有很大的可能这些敌人有可能会看起来一样。这个时候利用着色器修改模型的基本几何图形,从而让模型产生不同的变化,可以说是一种 相对来说性能较好的方法。这个知识点中我们会给你演示一种叫做 法线挤压(normal extrusion) 的技术,可以用来创建胖的或者瘦的模型,就比如下图所示的来自Unity的demo中的士兵: 始前准备 在这个知识点中,我们需要获取要挤压的模型的着色器。获得该着色器后,复制它,因为这样能安全的编辑拷贝的着色器。我们可以按照下面的步骤进行: 1.找到模型使用的着色器[原着色器就是一个标准的着色器,没有找到就自己新建一个也行],通过快捷键 Ctrl + D 复制它。 2.复制模型原有的材质,并且将之前复制的着色器添加给它。 3.把这个新的材质添加到模型上,并且开始编辑它。 为了达到效果,你的模型还要有 法线(normals) 。 操作步骤 为了创建这个效果,我们先来编辑刚刚复制的着色器: 1.首先给我们的着色器添加一个属性,用于调整压缩效果。我们这里的调节范围是从 -0.00005 到 0.00005 【书上是-1到1,但实际上书上的范围太大了,各位可以自己试试】,当然你可以根据自己的需要调节这个范围【 _MainTex 是需要的,因为人物是需要贴图的。】: _MainTex("Texture", 2D) = "white" {} _Amount ("Extrusion Amount", Range(-0.00005, 0.00005)) = 0 2.我们的属性和变量是成对出现的,在着色器中定义下面的变量: float _Amount; 3.修改 pragma 预编译指令让Unity知道我们要使用 顶点修饰(vertex modifier) 。然后在它后面添加 vertex:function_name ,function_name就是你自定义的方法名,当我我们这里叫 vert: #pragma surface surf Lambert vertex:vert 4.添加下面的顶点修改代码: void vert (inout appdata_full v) { v.vertex.xyz += v.normal * _Amount; } 5.这样着色器就写好了;这样你就可以在 检查器面板(Inspactor) 中通过修改材质上的 Amount 参数来控制士兵的胖瘦了。【书本写的不清楚,其实还有贴图的代码,作者没有交代,所以完整的代码如下:】 Shader "Custom/Normal Extrusion" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _Amount ("Extrusion Amount", Range(-0.00005, 0.00005)) = 0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Lambert vertex:vert // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; float _Amount; // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing. // #pragma instancing_options assumeuniformscaling UNITY_INSTANCING_CBUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_CBUFFER_END void vert (inout appdata_full v) { v.vertex.xyz += v.normal * _Amount; } void surf (Input IN, inout SurfaceOutput o) { // Albedo comes from a texture tinted by color o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb; } ENDCG } FallBack "Diffuse" } 原理介绍 表面着色器(Surface shader) 分两步来工作。在前面的章节中,我们只是探索了它最后一个步骤:表面函数(surface function。这里我们接触了另一种函数:顶点修饰(vertex modifier) 。它接收一个顶点的数据结构 (通常这个数据结构叫做 appdata_full )并且对它进行转变。它让我们对于模型的几何图形做各种视觉效果给与很大的自由度。我们通过在表面着色器的预编译指令 #pragma 那里添加 vertex:vert 来告知GPU 我们添加了 自己的顶点函数。你可以查看 第六章,片元着色器和抓取通道 来学习怎么在顶点着色器和片元着色器中定义顶点修饰。 ...

July 11, 2022 · 2 min · 358 words · Link

对表面着色器中的顶点使用动画

对表面着色器中的顶点使用动画 我们现在知道了如何访问每个顶点数据的一些基础知识,这次让我们更进一步的了解一些其他类型的数据和顶点的位置。 使用顶点函数,我们可以访问网格中每个顶点位置。具体来说就是可以在着色器处理渲染的过程中,这些函数可以让我们单独对每一个顶点进行修改。 这个知识点当中,我们会创建一个着色器,并且用三角函数的正弦函数( sine wave )来修改网格当中的每一个顶点。该技术可以用来创建旗子飘动或者海浪等物体动画。 始前准备 我们把资源都放一块儿,这样方便我们为顶点着色器( Vertex Shader )编写代码: 1.创建一个新的场景,并且创建一个平面网格( plane mesh ),把它放在场景正中央,位置归零。 2.然后创建一个新的材质和着色器。 3.最后,把着色器挂到材质上,在把材质挂到平面网格上。 最终,你的场景看起来应该跟下图一样: 操作步骤 场景创建好后,鼠标双击打开刚刚我们创建的着色器: 1.让我们给着色器的属性块下面的预设值: Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _tintAmount ("Tint Amount", Range(0,1)) = 0.5 _ColorA ("Color A", Color) = (1,1,1,1) _ColorB ("Color B", Color) = (1,1,1,1) _Speed ("Wave Speed", Range(0.1, 80)) = 5 _Frequency ("Wave Frequency", Range(0, 5)) = 2 _Amplitude ("Wave Amplitude", Range(-1, 1)) = 1 } 2.接下来我们通过下面的声明告诉Unity,我们需要使用顶点函数了,添加声明的位置在: #pragma statement: CGPROGRAM #pragma surface surf Lambert vertex:vert 3.为了访问属性块中的值,我们还要在 CGPROGRAM 块中声明与之对应的变量: sampler2D _MainTex; float4 _ColorA; float4 _ColorB; float _tintAmount; float _Speed; float _Frequency; float _Amplitude; float _OffsetVal; 4.我们也将使用顶点位置的变化作为 vert 颜色。因为这样我们可以顺便修改物体的颜色了: struct Input { float2 uv_MainTex; float3 vertColor; } 5.此时,我们要修改顶点的表现了,我们要使用到下面的正选函数和顶点函数,在我们的 Input Struct 后面添加如下代码: void vert(inout appdata_full v, out Input o) { float time = _Time * _Speed; float waveValueA = sin(time + v.vertex.x * _Frequency) * _Amplitude; v.vertex.xyz = float3(v.vertex.x, v.vertex.y + waveValueA, v.vertex.z); v.normal = normalize(float3(v.normal.x + waveValueA, v.normal.y,v.normal.z)); o.vertColor = float3(waveValueA,waveValueA,waveValueA); } 6.最后,我们要使用一个 lerp() 函数对两个颜色进行一个插值,这样我们就可以对新网格的波峰和波谷应用不同的颜色,为了获得这种表现,添加下面的代码让顶点函数起作用: void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); float3 tintColor = lerp(_ColorA, _ColorB, IN.vertColor).rgb; o.Albedo = c.rgb * (tintColor * _tintAmount); o.Alpha = c.a; } 当你完成了着色器的编写后,回到Unity并且等待着色器编译完成。当编译完成后,你就会看到如下图所示的情形: ...

July 9, 2022 · 1 min · 206 words · Link

顶点函数

第五章 顶点函数 着色器(shader) 这个术语最开始源于Cg领域,它主要用于模拟现实中的光照(阴影)在3D模型中的表现。而如今,着色器的用途远不止上面这些。它不仅可以定义物体的外观表现方式,还可以完全重新定义它们的形状。 如果你想学习如何通过着色器来修改3D模型的几何形状,这一章的内容非常适合你。 在这一章,你将会学习下面这些知识点: 在表面着色器(Surface Shader)中访问顶点颜色 对表面着色器中的顶点使用动画 模型挤压 实现下雪效果着色器 实现范围体爆炸 介绍 在第一章,创建你的第一个着色器 中,我们 解释了3D模型不仅仅是一些三角形的集合。每一个顶点都包含了要正确渲染这个模型所必须的一些数据。这一章,为了在着色中能使用这些数据,我们将探索如何访问这些信息。而且我们还将探索如何使用Cg代码对物体进行形变的具体细节。 在表面着色器中访问顶点颜色 在这个章节,让我们来看看如何在着色器中用顶点函数来访问这些模型的顶点信息。这些知识旨在让我利用模型顶点包含的信息元素来创造一些真正实用的和引人入胜的视觉效果。 顶点函数中的顶点能返回一些我们想要的信息。你可以用 float3 这类值来获得顶点的法线方向信息,顶点的位置信息。你甚至可以将颜色值保存在每个顶点中,并且用 float4 这类值来返回该颜色。这些将是这个知识点我们将要讲的东西。 我们需要理解在 表面着色器(Surface Shader) 中,如何将颜色信息保存到顶点中以及如何获取这些颜色信息。 始前准备 为了编写这个着色器,我们需要准备一些资源。下面这些步骤是我们创建这个顶点着色器(Vertex Shader)的准备和设置: 1.为了能够看见顶点颜色,我们需要一个顶点设置了颜色的模型。当然你可以用Unity来添加这些颜色,这样的话你不得不编写一个工具来单独的应用这些颜色,或者编写一些脚本来获取刚刚添加的颜色。但是在这里,我们简单的利用 Maya 来把颜色应用到我们的模型顶点上。但是现在这个模型你可以在这本书的支持页面获得https://www.packtpub.com/books/content/support【实际上这个地址根本没有这个模型,这本书的代码我放在这里了 本书的代码包 】 2.创建一个新的场景,并且把导入的模型放到场景中。 3.创建一个新的着色器和一个新的材质。完成创建后,把着色器添加到材质上,然后把材质添加到模型上。 你的场景看起来应该跟下面的屏幕截图一样: 操作步骤 随着我们的场景,着色器,材质这些创建好后,我们就可以开始为我们的着色器编写代码了。我们在编辑器的 Project 面板中双击我们创建的Shader打开它。然后跟着下面的步骤走: 1.因为我们创建的着色器很简单,所以在着色器的 属性(Properties) 块中没有必要包含任何属性。但我们仍然需要有一个全局的tint颜色,仅仅是为了跟书中的其他着色器形式上保持一致而已。在着色器的属性块中写入以下代码: Properties { _MainTint("Global Color Tint", Color) = (1,1,1,1) } 2.下面这一步中, 是告诉Unity我们将在着色器中包含一个顶点函数: CGPROGRAM #pragma surface surf Lambert vertex:vert 3.跟往常一样,如果我们在属性块中包含了属性,那么我们要确保在 CGPROGRAM 声明中定义与之对应的变量才行。我们在 #pragma 声明的正下方输入下面的代码: float4 _MainTint; 4.接下来我们要关注 输入结构体(Input struct) 。我们还要再定义一个新的变量,主要是给我们的 surf() 方法用,用来接收从 vert() 这个函数中返回的数据: ...

June 26, 2022 · 1 min · 205 words · Link

烘培场景中的光

烘培场景中的光 渲染光照的过程是非常消耗新能的。即使是目前最好的[state-of-the-art]1GPU,要准确的计算 光的运动(light transport) [是指光在物体表面之间反射]也要花好些小时。 为了在游戏中让这个过程可行,光的实时渲染是必须要有的。如今的游戏引擎会在画面和效率上采取一个合理的折中方案;大部分的计算都在一个叫 光烘焙(light baking) 过程中事先计算好了。这一个知识点将会讲解光烘焙是如何工作的并且你该如何充分的利用它。 始前准备 光的烘焙需要你先准备一个场景。里面要有一些几何体,当然光也一定要有。这个知识点我们会基于Unity的标准特性,所以没有必要创建新的着色器或者材质。为了更好的控制这个过程,你可能需要访问 Lighting 窗口。如果你没有看见这个窗口,可以通过菜单 Window | Lighting 打开,并且停靠到一个你方便使用的位置。 操作步骤 光的烘培需要一些自定义的配置。你需要做三个必要的独立步骤。 场景中静态几何体的配置 配置的时候必须按照下面的步骤: 确认好你的场景中所有不会改变位置,大小和材质的游戏物体。一般可能的是建筑物,墙,地形,道具,树木等等。 在场景中选择这些类型的游戏对象,并且在 检查器面板(Inspector tab) 中勾选 Static 这个勾选框,就如下图所示。如果任何一个被选择的对象有子对象,Unity编辑器会询问是否想把这些子对象也作为静态对象。如果它们也满足(固定的位置,大小和材质),则在弹出的对话框中选择 是,子对象也变为静态(Yes change children) : 如果想让光照即影响静态游戏对象又影响非静态游戏对象,请确认把 Baking 属性设置为 Mixed 。如果只希望影响到静态游戏对象,那么把它设置为 Baked 。 光探针的设置 在你的游戏场景中有些游戏对象是会移动的,比如主角,敌人和 NPC(non-playable characters) 。如果它们进入了一个被照亮的静态区域,那么你也许想要放置一些光照探针在他们的周围。为了做到这些,请按下面的操作步骤走: 在编辑器菜单中,依次选择 GameObject | Light | Light Probe Group 。一个名为 光探针组(Light Probe Group) 的新游戏对象就出现在 检查器面板(Inspector tab) 中了。 一旦你选中这个光探针组,八个相互链接球体会出现。点击选中它,并且在场景中移动它,让它尽可能把你的角色会进入的静态区域围起来。 下图展示的就是如何用光照探针把静态的办公区域空间给围起来的例子: 3. 选择会进入光照探针区域的移动游戏对象。 4. 在 检查面板(Inspector tab) 中,展开 渲染组件(renderer component) [通常是 网格渲染器(Mesh Renderer) ] 并且确认 Use Light Probes 这个选项被勾选了(如下图所示): ...

May 21, 2021 · 1 min · 133 words · Link

创建镜子和反射面

创建镜子和反射面 当我们从一定的角度看高光材质物体时,物体会反射光。然而可惜的是,即使是最精确的光照模型之一: 菲涅尔反射Fresnel reflection ,也不能完全准确的反射来自周围物体的光。前一个章节验证过的光照模型只考虑了一些光源,但是却忽略了来自其他表面的反射光。很显然,用目前我们学的关于着色的知识,来创建一面镜子是不可能的。但是 全局光照Global illumination 技术提供了这种可能性,这需要提供包含了周围光照信息的PBR着色器。 这就要求物体不仅需要有高光部分,还需要有依赖周围其他物体的真实的反射部分。实时的反射非常消耗性能并且需要一些自定义的设置和调整才能工作,它们可以用来创建类似高光的效果,就如下图所示: 始前准备 这个知识点中不会涉及新的着色器。恰恰相反,大部分的工作都可以直接在编辑器上完成。就像下面的步骤展示的那样: 创建一个新场景 然后在场景中创建一个 quad ,这个quad会用来作为镜子。 创建一个新的材质并且把它跟这个镜子关联起来。 把这个quad放在另一个游戏对象前面。 在Unity菜单中通过 GameObject | Light | Reflection Probe 的步骤创建一个 反射探针reflection probe 并且把它放在quad的前面。 操作步骤 如果正确的按照前面的步骤操作,那么在你的场景中间会有一个quad,靠近它还有一个反射探针。为了让它出现在镜子中,还需要做西面的这些改变: 把材质的着色器类型改成 Standard 并且把 Rendering Mode 设置成 Opaque 。【这里的属性英文不翻译把,因为Unity大部分人都是用的英文,我担心找不到】 把 Metallic 和 Smoothness 这两个的属性设置为1。你可以看到材质会很清晰的反射天空盒。 选择反射探针并且修改 Size 和 Probe Origin 直到它刚好在quad的前面,并且让探针包含你想反射的物体。 最后把 Type 改成 Realtime 。确定 Culling Mask 设置的是 Everything 。 这样的话,你的反射探针就设置好了,看起来就跟下图一样: 如果你的探针是用来做真正的镜子,那么你应该勾选 Box Projection 这个选项。如果你是用来做一些类似于亮晶晶的小金属碎片或者玻璃桌子,那么你就不应该勾选这个选项。 原理介绍 当着色器想要周围环境信息的时候,它自己提供了一个叫做 cube maps 的数据结构。它在第一章, 创建你的第一个着色器 简短的提及过,跟 Color ,2D ,Float 和 Vector 这些结构一样,都是Cg语言中的一种数据结构。笼统一点来说,cube maps 就是2D纹理的3D数据结构。它们表示从中心点看过来的360度的世界视角。 Unity5可以通过特殊的投影预览 cube maps ,如下图所示: ...

May 17, 2021 · 1 min · 117 words · Link

Unity 5中基于物理原理的渲染

第四章 Unity 5中基于物理原理的渲染 基于物理原理的渲染physically-based rendering 是Unity5中加入的最大的变化之一,也就是我们常说的 PBR 。前面的一些章节重复提到过它但是没有却没向大家过多的展现。如果你不仅想知道PBR的工作原理,还想搞明白如何构建它们,那么这一张正是你需要阅读的。 在这一章,你将会学习下面的几个知识点: 理解金属质感的设置 向PBR中添加透明度 创建镜子和反射面 烘培场景中的光 介绍 我们在 第三章 , 理解光照模型 中介绍了所有的光照模型,简单的讨论了一下光是如何表现的。在编写它们的时候 效率efficiency 是最重要的方面。实时渲染的开销是很大的,类似于 Lambertian 和 BlinnPhong 这样的光照模型技术,也仅仅是在模拟现实和性能开销中的折中方案。拥有了更加强劲的 GPU(graphics processing unit) 后,我们就可以编写更加精细的光照模型和渲染引擎,目的就是为了模拟光的真实行为。简单概括前面的来说,这就是PBR后面的哲学。正如它的名字所表达的那样, 它是尽可能的去接近真实的物理,来处理每一个不同材质,让他们看起来都不一样。不仅如此,PBR这个术语被广泛的用于市场营销,它更像是 艺术级的渲染(state-of-the-art rendering) ,而不是一个简单的技术。Unity5通过引入两个重要的改变,实现了PBR。首先是一个全新的光照模型(叫 Standard )。表面着色器允许开发人员指定材质的物理属性,但是他们却没有对它们应用任何的物理原理限制。PBR用新的光照模型来弥补了这个差距,应用了一些物理原理,比如 能量守恒(energy conservation) [一个物体反射的光线不可能多于接收的光线], 微表面散射(microsurface scattering) [粗糙表面比光滑表面的反射更没有规律], 菲涅尔反射(Fresnel reflectance) [高光反射出现在掠射角内],和 表面阻塞(surface occlusion) [一些角落的暗部和一些几何体很难照亮]。所有说的这方面,还有一些其他的,都被用来计算标准光照模型。第二方面让PBR开起来如此真实技术叫 全局光照(Global Illumination [GI]) ,它是对基于物理原理的光线传输的模拟。也就是说,如果这些物体是独立的实体,那么它们不会被绘制[不会反光只吸收光的的物体,这种绝对黑体是看不见的]。它们会影响最终的渲染效果,因为光线在碰到其他物体前首先会从它们身上反射。虽然在着色器中不会自动提及这方面,但是对于了解渲染引擎是如何工作来说是很重要的部分。然而令人难过的是,实时的精确模拟光线在物体表面到底是如何反弹,这已经超出了现代GPU的能力范围。Unity5做了一些很聪明的优化,即保持了视觉质量有没有牺牲性能。然而大部分的一些进阶技术(比如反射) 需要用户的输入。上面说的这些方面都会在本章介绍。 不过希望各位留意的是,即使是PBR或者GI这些技术也不能保证你的游戏可以获得照片级的画质。要获得照片级的画面是一项很有挑战性的工作,跟每一门艺术一样,需要非常专业和杰出的技巧。 理解金属质感的设置 Unity5提供了两种不同类型的PBR着色器; 它们指的是材质的 检查器面板(Inspector) 中的下拉列表中的 Standard 着色器和 Standard (Specular setup) 着色器。两者的主要区别在于前者为我们暴露了 Metallic 这个属性,而后者没有 Metallic ,但暴露了 Specular 这个属性。metallic 属性和specular 属性代表了初始化PBR材质不同方式。推动PBR的概念之一是提供给开发人员和艺术家一种有目的性的,基于物理相关的一些属性,让他们可以调整和把玩它们。 有些材质的属性更容易用来表示它们的质金属质感强度指标,而其他的另一些属性则直接于定义了光是如何反射的,也很重要。如果你过去使用过Unity4,那么对于 Standard (Specular setup) 着色器应该看起来更熟悉。这个知识点会教你如何有效的使用 金属质感设置(metallic setup) 。有个重点需要各位注意,金属质感的工作流不仅仅用于金属材质;它是根据表面的金属质感或者非金属质感来定义材质的视觉效果的一种方式。尽管呈现的是两种不同的类型的着色器,但这 金属(Metallic ) 和 高光(Specular) 这两种方案通常来说是相等的表示。 就像Unity文档中所展示的:http://docs.unity3d.com/Manual/StandardShaderMetallicVsSpecular.html,这两种设置都可以创建同样的材质(如下图所示): ...

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

向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