模型挤压
重复是游戏当中最要命的一个问题之一。在游戏中创建新内容是一项费时的任务,当你要创建成千上万的敌人时,那么有很大的可能这些敌人有可能会看起来一样。这个时候利用着色器修改模型的基本几何图形,从而让模型产生不同的变化,可以说是一种 相对来说性能较好的方法。这个知识点中我们会给你演示一种叫做 法线挤压(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 我们添加了 自己的顶点函数。你可以查看 第六章,片元着色器和抓取通道 来学习怎么在顶点着色器和片元着色器中定义顶点修饰。法线挤压是改变模型的几何图形中最简单,也是最有效的技术之一。它是利用顶点对法线方向的投影来工作的。它是通过下面的着色器代码来实现的:
v.vertex.xyz += v.normal * _Amount;
顶点的位置会被一个朝向法线的 _Amount 单位长度的向量取代。如果 _Amount 的数值太大,呈现的效果会让人有点不舒服【脑袋特别大,而且很恐怖】。所以要通过一些比较小的合理的值,这样就可以让你的模型可以获得大量的不同的变体。
额外内容
如果你有多个敌人并且希望每个敌人都有不同的体重【也就是胖瘦不一样】,那么你就要为每个不同的敌人创建不同的材质。这是必要的操作,这是因为模型之间通常共用一个材质,为一个敌人改变了这个材质那么其他所有用了该材质的 的敌人也会跟着改变。这里有几种方法可以为你做这件事。下面的脚本,只要添加到有 Renderer 的游戏对象上,就会自动复制一份它的第一个材质并且自动设置好 _Amount 属性:using UnityEngine; public class NormalExtruder : MonoBehaviour { [Range(-0.0001f, 0.0001f)] public float amount = 0; // Use this for initialization void Start () { Material material = GetComponent<Renderer>().sharedMaterial; Material newMaterial = new Material(material); newMaterial.SetFloat("_Amount", amount); GetComponent<Renderer>().material = newMaterial; } }
添加挤压贴图
这个技术其实还可以进一步的提升。我们可以添加一个额外的纹理(或者使用主纹理的alpha通道)来表示挤压的程度。这样可以让我们对于哪一部分应该隆起或者凹陷有更好控制。下面的代码向你展示了如何获得这种效果:sampler2D _ExtrusionTex; void vert(inout appdata_full v) { float4 tex = tex2Dlod (_ExtrusionTex, float4(v.texcoord.xy,0,0)); float extrusion = tex.r * 2 - 1; v.vertex.xyz += v.normal * _Amount * extrusion; }
_ExtrusionTex 的红色通道被用来作为法线挤压相乘的系数。如果是0.5的话模型不会有什么影响;它的暗亮程度会被用于表示顶点的向内或向外挤压。有一点你需要注意的是,如果是用顶点修饰来对纹理采样,tex2Dlod 应该 用 tex2D 来替换。
注意
在着色器中,颜色通道的值的范围值 0 到 1,但有时候你又想用负数(比如你想用负数表示向内挤压)。那么针对这种情况,你可以用0.5表示0,比0.5小的看成负数,比0.5大的看成正数。其实在RGB编码的纹理中,就是用这 中方法来表示法线的。比如 UnpackNormal() 函数就被用来作为 (0, 1) 映射到 (-1, 1) 的映射函数。它其实等同于数学表达式 tex.r * 2 -1 。挤压贴图特别适合用来表现丧尸化的人物,方法就是通过扭曲人物的皮肤来突出皮肤下面的骨头的形状。下图向你展示了如何把一个健康的士兵,通过一个着色器和挤压贴图,变成一具毫无生气尸体的。对比与前面的几个例子,你可以注意到 那些衣服是如何不受影响的【就是挤压贴图里,对衣服进行挤压的数值是0.5】。下图所使用的着色器通过让挤压贴图的某些区域变得更黑,让士兵看起来更加消瘦:
希望它能帮到你或者帮你找到思路(^_^)选个表情,或者留个评论吧!