CG中的sampler2D对应unity什么unity 同类型组件

UnityShader2(14)
0.引入齐次坐标的意义:
a.为了将空间向量与空间点进行区分,所以将三元数扩展到四元数,第四元如果是1,则表示点;如果是0,则表示向量。
b.使用四元数后,就可以通过四阶矩阵运算,将空间点的平移、旋转和缩放等操作全部统一到矩阵的乘法运算中。
1.图形绘制管线的三个阶段:
a.应用程序阶段:将几何体数据(顶点坐标/颜色/法向量/纹理坐标、纹理等)通过数据总线传送到图形硬件。
b.几何阶段:主要负责顶点坐标变换、光照、裁剪、投影以及屏幕映射,该阶段基于GPU进行运算,该阶段的末端得到了经过变换和投影之后的顶点坐标、颜色、以及纹理坐标。
c.光栅化阶段:基于几何阶段的输出数据,为像素正确配色,以便绘制完整图像,该阶段进行的都是单个像素的操作,每个像素的信息存储在颜色缓冲器(color buffer或者frame buffer)中。
值得注意的是,光照计算属于几何阶段,因为光照计算涉及视点、光源和物体的世界坐标,所以通常放在世界坐标系中进行计算;而雾化以及涉及物体透明度的计算属于光栅化阶段,因为上述两种计算都需要深度值信息(Z值),而深度值是在几何阶段中计算,并传递到光栅阶段的。
2.几何阶段
a.几何阶段的主要工作是是“变换三维顶点坐标”和“光照计算”。(四个空间)
b.顶点法向量在模型文件中属于模型空间,在GPU的顶点程序中必须将法向量转换到世界空间中才能使用。
c.图元装配,即顶点根据连接关系,还原出网格结构。网格由顶点和索引组成,在之前的流水线中是对顶点的处理,在这个阶段是根据索引将顶点链接在一起,组成线、面单元。之后就是对超出屏幕外的三角形进行裁剪,想象一下:一个三角形其中一个顶点在画面外,另外两个顶点在画面内,这是我们在屏幕上看到的就是一个四边形。然后将该四边形切成两个小的三角形。
d.三角形处理,根据右手定则来决定三角面片的法向量,如果该法向量朝向视点(法向量与到视点的方向的点积为正),则该面是正面。一般是顶点按照逆时针排列。如果该面是反面,则进行背面去除操作( Back-face Culling)。在OpenGL中有专门的函数 enable 和 disable 背面去除操作。
3.光栅化阶段
a.经过几何阶段,已经可以得到每个点的屏幕坐标值,也知道我们需要绘制的图元(点、线、面),接下来就是将点对应到像素,再然后就是给像素赋予颜色值。
b.要赋予像素颜色,那么就要先计算每个像素的颜色值,其中包含的操作有:
b1.消除遮挡面
b2.纹理操作,即根据像素的纹理坐标,查询对应的纹理值
b3.Blending,混色
b4.Filtering,过滤,可以理解为,经过一种数学运算后变成新的颜色值
c.该阶段之后,像素的颜色值被写入帧缓冲中。
4.图像硬件
a.深度缓冲区(Z buffer),其中存放的是视点到每个像素所对应的空间点的距离衡量,称之为 Z 值或者深度值。可见物体的 Z 值范围位于【 0, 1】区间,默认情况下,最接近眼睛的顶点(近裁减面上)其 Z 值为 0.0,离眼睛最远的顶点(远裁减面上)其 Z值为 1.0。使用 z buffer 可以用来判断空间点的遮挡关系。
当 3D 图形处理器将基础图元(点、线、面)渲染到屏幕上时,需要以逐行扫描的方式进行光栅化。图元顶点位置信息是在应用程序中指定的(顶点模型坐标),然后通过一系列的过程变换到屏幕空间,但是图元内部点的屏幕坐标必须由已知的顶点信息插值而来。例如,当画三角形的一条扫描线时,扫描线上的每个像素的信息,是对扫描线左右端点处已知信息值进行插值运算得到的,所以内部点的 Z 值也是插值计算得到的。
b.帧缓冲区(Frame buffer),其中存放的是像素颜色值,也可以叫做颜色缓冲区。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1.数据类型
基本数据类型
a.float:32位浮点数据
b.half:16位浮点数据
c.fixed:12位定点数
d.int:32位整形数据
f.sampler*:纹理对象的句柄,分为6类:sampler, sampler1D, sampler2D, sampler3D, samplerCUBE和 samplerRECT。
向量类型,如float4,但最长不能超过4元,即可以声明float1,float2,float3,float4
向量初始化:float4 array = float4(1.0,2.0,3.0,4.0)
矩阵类型,不过最大的维数不能超过4*4阶,例如:
float1x1 matrix1;//等价于 float matirx1; x 是字符,并不是乘号!
float2x3 matrix2;// 表示 2*3 阶矩阵,包含 6 个 float 类型数据
float4x2 matrix3;// 表示 4*2 阶矩阵,包含 8 个 float 类型数据
float4x4 matrix4;//表示 4*4 阶矩阵,这是最大的维数
矩阵初始化:float2x3 matrix5 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
数组类型,在cg中,数组是一种数据结构,不是内置数据类型。数组数据类型在 Cg 程序中的作用是:作为函数的形参,用于大量数据的转递。
声明:float4 b[10];//声明了一个数组,包含 10 个 float4 类型向量数据
初始化:float a[4] = {1.0, 2.0, 3.0, 4.0}; //初始化一个数组
获取长度:int length = a.//获取数组长度
结构类型,结构体的声明以关键字 struct 开始,然后紧跟结构体的名字,接下来是一个大括号,并以分号结尾(不要忘了分号)。大括号中是结构体的定义,分为两大类:成员变量和成员函数。
struct myAdd
& & &float add(float x) &{return val +}
float a = s.
float b = s.add(a);
一般来说,Cg 的源代码都会在文件首部定义二个结构体,分别用于定义输人和输出的类型,这二个结构体定义与普通的 C 结构定义不同,除了定义结构体成员的数据类型外,还定义了该成员的绑定语义类型( Binding Semantics),所谓绑定语义类型是为了与宿主环境进行数据交换的时候识别不同数据类型的。目前Cg 支持的绑定语义类型包括 POSTION 位置), COLOR(颜色), NORMAL(法向量), Texcoord(纹理坐标)等类型。
当顶点着色程序向片段着色程序传递的数据类型较多的情况下, 使用结构体可以大大的方便代码的编写和维护。
float a = 1.0;
half b = 2.0;
float c = a+b; //等价于 float c = a + (float)b;
常量的类型后缀有 3 种:
x: 表示 fixed
2.表达式与控制语句
swizzle操作符
swizzle操作符后接x、 y、 z、 w,分别表示原始向量的第一个、第二个、第三个、第四个元素;&
swizzle操作符后接r、 g、 b和a的含义与前者等同。不过为了程序的易读性,建议对于表示颜色值的向量,使用swizzle操作符后接r、 g、 b和a的方式。
float4(a, b, c, d).xyz 等价于 float3(a, b, c)
float4(a, b, c, d).xyy 等价于 float3(a, b, b)
float4(a, b, c, d).wzyx 等价于 float4(d, c, b, a)
float4(a, b, c, d).w 等价于 float d
3.输入/输出和语义绑定
Cg 语言将输入数据流分为两类
1. Varying inputs,即数据流输入图元信息的各种组成要素。 从应用程序输入到 GPU 的数据除了顶点位置数据,还有顶点的法向量数据,纹理坐标数据等。 Cg 语言提供了一组语义词,用以表明参数是由顶点的哪些数据初始化的。
2. Uniform inputs,表示一些与三维渲染有关的离散信息数据,这些数据通常由应用程序传入,并通常不会随着图元信息的变化而变化,如材质对光的反射信息、运动矩阵等。 Uniform 修辞一个参数,表示该参数的值由外部应用程序初始化并传入;例如在参数列表中写:
uniform float brightness,uniform float4x4 modleWorldProject
表示从“外部”传入一个 float 类型数据,和一个 4 阶矩阵。“外部”的含义通常是用 OpenGL 或者 DirectX 所编写的应用程序。使用 Uniform 修辞的变量, 除了数据来源不同外, 与其他变量是完全一样的。需要注意的一点是: uniform 修辞的变量的值是从外部传入的,所以在 Cg 程序(顶点程序和片段程序)中通常使用 uniform 参数修辞函数形参,不容许声明一个用 uniform 修辞的局部变量!否则编译时会出现错误提示信息
in / out / inout(输入/输出修饰符,用来修饰形参。输入/输出修辞符通常和语义词一起使用,表示顶点着色程序和片段着色程序的输入输出)
in: 修辞一个形参只是用于输入,进入函数体时被初始化,且该形参值的改变不会影响实参值,这是典型的值传递方式。
out: 修辞一个形参只是用于输出的,进入函数体时并没有被初始化,这种类型的形参一般是一个函数的运行结果。
inout: 修辞一个形参既用于输入也用于输出,这是典型的引用传递。
void myFunction(out float x); //形参 x,只是用于输出
void myFunction(inout float x); //形参 x,即用于输入时初始化,也用于输出数据
void myFunction(in float x); //形参 x,只是用于输入
void myFunction(float x); /等价与 in float x,这种用法和 C\C++完全一致
语义词与语义绑定
语义词,表示输入图元的数据含义(是位置信息,还是法向量信息),也表明这些图元数据存放的硬件资源(寄存器或者纹理缓冲区)。顶点着色程序和片段着色程序中 Varying inputs 类型的输入,必须和一个语义词相绑定,这称之为绑定语义( binding semantics)。
由于 Cg 语言并不支持指针机制,且图形硬件处理过程中,数据通常暂存在寄存器中,故而在 Cg 语言中,通过引入语义绑定( binding semantics)机制,指定数据存放的位置,实际上就是将输入\输出数据和寄存器做一个映射关系(在 OpenGL Cg profiles 中是这样的,但在DirectX-based Cg profiles 中则并没有这种映射关系)。根据输入语义,图形处理器从某个寄存器取数据;然后再将处理好的数据,根据输出语义,放到指定的寄存器。
记住这一点:语义,是两个处理阶段(顶点程序、片段程序)之间的输入\输出数据和寄存器之间的桥梁,同时语义通常也表示数据的含义,如 POSITION,一般表示参数种存放的数据是顶点位置。
语义,分为输入语义和输入语义;输入语义和输出语义是有区别的。虽然一些参数经常会使用相同的绑定语义词, 例如: 顶点 Shader 的输入参数, POSITION指应用程序传入的顶点位置, 而输出参数使用 POSITION 语义就表示要反馈给硬件光栅器的裁剪空间位置,光栅器把 POSITION 当成一个位置信息。虽然两个语
义都命名为 POSITION,但却对应着图形流水线上不同的寄存器。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
a.顶点着色程序的输入语义:
语义词关键字:
POSITION & & & & & & & & & BLENDWEIGHT
NORMAL & & & & & & & & & & TANGENT
BINORMAL & & & & & & & & PSIZE
BLENDINDICES & & & TEXCOORD0---TEXCOORD7
语义词 POSITION0 等价于 POSITION, 其他的语义词也有类似的等价关系。
in float4 modelPos: POSITION & &表示该参数中的数据是顶点位置坐标(通常位于模型空间),属于输入参数,语义词 POSITION 是输入语义,如果在 OpenGL 中则对应为接受应用程序传递的顶点数据的寄存器(图形硬件上)。
in float4 modelNormal: NORMAL &表示该参数中的数据是顶点法向量坐标(通常位于模型空间),属于输入参数,语义词 NORMAL 是输入语义,如果在 OpenGL 中则对应为接受应用程序传递的顶点法向量的寄存器(图形硬件上)。
b.顶点着色程序的输出语义:
语义词关键字:
POSITION& & & & &&PSIZE,&
FOG & & & & & & & & & &COLOR0-COLOR1
TEXCOORD0-TEXCOORD7
顶点程序的输出数据被传入到片断程序中,所以顶点着色程序的输出语义词,通常也是片段程序的输入语义词,不过语义词POSITION除外。
顶点着色程序必须声明一个输出变量,并绑定POSITION语义词,该变量中的数据将被用于,且只被用于光栅化!&
为了保持顶点程序输出语义和片段程序输入语义的一致性, 通常使用相同的struct类型数据作为两者之间的传递,这是一种非常方便的写法,推荐使用。
struct VertexScreen
float4 oPosition : POSITION;
float4 objectPos : TEXCOORD0;
float4 objectNormal : TEXCOORD1;
注意:当使用struct结构中的成员变量绑定语义时,需要注意到顶点着色程序中使用的POSITION语义词,是不会被片段程序所使用的。
如果需要从顶点着色程序向片段程序传递数据,例如顶点投影坐标、光照信息等,则可以声明另外的参数,绑定到TEXCOORD系列的语义词进行数据传递,实际上TEXCOORD系列的语义词通常都被用于从顶点程序向片段程序之间传递数据
当然,你也可以选择不使用struct结构,而直接在函数形参中进行语义绑定。无论使用何种方式,都要记住vertex program中的绑定语义( POSITION除外)的
输出形参中的数据会传递到fragment program中绑定相同语义的输入形参中。
c.片段着色程序的输出语义:
片段着色程序的输出语义词较少,通常是COLOR。这是因为片段着色程序运行完毕后,就基本到了GPU流水线的末端了。 片段程序必须声明一个out向量(三元或四元),绑定语义词COLOR,这个值将被用作该片断的最终颜色值。
语义绑定方法:
a.绑定语义放在函数的参数列表的参数声明后面中
void mian_v(float4 position_obj : POSITION,float3 normal_obj : NORMAL,out float4 oPosition : POSITION,out float4 oColor : COLOR,uniform float4x4 modelViewProj){………………}
b.绑定语义可以放在结构体( struct)的成员变量后面
struct C2E1v_Output {float4 position : POSITION;float3 color : COLOR;};
c.绑定语义词可以放在函数声明的后面
float4 main_v(float4 position: POSITION,out float4 oposition : POSITION,uniform float4x4 modelViewProj):COLOR
{oposition = mul(modelViewProj,position);
float4 ocolor = float4(1.0,0,0,0);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:526512次
积分:6558
积分:6558
排名:第3906名
原创:134篇
转载:88篇
评论:193条
(2)(8)(16)(1)(6)(10)(20)(1)(1)(3)(19)(22)(5)(1)(5)(13)(32)(32)(26)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'Cg入门22:Fragment shader - 2D纹理采样(光照贴图制作和使用)
一、光照贴图制作:
实时光照效果:
使用光照贴图步骤:
1.选择要灯光贴图的模型,然后设置为Lightmap Static
2.光源Baking设置为Baked
3.选择Windows/Lighting,打开Lighting界面,
1》选择Scene 选项卡,设置Ambient Gi为Baked
2》选择LightMaps 选项卡,然后按Build,生成光照贴图
数秒后LightMaps界面会显示光照贴图,效果如下:
3》关掉场景中的Light
(使用光照贴图后的效果:平台差异导致的)
移动物体后效果
二、光照贴图的Shader使用:
uv:纹理贴图坐标
unity_LightmapST:纹理坐标缩放偏移向量
v.texcoord:就是uv坐标
模型展开的UV坐标信息,包含在模型网格中了。
// Upgrade NOTE: commented out 'float4 unity_LightmapST', a built-in variable
// Upgrade NOTE: commented out 'sampler2D unity_Lightmap', a built-in variable
Shader &Sbin/TexShader2&
Properties
_MainTex (&Texture&, 2D) = &white& {}
#pragma vertex vert
#pragma fragment frag
#include &UnityCG.cginc&
sampler2D _MainT
float4 _MainTex_ST;//纹理缩放偏移向量(Unity默认此变量赋值,变量名规则:纹理名_ST)
// sampler2D unity_L//若开启光照贴图,默认填值
// float4 unity_LightmapST;//与上unity_Lightmap同理
struct v2f{
float4 pos:POSITION;
float2 uv:TEXCOORD0;
float2 uv2:TEXCOORD1;
v2f vert (appdata_full v)
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//第一种方式:
//o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.
//第二种方式:内建宏,双方和第一种一样,只是对第一种计算的封装
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
o.uv2 = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.
fixed4 frag (v2f v) : COLOR
//解密光照贴图计算公式
float3 lightmapColor = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap,v.uv2));
fixed4 col = tex2D(_MainTex, v.uv);//第一个参数:纹理,第二个参数UV向量
col.rgb *= lightmapC&img src=&/50/v2-b7cacf322c7e42e844cad7_b.png& data-rawwidth=&1909& data-rawheight=&844& class=&origin_image zh-lightbox-thumb& width=&1909& data-original=&/50/v2-b7cacf322c7e42e844cad7_r.png&&&h2&简介&/h2&&p&《塞尔达传说
风之杖》是一款由任天堂开发的任天堂GameCube平台上的动作冒险游戏,一经发布就广受好评,之后任天堂在WiiU上还发布了HD版本。其别具一格的卡通美术风格现如今看来都不算过时,甚至给最近火的一塌糊涂的《塞尔达传说
荒野之息》带来了很大的影响。&/p&&img src=&/v2-4b7c06cc155af5b9ff109e8_b.png& data-rawwidth=&1277& data-rawheight=&670& class=&origin_image zh-lightbox-thumb& width=&1277& data-original=&/v2-4b7c06cc155af5b9ff109e8_r.png&&&img src=&/v2-0e6fdb30a8fc_b.png& data-rawwidth=&1204& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1204& data-original=&/v2-0e6fdb30a8fc_r.png&&&p&虽说gamebox上的版本马赛克比较重,但是link的表情表现的非常的到位,从简单的眨眼,到各种神情的变化,都特别的到位,比如下面这个他被用木桶投掷到岛上的过场&/p&&img src=&/v2-b4d5169e29bae1b70e2fe60af44c500a_b.png& data-rawwidth=&653& data-rawheight=&308& class=&origin_image zh-lightbox-thumb& width=&653& data-original=&/v2-b4d5169e29bae1b70e2fe60af44c500a_r.png&&&img src=&/v2-2bb1551cfc078b4e44cded1_b.png& data-rawwidth=&626& data-rawheight=&302& class=&origin_image zh-lightbox-thumb& width=&626& data-original=&/v2-2bb1551cfc078b4e44cded1_r.png&&&img src=&/v2-c532e44dc6be51d9e25e0aca_b.png& data-rawwidth=&669& data-rawheight=&327& class=&origin_image zh-lightbox-thumb& width=&669& data-original=&/v2-c532e44dc6be51d9e25e0aca_r.png&&&p&非常带感。&br&&/p&&br&&p&今天要分析的是游戏中的角色渲染和表情系统,并且在Unity中进行实现。&/p&&p&&b&工具&/b&&/p&&p&Unity3D 5.3.5&/p&&p&Maya 2014&/p&&h2&模型处理&/h2&&p&原始的模型可以从&a href=&/?target=https%3A//www./submitter/Mystie/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&i class=&icon-external&&&/i&&/a&下载到。最原始的模型是obj的模型,要放到Unity里的话,首先要将其转化成fbx。&/p&&p&打开Maya,选择导入模型&/p&&img src=&/v2-8b98acd2fefde_b.png& data-rawwidth=&670& data-rawheight=&631& class=&origin_image zh-lightbox-thumb& width=&670& data-original=&/v2-8b98acd2fefde_r.png&&&p&导入之后的mesh是一个完整的mesh,先把它打撒。选中模型,然后
网格-&分离,Mesh就被打散了&/p&&img src=&/v2-845d020bc9c43aa1d38f23af879f0356_b.png& data-rawwidth=&306& data-rawheight=&140& class=&content_image& width=&306&&&br&&p&看下面数,不到3000&/p&&img src=&/v2-607ecdf00c780c9d7a28ca4_b.png& data-rawwidth=&273& data-rawheight=&114& class=&content_image& width=&273&&&br&&p&衣服的贴图尺寸180*96,怎么”挤” 进去的?&/p&&img src=&/v2-76eedf0ccceaf6d30cdcb93558befacc_b.png& data-rawwidth=&726& data-rawheight=&270& class=&origin_image zh-lightbox-thumb& width=&726& data-original=&/v2-76eedf0ccceaf6d30cdcb93558befacc_r.png&&&br&&p&由于衣服大部分的地方都是大块的纯色,不同于常规的展UV的方法,纹理也是一些很简单的颜色,uv就直接被放到对应的色块上。&/p&&p&在看最重要的面部。&/p&&img src=&/v2-d641b1baec1ae2b1e63e1c0dedf52890_b.png& data-rawwidth=&658& data-rawheight=&203& class=&origin_image zh-lightbox-thumb& width=&658& data-original=&/v2-d641b1baec1ae2b1e63e1c0dedf52890_r.png&&&p&嘴巴部分是专门用了一块mesh来处理,这样只要在材质中切换贴图就能够达到换表情的作用。&/p&&p&嘴巴部分由于是切割的mesh,所以在导入的时候生成法线会出问题&/p&&img src=&/v2-f8d76e50684cdd66712c82_b.png& data-rawwidth=&787& data-rawheight=&388& class=&origin_image zh-lightbox-thumb& width=&787& data-original=&/v2-f8d76e50684cdd66712c82_r.png&&&br&&p&嘴巴部分的的法线和面部的法线不是连续的,这样会导致后面处理光照的时候会很奇怪。&/p&&p&处理方法是用Maya的法线工具&/p&&img src=&/v2-3e3f4da7ec46f_b.png& data-rawwidth=&687& data-rawheight=&431& class=&origin_image zh-lightbox-thumb& width=&687& data-original=&/v2-3e3f4da7ec46f_r.png&&&br&&p&会生成一个合并好的mesh,&/p&&img src=&/v2-e7bbe278cc3d0f76ed00ff8b3ea431c0_b.png& data-rawwidth=&658& data-rawheight=&209& class=&origin_image zh-lightbox-thumb& width=&658& data-original=&/v2-e7bbe278cc3d0f76ed00ff8b3ea431c0_r.png&&&br&&p&这下正常了。再看眼睛,眼睛的表现分为三个部分。眉毛,眼白,眼睛。这里的处理方法不是用面部的网格切一块下来了,而是在眼睛出罩一些片mesh&/p&&img src=&/v2-9ceb3ff7a64e59da94c877_b.png& data-rawwidth=&427& data-rawheight=&287& class=&origin_image zh-lightbox-thumb& width=&427& data-original=&/v2-9ceb3ff7a64e59da94c877_r.png&&&p&每一边的眼睛都覆盖了三个mesh片,分别是眉毛,眼白,瞳孔(为了看清楚将它们拉开了一些)。&/p&&p&实际上是往外拉了一点点,防止Z-Fighting&/p&&img src=&/v2-dbc643eae2cb_b.png& data-rawwidth=&737& data-rawheight=&307& class=&origin_image zh-lightbox-thumb& width=&737& data-original=&/v2-dbc643eae2cb_r.png&&&br&&p&接下来可以导出模型了,文件-&导出,选fbx,默认设置。&/p&&br&&h2&角色渲染&/h2&&p&角色渲染的话就是最简单的卡通渲染,根据direction light的方向和法线的角度得到一个shadow值,和纹理的颜色相乘。&/p&&br&&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&n&&Shader&/span& &span class=&s&&&Custom/celshading&&/span&
&span class=&p&&{&/span&
&span class=&n&&Properties&/span&
&span class=&p&&{&/span&
&span class=&p&&[&/span&&span class=&n&&NoScaleOffset&/span&&span class=&p&&]&/span& &span class=&n&&_MainTex&/span&&span class=&p&&(&/span&&span class=&s&&&Texture&&/span&&span class=&p&&,&/span& &span class=&mi&&2&/span&&span class=&n&&D&/span&&span class=&p&&)&/span& &span class=&o&&=&/span& &span class=&s&&&white&&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&n&&SubShader&/span&
&span class=&p&&{&/span&
&span class=&n&&Pass&/span&
&span class=&p&&{&/span&
&span class=&n&&Tags&/span&&span class=&p&&{&/span& &span class=&s&&&LightMode&&/span& &span class=&o&&=&/span& &span class=&s&&&ForwardBase&&/span& &span class=&p&&}&/span&
&span class=&n&&CGPROGRAM&/span&
&span class=&cp&&#pragma vertex vert&/span&
&span class=&cp&&#pragma fragment frag&/span&
&span class=&cp&&#include&/span& &span class=&cpf&&&UnityCG.cginc&&/span&&span class=&cp&&&/span&
&span class=&k&&struct&/span& &span class=&n&&v2f&/span&
&span class=&p&&{&/span&
&span class=&n&&float2&/span& &span class=&nl&&uv&/span& &span class=&p&&:&/span& &span class=&n&&TEXCOORD0&/span&&span class=&p&&;&/span& &span class=&c1&&// texture coordinate&/span&
&span class=&n&&float4&/span& &span class=&nl&&vertex&/span& &span class=&p&&:&/span& &span class=&n&&SV_POSITION&/span&&span class=&p&&;&/span& &span class=&c1&&// clip space position&/span&
&span class=&n&&half3&/span& &span class=&nl&&worldNormal&/span& &span class=&p&&:&/span& &span class=&n&&TEXCOORD1&/span&&span class=&p&&;&/span&
&span class=&p&&};&/span&
&span class=&c1&&// vertex shader&/span&
&span class=&n&&v2f&/span& &span class=&nf&&vert&/span&&span class=&p&&(&/span&&span class=&n&&appdata_base&/span& &span class=&n&&v&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&n&&v2f&/span& &span class=&n&&o&/span&&span class=&p&&;&/span&
&span class=&c1&&// transform position to clip space&/span&
&span class=&c1&&// (multiply with model*view*projection matrix)&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&vertex&/span& &span class=&o&&=&/span& &span class=&n&&mul&/span&&span class=&p&&(&/span&&span class=&n&&UNITY_MATRIX_MVP&/span&&span class=&p&&,&/span& &span class=&n&&v&/span&&span class=&p&&.&/span&&span class=&n&&vertex&/span&&span class=&p&&);&/span&
&span class=&c1&&// just pass the texture coordinate&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span& &span class=&o&&=&/span& &span class=&n&&v&/span&&span class=&p&&.&/span&&span class=&n&&texcoord&/span&&span class=&p&&;&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&worldNormal&/span& &span class=&o&&=&/span& &span class=&n&&UnityObjectToWorldNormal&/span&&span class=&p&&(&/span&&span class=&n&&v&/span&&span class=&p&&.&/span&&span class=&n&&normal&/span&&span class=&p&&);&/span&
&span class=&k&&return&/span& &span class=&n&&o&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&c1&&// texture we will sample&/span&
&span class=&n&&sampler2D&/span& &span class=&n&&_MainTex&/span&&span class=&p&&;&/span&
&span class=&c1&&// returns low precision (&fixed4& type)&/span&
&span class=&n&&fixed4&/span& &span class=&nf&&frag&/span&&span class=&p&&(&/span&&span class=&n&&v2f&/span& &span class=&n&&i&/span&&span class=&p&&)&/span& &span class=&o&&:&/span& &span class=&n&&SV_Target&/span&
&span class=&p&&{&/span&
&span class=&c1&&// sample texture and return it&/span&
&span class=&n&&fixed4&/span& &span class=&n&&col&/span& &span class=&o&&=&/span& &span class=&n&&tex2D&/span&&span class=&p&&(&/span&&span class=&n&&_MainTex&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span&&span class=&p&&);&/span&
&span class=&kt&&float&/span& &span class=&n&&shadow&/span& &span class=&o&&=&/span& &span class=&n&&dot&/span&&span class=&p&&(&/span&&span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&worldNormal&/span&&span class=&p&&,&/span& &span class=&n&&normalize&/span&&span class=&p&&(&/span&&span class=&n&&_WorldSpaceLightPos0&/span&&span class=&p&&.&/span&&span class=&n&&xyz&/span&&span class=&p&&));&/span&
&span class=&n&&col&/span& &span class=&o&&*=&/span& &span class=&n&&smoothstep&/span&&span class=&p&&(&/span&&span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.1&/span&&span class=&p&&,&/span& &span class=&n&&shadow&/span&&span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&mf&&0.4&/span& &span class=&o&&+&/span& &span class=&mf&&0.6&/span&&span class=&p&&;&/span&
&span class=&c1&&//fixed4 col = 0;&/span&
&span class=&c1&&//col.rgb = i.worldNormal*0.5 + 0.5;&/span&
&span class=&c1&&//col.rgb = _WorldSpaceLightPos0.&/span&
&span class=&k&&return&/span& &span class=&n&&col&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&n&&ENDCG&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&&p&不加光照&/p&&img src=&/v2-45ac68aa62ad449dad4fc03_b.png& data-rawwidth=&842& data-rawheight=&385& class=&origin_image zh-lightbox-thumb& width=&842& data-original=&/v2-45ac68aa62ad449dad4fc03_r.png&&&p&加上之后得到结果&/p&&img src=&/v2-54a0d8dcf5dd0fdee0310dbf_b.png& data-rawwidth=&854& data-rawheight=&379& class=&origin_image zh-lightbox-thumb& width=&854& data-original=&/v2-54a0d8dcf5dd0fdee0310dbf_r.png&&&img src=&/v2-37cecacff0fae_b.png& data-rawwidth=&903& data-rawheight=&367& class=&origin_image zh-lightbox-thumb& width=&903& data-original=&/v2-37cecacff0fae_r.png&&&br&&p&用到了一个内置函数&/p&&p&smoothstep
- interpolate smoothly between two input
values based on a third&/p&&p&目的是为了让明暗交接的地方不要太锋利,过度的平滑一些。&/p&&p& 加上阴影&/p&&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&c1&&// shadow caster rendering pass, implemented manually&/span&
&span class=&c1&&// using macros from UnityCG.cginc&/span&
&span class=&n&&Pass&/span&
&span class=&p&&{&/span&
&span class=&n&&Tags&/span&&span class=&p&&{&/span& &span class=&s&&&LightMode&&/span& &span class=&o&&=&/span& &span class=&s&&&ShadowCaster&&/span& &span class=&p&&}&/span&
&span class=&n&&CGPROGRAM&/span&
&span class=&cp&&#pragma vertex vert&/span&
&span class=&cp&&#pragma fragment frag&/span&
&span class=&cp&&#pragma multi_compile_shadowcaster&/span&
&span class=&cp&&#include&/span& &span class=&cpf&&&UnityCG.cginc&&/span&&span class=&cp&&&/span&
&span class=&k&&struct&/span& &span class=&n&&v2f&/span& &span class=&p&&{&/span&
&span class=&n&&V2F_SHADOW_CASTER&/span&&span class=&p&&;&/span&
&span class=&p&&};&/span&
&span class=&n&&v2f&/span& &span class=&nf&&vert&/span&&span class=&p&&(&/span&&span class=&n&&appdata_base&/span& &span class=&n&&v&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&n&&v2f&/span& &span class=&n&&o&/span&&span class=&p&&;&/span&
&span class=&n&&TRANSFER_SHADOW_CASTER_NORMALOFFSET&/span&&span class=&p&&(&/span&&span class=&n&&o&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&o&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&n&&float4&/span& &span class=&nf&&frag&/span&&span class=&p&&(&/span&&span class=&n&&v2f&/span& &span class=&n&&i&/span&&span class=&p&&)&/span& &span class=&o&&:&/span& &span class=&n&&SV_Target&/span&
&span class=&p&&{&/span&
&span class=&n&&SHADOW_CASTER_FRAGMENT&/span&&span class=&p&&(&/span&&span class=&n&&i&/span&&span class=&p&&)&/span&
&span class=&p&&}&/span&
&span class=&n&&ENDCG&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&&img src=&/v2-8b6d2f9ac5bcf33ed0d64e_b.png& data-rawwidth=&919& data-rawheight=&454& class=&origin_image zh-lightbox-thumb& width=&919& data-original=&/v2-8b6d2f9ac5bcf33ed0d64e_r.png&&&br&&h2&面部表情&/h2&&p&嘴巴部分最简单,用的和身体一样的shader,要变换嘴型的时候,只需要切换材质的贴图。&/p&&img src=&/v2-7f11aade073eba6b35f194_b.png& data-rawwidth=&450& data-rawheight=&165& class=&origin_image zh-lightbox-thumb& width=&450& data-original=&/v2-7f11aade073eba6b35f194_r.png&&&img src=&/v2-4aa22f4b7ea7dfb1a17a925_b.png& data-rawwidth=&490& data-rawheight=&128& class=&origin_image zh-lightbox-thumb& width=&490& data-original=&/v2-4aa22f4b7ea7dfb1a17a925_r.png&&&img src=&/v2-4db3db0d3e12c4c9d6a0_b.png& data-rawwidth=&477& data-rawheight=&127& class=&origin_image zh-lightbox-thumb& width=&477& data-original=&/v2-4db3db0d3e12c4c9d6a0_r.png&&&br&&p&眼睛部分会比较复杂。眼睛和眉毛的shader是一样的,就是基本的transparent&/p&&img src=&/v2-e1adcb5d5_b.png& data-rawwidth=&502& data-rawheight=&122& class=&origin_image zh-lightbox-thumb& width=&502& data-original=&/v2-e1adcb5d5_r.png&&&br&&p&记住左右眉毛和左右眼睛得用不同的材质,因为不同的表情左右可能是不对称的。&/p&&p&眉毛和眼睛正常了,但是眼珠不正常,这里需要在shader中给眼珠加一个mask,将当前眼白的部分叠加到眼珠上得到最终的结果。&/p&&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&n&&fixed4&/span& &span class=&nf&&frag&/span&&span class=&p&&(&/span&&span class=&n&&v2f&/span& &span class=&n&&i&/span&&span class=&p&&)&/span& &span class=&o&&:&/span& &span class=&n&&SV_Target&/span&
&span class=&p&&{&/span&
&span class=&c1&&// sample texture and return it&/span&
&span class=&n&&fixed4&/span& &span class=&n&&col&/span& &span class=&o&&=&/span& &span class=&n&&tex2D&/span&&span class=&p&&(&/span&&span class=&n&&_PupilTex&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span&&span class=&p&&);&/span&
&span class=&n&&fixed4&/span& &span class=&n&&mask&/span& &span class=&o&&=&/span& &span class=&n&&tex2D&/span&&span class=&p&&(&/span&&span class=&n&&_EyeMaskTex&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span&&span class=&p&&);&/span&
&span class=&n&&col&/span& &span class=&o&&*=&/span& &span class=&n&&mask&/span&&span class=&p&&;&/span&
&span class=&k&&return&/span& &span class=&n&&col&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&&img src=&/v2-a1dc8c8e8f7596eba7bfe6b45c3813bb_b.png& data-rawwidth=&562& data-rawheight=&164& class=&origin_image zh-lightbox-thumb& width=&562& data-original=&/v2-a1dc8c8e8f7596eba7bfe6b45c3813bb_r.png&&&br&&p&需要注意的是眼珠的EyeMaskTex 需要和眼白的maintex要同步好,不然就会很囧。&/p&&img src=&/v2-efb45b9bc764a90e45dad1b968b43a82_b.png& data-rawwidth=&641& data-rawheight=&223& class=&origin_image zh-lightbox-thumb& width=&641& data-original=&/v2-efb45b9bc764a90e45dad1b968b43a82_r.png&&&br&&p&&b&瞳孔的放大缩小&/b&&/p&&p&Link眼睛的瞳孔的位置也是可以控制的,用来表现看的方向&/p&&img src=&/v2-46bfba2801efe9b0c825c66_b.png& data-rawwidth=&460& data-rawheight=&223& class=&origin_image zh-lightbox-thumb& width=&460& data-original=&/v2-46bfba2801efe9b0c825c66_r.png&&&br&&p&同时也是可以放大缩小的,比如说上面的用来表示惊恐的表情&/p&&p&瞳孔的位置处理直接叠加上一个uv偏移就可以了。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&_XOffset(&X Offset&, float) = 0
_YOffset(&Y Offset&, float) = 0
float2 finalUv = i.uv + float2(_XOffset, _YOffset)
fixed4 col = tex2D(_PupilTex, finalUv);
&/code&&/pre&&/div&&br&&p&调一个结果&/p&&img src=&/v2-7cf32a16aba5c9bd9b765fd_b.png& data-rawwidth=&721& data-rawheight=&278& class=&origin_image zh-lightbox-thumb& width=&721& data-original=&/v2-7cf32a16aba5c9bd9b765fd_r.png&&&br&&p&惊悚的表情需要进行UV缩放,注意不是将uv直接乘以scale值,这样会从左下角为中心缩放,我们的目的是从贴图的中心开始缩放,需要一个简单的换算&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&float2 scaleCenter = float2(0.5f, 0.5f);
float2 finalUv = (i.uv + float2(_XOffset, _YOffset) - scaleCenter) * _Scale + scaleC
fixed4 col = tex2D(_PupilTex, finalUv);
&/code&&/pre&&/div&&p&调个简单的结果&/p&&img src=&/v2-d0f0d838bf6de4db02e05f2_b.png& data-rawwidth=&737& data-rawheight=&213& class=&origin_image zh-lightbox-thumb& width=&737& data-original=&/v2-d0f0d838bf6de4db02e05f2_r.png&&&p&还有一个点要注意,瞳孔的纹理的wrap mode 要设置为&/p&&img src=&/v2-d53adf950b3e7e414b63a2ec9d2397b7_b.png& data-rawwidth=&354& data-rawheight=&81& class=&content_image& width=&354&&&br&&p&不然会出现…&/p&&img src=&/v2-66fae26a10fb9b6e578d6d212ecfa78f_b.png& data-rawwidth=&437& data-rawheight=&185& class=&origin_image zh-lightbox-thumb& width=&437& data-original=&/v2-66fae26a10fb9b6e578d6d212ecfa78f_r.png&&&br&&p&&b&头发不会挡住眼睛&/b&&/p&&p&游戏中是这样的就像下面这样&br&&/p&&img src=&/v2-6a3ebec7ef689b443d3df_b.png& data-rawwidth=&756& data-rawheight=&337& class=&origin_image zh-lightbox-thumb& width=&756& data-original=&/v2-6a3ebec7ef689b443d3df_r.png&&&br&&p&虽然有时候看起来有点奇怪&/p&&img src=&/v2-d5ce00e8e4ef2f1f0fa89d4_b.png& data-rawwidth=&963& data-rawheight=&341& class=&origin_image zh-lightbox-thumb& width=&963& data-original=&/v2-d5ce00e8e4ef2f1f0fa89d4_r.png&&&p&但是整体表现还是挺好的。&/p&&p&这里使用的方法是利用Stencil buffer来处理,首先标记出被遮挡的部分,然后清掉那一部分的zbuffer,最后绘制出来,下面是具体的实现。&/p&&p&首先单独给头发一个shader,用来写stencil&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Stencil
Comp always
Pass replace
&/code&&/pre&&/div&&p&其余部分和身体的shader一样,头发绘制过的地方Stencil都变成了1。&/p&&p&眼睛分为三个pass绘制,&/p&&p&第一个pass标记出stencil为1,且ztest fail的部位&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Stencil
Comp Equal
Pass replace
ZFail IncrSat
&/code&&/pre&&/div&&p&ZFail
IncrSat表示通过stencil test,Ztest失败之后,将stencil的值+1.所以被头发遮挡住的地方stencil变成了2.下面蓝色部分就是&/p&&img src=&/v2-45acc372baf6de322fefb22_b.png& data-rawwidth=&442& data-rawheight=&192& class=&origin_image zh-lightbox-thumb& width=&442& data-original=&/v2-45acc372baf6de322fefb22_r.png&&&br&&p&第二个pass,清掉标记区的Zbuffer&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&ZTest Greater
Comp Equal
&/code&&/pre&&/div&&br&&p&第三个pass,就是最简单的transparent&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Pass
Tags{ &IgnoreProjector& = &True& &RenderType& = &Transparent& }
// use &vert& function as the vertex shader
#pragma vertex vert
// use &frag& function as the pixel (fragment) shader
#pragma fragment frag
#include &UnityCG.cginc&
// vertex shader outputs (&vertex to fragment&)
struct v2f
float2 uv : TEXCOORD0; // texture coordinate
float4 vertex : SV_POSITION; // clip space position
// vertex shader
v2f vert(appdata_base v)
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
// texture we will sample
sampler2D _MainT
fixed4 frag(v2f i) : SV_Target
fixed4 col = tex2D(_MainTex, i.uv);
&/code&&/pre&&/div&&br&&p&最终结果&/p&&img src=&/v2-a64bb1b988a331c260d59f_b.png& data-rawwidth=&695& data-rawheight=&290& class=&origin_image zh-lightbox-thumb& width=&695& data-original=&/v2-a64bb1b988a331c260d59f_r.png&&&img src=&/v2-553fe26df467bbe50e7757cbd1751e33_b.png& data-rawwidth=&832& data-rawheight=&330& class=&origin_image zh-lightbox-thumb& width=&832& data-original=&/v2-553fe26df467bbe50e7757cbd1751e33_r.png&&&br&&p&最后来摆几个Pose&/p&&br&&img src=&/v2-751f72b9c0de946f05da21_b.png& data-rawwidth=&778& data-rawheight=&320& class=&origin_image zh-lightbox-thumb& width=&778& data-original=&/v2-751f72b9c0de946f05da21_r.png&&&br&&img src=&/v2-90c0b1fa4f_b.png& data-rawwidth=&801& data-rawheight=&339& class=&origin_image zh-lightbox-thumb& width=&801& data-original=&/v2-90c0b1fa4f_r.png&&&br&&img src=&/v2-20e58c2e68fcbf0051a4_b.png& data-rawwidth=&1092& data-rawheight=&533& class=&origin_image zh-lightbox-thumb& width=&1092& data-original=&/v2-20e58c2e68fcbf0051a4_r.png&&&br&&h2&小结&/h2&&p&相对于UnityChan中完全用骨骼驱动的表情系统(王者荣耀里面角色展示的部分似乎也是这样处理的)&/p&&img src=&/v2-280b79c0ebea6aaf69e9fe1_b.png& data-rawwidth=&621& data-rawheight=&319& class=&origin_image zh-lightbox-thumb& width=&621& data-original=&/v2-280b79c0ebea6aaf69e9fe1_r.png&&&br&&br&&p&这套表情系统的特点是&/p&&p&非常节省资源,几乎没有什么消耗(也许贴图还可以再pack一下?)。&/p&&p&和美术风格非常的搭。&/p&&p&面部的所有细节都不会糊掉。&/p&&p&无法使用macanim动画系统,需要自己写一套控制角色表情的脚本。&/p&&p&对于在移动平台上想自己实现一个的表情系统的卡通风格项目可以考虑用这种方案。&/p&&h2&参考&/h2&&p&&a href=&/?target=http%3A//www.benjones.us/twilight-princess-eyes-breakdown/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Twilight Princess Eyes Breakdown&i class=&icon-external&&&/i&&/a&&br&&/p&&p&&a href=&/?target=http%3A///2013/07/animated-facial-textures-in-maya-and_26.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&&/span&&span class=&invisible&&/2013/07/animated-facial-textures-in-maya-and_26.html&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&br&&/p&&p&&a href=&/?target=https%3A///%40gordonnl/links-expressions-eb7beae2c62c& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/@gordonnl/li&/span&&span class=&invisible&&nks-expressions-eb7beae2c62c&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&br&&/p&&p&&a href=&/?target=http%3A///bitzhuwei/p/explain-offset-and-tiling-with-Unity-and-3ds.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&图文详解Unity3D中Material的Tiling和Offset是怎么回事 - BIT祝威 - 博客园&i class=&icon-external&&&/i&&/a&&/p&&p&&a href=&/?target=http%3A///blog/puppet_master/article/p-6362899.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Unity Shader-渲染队列,ZTest,ZWrite,Early-Z&i class=&icon-external&&&/i&&/a&&/p&&p&&a href=&/?target=http%3A//blog.csdn.net/silangquan/article/details/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Unity3D游戏开发从零单排(十) - 进击的Shader续&i class=&icon-external&&&/i&&/a&&/p&
简介《塞尔达传说
风之杖》是一款由任天堂开发的任天堂GameCube平台上的动作冒险游戏,一经发布就广受好评,之后任天堂在WiiU上还发布了HD版本。其别具一格的卡通美术风格现如今看来都不算过时,甚至给最近火的一塌糊涂的《塞尔达传说
荒野之息》带来了很大…
&p&上图是最有用的,这里随便往场景里放了几个模型,这是最初始的效果。&/p&&img src=&/v2-e678dcabcaaa5_b.png& data-rawwidth=&1056& data-rawheight=&595& class=&origin_image zh-lightbox-thumb& width=&1056& data-original=&/v2-e678dcabcaaa5_r.png&&&p&&b&添加环境光&/b&&/p&&p&打开 Windows-&Lighting,在Environment Lighting 里设置Skybox为Default-skybox,unity5应该是默认设好了的,设置之后效果如下:&/p&&img src=&/v2-e05f5dd807d549a229087fce5f0660d1_b.jpg& data-rawwidth=&1049& data-rawheight=&620& class=&origin_image zh-lightbox-thumb& width=&1049& data-original=&/v2-e05f5dd807d549a229087fce5f0660d1_r.jpg&&&p&&b&添加SSAO特效&/b&&/p&&p&Unity自带了一个SSAO特效,导入特效:Assets-&Import Package-&Effects。导入了之后,在相机上添加脚本Screen Space Ambient Occlusion。我这里故意把效果弄得比较强烈,可以看到物体的底边出现了一些黑色的阴影。&/p&&img src=&/v2-ab58af6cdde6cd54be0d51_b.jpg& data-rawwidth=&1051& data-rawheight=&637& class=&origin_image zh-lightbox-thumb& width=&1051& data-original=&/v2-ab58af6cdde6cd54be0d51_r.jpg&&&p&&b&添加软阴影&/b&&/p&&p&如果对上述特效不满意的可以继续用Unity自带的烘培系统进行场景的烘培。首先把所有参与烘培渲染的物品设置为Static(至少是Lighting的Static)。&/p&&img src=&/v2-418fba15e2fef16_b.jpg& data-rawwidth=&812& data-rawheight=&278& class=&origin_image zh-lightbox-thumb& width=&812& data-original=&/v2-418fba15e2fef16_r.jpg&&&p&为所有参与烘培渲染的模型添加Lightmap UV。这里我给狼的模型添加了Lightmap UV。&/p&&img src=&/v2-e44c73f44e98f956fd25fc7_b.jpg& data-rawwidth=&416& data-rawheight=&990& class=&content_image& width=&416&&&p&同时我把主光源(一个Directional Light)设置为Baked(也可以设为Mixed),让灯光使用烘培效果。然后把Shadow Type设置为Soft Shadow,并调节Baked Shadow Angle,让灯光投射出软阴影。&/p&&p&在Windows-&Lighting的窗口中,打开Baked GI选项,其中的各种系数这里就不详细解释了,可以参考官网文档。&/p&&img src=&/v2-cdfee47fabef6_b.jpg& data-rawwidth=&811& data-rawheight=&971& class=&origin_image zh-lightbox-thumb& width=&811& data-original=&/v2-cdfee47fabef6_r.jpg&&&p&设置完毕后,点击Lighting最底下的Build,进行灯光的烘培。这时候会右下方会有个进度条,提示烘培的进度。进度条读完(通常需要几分钟)之后,可以看到场景已经出现软阴影了。&/p&&img src=&/v2-ee4af97d_b.jpg& data-rawwidth=&1025& data-rawheight=&591& class=&origin_image zh-lightbox-thumb& width=&1025& data-original=&/v2-ee4af97d_r.jpg&&&p&用三点打光法做出更加柔和的灯光。&/p&&img src=&/v2-98da65af62aa9bdca83c_b.jpg& data-rawwidth=&1056& data-rawheight=&589& class=&origin_image zh-lightbox-thumb& width=&1056& data-original=&/v2-98da65af62aa9bdca83c_r.jpg&&&p&&b&阴影分辨率&/b&&/p&&p&顺便提一下,实时渲染(非烘焙)的阴影有时候会出现分辨率太低的问题。这可以把Edit-&Project Settings-&Quality 窗口中的Shadow Distance 来进行调节(调低能够提高分辨率,但是会减少阴影渲染的范围)。&/p&&img src=&/v2-fcdd07fbad2b0c9a0fa566_b.jpg& data-rawwidth=&1916& data-rawheight=&1040& class=&origin_image zh-lightbox-thumb& width=&1916& data-original=&/v2-fcdd07fbad2b0c9a0fa566_r.jpg&&
上图是最有用的,这里随便往场景里放了几个模型,这是最初始的效果。添加环境光打开 Windows-&Lighting,在Environment Lighting 里设置Skybox为Default-skybox,unity5应该是默认设好了的,设置之后效果如下:添加SSAO特效Unity自带了一个SSAO特效,导入特…
&p&做游戏技术主要讲究的是套路,以及对套路的掌握程度。比如说你要搞个体积光,那么从用mesh+uv动画,到volumetric scattering你都得知道,而且要知道这些方案的优缺点,以及具体的实现细节,比如camera会不会到体积光里边之类的,这种细节的了解会让你更加有信心做出各种技术决策。&/p&&p&所以3a级游戏的大神技术也是对各种领域的套路玩得比较溜,这里我也分享一些做游戏20来年自己领悟出的一些套路吧。&/p&&p&美术用工具输出的3d模型,材质一定要做一个导出插件,否则做出来和进引擎效果不一样,就只能做做手游这种无光照的手绘贴图质量的产品了。&/p&&p&动画要想不滑步,就只能用animation driven的方法,这个需要跟策划做好深度的沟通,得让策划知道移动不是说配置一个速度就可以了,得去找动画师一起调整移动的动画。&/p&&p&动态天气的困难不是制作上的困难,是可以用lightmap的物件会少很多,性能不一定扛得住,这个要在项目初期一定得和制作团队沟通清楚。&/p&&p&球类游戏要达到非常自然的动画,一方面动画肯定是要动捕的,另一方面更重要的是搭建一套动画选择机制,然后根据运动中的球类位置来选择最合适的动画来匹配,再加上小部分的ik。&/p&&p&头上冒字和冒血这种hud就应该用hud的标准方法来制作,切记不能涂省事用ui的方法,对性能效率会有非常大的影响。&/p&&p&音频对最终品质影响很大,一般遵循46原则,即图像资源在最终包中占6成,音频资源占4成。给音频设计师配置一个独立的程序来达成音频的设计需求。&/p&&p&状态机最好是用可视化的方法来实现,游戏中80%的bug都是和状态没切对有关,有个可视化的状态机对于找bug非常方便。&/p&&p&control是最需要状态机化设计的(不同的情况下按同样的按键达成不同的逻辑)&/p&&p&做面部表情,如果不是捕捉就用骨骼简单搭搭就好,如果是捕捉,制作成本会变得非常的高(制作各种morphing target)&/p&&p&对最终画面影响最大的是镜头效果,所以尽量节省渲染时间留给镜头特效。一般影响最大的是校色,如果硬件平台允许尽量给美术提供带深度校色的工具。&/p&&p&有比较宽广视野的游戏,室外可以用一些很取巧的方法来模拟mie scattering和rayleigh scattering,加上哪怕是假的对画面的提升也是巨大的。&/p&&p&对于不同的数据采用不同的配置方法,不要什么数据都用excel,对于有可视化需求的配置,比如ui或者角色身上需要装配一些武器的,提供可视化的工具。对于需要描述父子关系的例如技能树解锁之类的用json或者xml来描述,对于纯数字逻辑的就用csv就好了。&/p&&p&场景的材质如果制作normal map对于你们团队比较复杂,多加一层detail map也能对效果获得较大的提升。&/p&&p&角色的材质,如果性能有限或者说制作成本无法承受的话specular map比normal map的效果要好。&/p&&p&如果角色会近距离看脸,脸部的眼睛一定要单独拿出来处理,脸可以糊,眼睛一定不能糊。&/p&&p&脚步声的标准做法是在地面上放一层低模做和脚的碰撞检测(脚步声如果追求真实,最少得有3个以上的样本随机)&/p&&p&如果要提升动画效果,考虑主角的前臂加上twist骨骼,左肩和右肩不要从脖子上搭建骨骼,要从胸口开始(做head lookat的时候效果会更好)&/p&&p&衣服的标准做法是通过贴图来控制哪些顶点受skin和物理影响的权重比,那张贴图还可以用一个通道来控制流汗的时候哪些地方的smothness要提高。&/p&&p&搭建一个运行时数据库,并且游戏中经常需要访问的数据都放入数据库,可以类似redis那样非常简单的提供一个set和get就行了,运行时数据库可以帮助你找到绝大多数bug。&/p&&p&制作ai尽量使用行为树,并且花时间一定要让策划具备编辑和调式行为树的能力。&/p&&p&顶点色可以各种花式使用,无论是用来bake ao还是决定detail map的权重,都可以用很低廉的代价来获得非常棒的效果。&/p&&p&动画不是在任何时候都可以blend的,要想效果好,牢记:左脚落地的时候只能blend到左脚落地开始的动画,右脚同上。&/p&&p&做上下楼梯的locomotion的时候,用椭球碰撞体会非常简单的获得还不错的效果。&/p&&p&如果画面会经常快速的运动,可以加入一个vector based motionblur,效果会出奇的好。&/p&&p&要想场景生动,一定要使用decal,一个方便美术的decal工具可以让美术一天之内让整个场景提升几个档次。&/p&&p&暂时想到这些,不说了,休息好了继续干活。&/p&&p&~~~~~继续更新一波套路&/p&&p&解决alpha透贴排序的标准方法是两次绘制,第一次在不透明队列里渲染一次alpha test(cutout),打开zwrite,第二次在transparent队列里开alpha blend不开zwrite,ztest lessequal渲染就能还原美术在maya里看到的效果。&/p&&p&弹簧是个非常好用的物理组件,其虎克系数可以有效的模拟力的衰减来做出很多感人的效果,从乳摇,臀摇,尾巴,头发马尾,布料,脸上被重拳击中的肌肉变化都可以看到弹簧的身影。&/p&&p&adobe fuse cc + mixamo可以非常快速的搭建模型和动画来帮助策划找到剧情的场景的感觉。&/p&&p&脚下ik从程序来讲是很方便实现的,大多数引擎都有ik功能,但是工作流程我发现很多公司都没有正确使用。正确的流程是对输出的动画有一个左右脚高低的分析工具,逐动画生成每个脚步离水平面的高度曲线,然后运行时根据脚步的高低来决定ik和skin的权重。&/p&&p&镜头的设置很重要,最好给美术一个和相机镜头类似的算法来反过来计算fovx和fovy,我看到很多产品的fov都是一水的60之类的。&/p&&p&对于类似铁丝网或者其他半透物体的mipmap的标准做法是先自动生成mipmap,然后要美术手动的来降低不同lod贴图的alpha,换句话说近处看有铁丝网,远看就只有框中间近乎全透了来降低锯齿感。&/p&&p&dx10以上的平台上特效的shader加一句根据当前depth和已经绘制的depth的差来控制alpha可以实现简单且效果不错的软粒子效果。&/p&&p&头发的物理效果可以参考衣服的做法,发根受skin影响多,发梢受物理多一些即可。(额前刘海受物理要关掉重力)&/p&&p&如果实时sss负担太重,可以简单的把模型厚度信息烘培在顶点色的某个通道上,然后只需三两行代码就可以让皮肤有sss效果。&/p&&p&看到好多项目用双面材质只是在shader中简单加一句cull none,这个是完全错误的实现。正确的做法是需要把三角形索引信息走vs传递进来,然后判断是否顺逆时针,对于反面需要把法线反转才能获得正确的渲染结果。&/p&&p&先更新到这。&/p&&p&完了,感觉思绪开始打开了,又更新一些,怕一会会忘。&/p&&p&动画驱动的模式下,转向最少需要8个动画,原地左转90,右转90,左转180,右转180,移动同样四个,然后根据控制器的输入来决定混合的权重,一般神海这种级别的产品光locomotion牵扯到的动画会在40-50个这种量级。&/p&&p&对于衣物和皮肤,detail normal十分重要,可以瞬间让你的衣服能看出针织的材质。&/p&&p&处理声音的时候,远处的声音混响需要程序来手动提升,近处降低混响,对临场感增加很多。&/p&&p&声音如果样本有限,也一定要做随机,哪怕就一个脚步声样本,播放的时候也应该随机pitch和volume。&/p&&p&给策划提供一个调整动画曲线的工具,横轴是动画时间,纵轴是动画播放的速度,策划通过这个工具可以调整出情绪非常饱满的动画效果(例如:攻击前摇动画速度变慢,攻击过程加速之类的)这个需要和动画师沟通清楚,要求他们只需要k好几个相关pose即可。&/p&&p&卡通渲染的时候有一步是非常关键也是很多公司都忽略的,就是手动调整模型法线,这一步得在美术工具内完成,通常是定义toon shading的光方向之后,通过调整法线来让一些部位有“看起来”较为舒服的受光,主要是鼻子,腋下和两个胯这三个位置。&/p&&p&游戏加载的正确做法是,在出包的时候对各资源的单位加载时间进行预计算(适合配置固定的主机游戏)。之后在运行时加载的时候,根据每一帧cpu的可用空闲时间来决定当前帧可以加载的资源类型和数量。&/p&&p&渲染阴影的时候,很多人都会忽略绘制阴影那个pass的优化,以前做上一代游戏机的时候所有会有实时阴影的模型都会做一个低模和专门的材质(如果有透贴阴影的需求)。&/p&&p&对于写实类型的游戏,decals的正确制作方法是拿相机去拍,比如地上的水渍,墙上的裂痕,车上的划痕之类的,开闪光灯拍照,然后回来在ps里面抠出来。&/p&&p&有很多arpg的游戏中都有霸体的设计,霸体中不会受击,其实这个的正确做法是动画新增一个受击层,在播放霸体动画的同时轻微的blend一个受击动画,打击感会舒服很多。&/p&&p&午饭时间!&/p&&p&反响不错啊,再来一波=)&/p&&p&写实风格的贴图,推荐尽量使用Substance制作,打包不同目标平台的时候,可以根据目标平台的实际硬件情况,决定是Bake出Texture还是用Procedual Texture,其实很多时候会发现很多PBR贴图除了Google图片出来修改之外,只有Substance才能比较好的制作出来。&/p&&br&&p&有一个关于Animation Driven的系统中,AI的制作问题,主要是因为动画状态机自己有自己的行为准则,所以传统的AI设计思路就变得比较无力,比如说我希望我的NPC每0.3秒攻击一次,但是攻击动画本身就超过0.3秒了,怎么办?或者说状态机的逻辑无法响应到0.3秒的攻击。这个时候需要跟策划沟通,在Animation Driven的系统下,要完全的分开设计,NPC的大脑是AI,NPC的体格是动画状态机,很多时候AI的设计要根据体格的情况来酌情调整。要在行为树里增加一些检查身体体格的节点,比如说,我要转向某个方向,那么在适当的时候需要检查自己的身体是否真的转到指定方向了;同时对于大脑向身体发送的指令要分优先级,优先级的实现方法是该指令的有效时长,比如说我的身体在受击的状态中,但是我的大脑仍然下命令让我攻击一次对方,这条指令优先级存在时间0.2秒,如果受击状态在0.2秒内复原了,则该指令身体可以执行一次,如果0.2秒内身体仍然无法攻击,就丢掉此次指令。&/p&&br&&p&Volumetric Cloud/Fog/Light的使用一定要提前和美术进行商量,因为计算过程中牵扯到大量的Depth sampling,所以画面中多大会存在多大面积的这类效果一定需要严格控制,否则到了后期不得不移除这些效果的时候会让整个产品瞬间降低一个档次。&/p&&br&&p&在一些中低端设备上可以用一些简单的压缩算法来把RGB + Luminance除以一个除数来压缩到32bits的颜色空间中来获得非常划算的HDR效果,相比较RGBM,用除数可以免费获得“假”正确的alpha blend效果。这一条无需和美术沟通,直接上就行。(移动平台可用,效果很好)&/p&&br&&p&关于差值,差值计算最大的问题是需要和策划沟通清楚,越是符合现实生活运动规则的,只能使用物理的方法进行差值,物理的方法是没有办法精确的保障差值的最终位置和时间,但是却可以获得完美的差值效果。这里有一个非常小的tips来实现物理差值:一个物体从旋转R0,位置P0,差值到旋转R1,位置P1,需要差值的时候,在物体上挂一个弹簧,弹簧的另外一端挂在位置P1上,另外物体的运动规则增加两条:1,每帧运动转角不能超过XX度;2,物体的实际运动位置是当前朝向 * 力矩;即可,差值出来效果棒棒哒,无论是飞个火球还是跟踪导弹都可以用这个。&/p&&br&&p&美术资源的优化,这件事情比较好的方法是要严格的关注,但是尽量不要过早的修改这些问题,因为开发的过程中始终要留一些空间用来提升画面效果,所以过早的修改掉性能瓶颈,可能会导致到最后不得不放弃已经集成进版本的一些效果,非常不划算。所以等大家都对画面比较满意了之后,再进行一轮性能优化,结果还能再增加一些效果,对于美术大兄弟们来说就变成一件很好的事情了。&/p&&p&本来没打算写这么多,只是开始写了个头,就发现还有很多类似的问题也都可以罗列出来,于是出现这么多杂乱的内容,回头有时间会仔细整理整理,比如说配个图什么的,如果有朋友对于一些tips有疑义或者想知道一些详细的实现细节,欢迎私信单聊。&/p&&br&&p&应某同学的要求&/p&&p&更新一波手游的经验ヽ(o?ωo? )ゝ&/p&&p&贴图进版本的时候做一个alpha通道的剥离,一方面可以比较方便的适配安卓ETC1压缩格式,另一方面后期要压包的时候alpha通道可以随意缩大小,美术基本无感知。&/p&&p&如果是UI资源比较重的游戏可以采取layout和资源分离的方式来获得大量的性能提升。针对unity引擎来说就是遍历ui里所有用到的material,把里面的贴图替换成4*4的空白贴图,另外建立一个从材质对应贴图的索引。因为手机是ssd的缘故所以磁盘io读取贴图的速度是非常快的,这样就可以把整个ui的layout都留在内存中不用切场景释放,不同场景对应加载不同的贴图做一个运行时的对应即可,加载ui的时间能提升80%。这一步建议新项目中前期使用,后期项目做这种级别的修改不划算,风险高。&/p&&p&如果是动画资源比较重的产品,建议使用自定义的动画格式,采取float16的四元数储存关键帧数据,然后加载时还原引擎需要的动画数据,以前做wii上的产品,1000个左右的人形动捕数据可以压缩到23m左右。&/p&&p&经历过很多手游项目的全屏大图比较多(推广图,loading图),有的会占到整个贴图量的30%甚至更多,这时候一定要优化大图的制作工艺,和美术沟通把背景,文字和前景拆开制作,一方面可以更好的压缩(方形,2的次幂),另一方面背景图可以采取比较极端的压缩参数。&/p&&p&加载的过程中往往最后会卡一下,这个是第一次shader提交到gpu产生编译消耗的时间,可以通过在屏幕上绘制一个透明的三角形来完成warmup,这一步基本上主流引擎都有接口,如果单帧消耗过高,可以考虑分布在若干帧里完成。&/p&&p&打击感的一个小trick,击中的一瞬间把时间tick调成0持续个0.1秒左右,又轻巧效果又好。&/p&&p&搭建UI框架的时候给每页ui增加一个fadein和fadeout的接口并且管理好状态机的切换,今后可以很方便策划或者美术增加ui打开和关闭的动态效果。&/p&
做游戏技术主要讲究的是套路,以及对套路的掌握程度。比如说你要搞个体积光,那么从用mesh+uv动画,到volumetric scattering你都得知道,而且要知道这些方案的优缺点,以及具体的实现细节,比如camera会不会到体积光里边之类的,这种细节的了解会让你更加有…
&img src=&/50/v2-e129e99c5e502ab5cea8_b.png& data-rawwidth=&710& data-rawheight=&532& class=&origin_image zh-lightbox-thumb& width=&710& data-original=&/50/v2-e129e99c5e502ab5cea8_r.png&&&p&承接上回,继续介绍一些数学工具,为了引出我们今天的主角,纳维斯托克斯(Navier--Stokes, NS)方程做准备。&br&&br&&b&拉普拉斯算子&/b&&br& 拉普拉斯(Laplacial)算子&img src=&/equation?tex=%5Cnabla%5E2& alt=&\nabla^2& eeimg=&1&&又称拉氏算子,是一个标量场的梯度场的散度,而在某些文献中亦写成&img src=&/equation?tex=%5Cnabla%5Ccdot%5Cnabla& alt=&\nabla\cdot\nabla& eeimg=&1&& 或者 &img src=&/equation?tex=%5CDelta& alt=&\Delta& eeimg=&1&&的形式。在下面的介绍中,我们统一使用&img src=&/equation?tex=%5Cnabla%5E2& alt=&\nabla^2& eeimg=&1&&的形式。拉氏算子通常用于描述一个函数中某点的值与它邻域的平均值之差,或者说用于描述某点 在它的邻域中是“多么与众不同”。在三维空间中,它的计算方式为:&br&&img src=&/equation?tex=%5Cnabla%5E2+f%3D%5Cfrac%7B%5Cpartial%5E2+f%7D%7B%5Cpartial+x%5E2%7D%2B%5Cfrac%7B%5Cpartial%5E2+f%7D%7B%5Cpartial+y%5E2%7D%2B%5Cfrac%7B%5Cpartial%5E2+f%7D%7B%5Cpartial+z%5E2%7D& alt=&\nabla^2 f=\frac{\partial^2 f}{\partial x^2}+\frac{\partial^2 f}{\partial y^2}+\frac{\partial^2 f}{\partial z^2}& eeimg=&1&&&br&其中&img src=&/equation?tex=%5Cfrac%7B%5Cpartial%5E2+f%7D%7B%5Cpartial+x%5E2%7D& alt=&\frac{\partial^2 f}{\partial x^2}& eeimg=&1&&是函数f在x方向的二阶偏导数。&br&另外,拥有&img src=&/equation?tex=%5Cnabla%5Ccdot%28a%5Cnabla+f%29%3Dq& alt=&\nabla\cdot(a\nabla f)=q& eeimg=&1&&形式的方程称为泊松方程(Poisson equation),特别的,当&img src=&/equation?tex=q%3D0& alt=&q=0& eeimg=&1&&时,方程被称为拉普拉斯方程。&br&&br&&b&高斯定理&/b&&br&高斯定理亦被称为散度定理,是多元微积分中的基本定理之一。对于流体而言,高斯定理通常用于描述一个区域中流量与这个区域的边界上流入和流出的关系。如下图中,包含区域&img src=&/equation?tex=V& alt=&V& eeimg=&1&&的边界设为&img src=&/equation?tex=S& alt=&S& eeimg=&1&&,其表面法向为&img src=&/equation?tex=%5Cboldsymbol%7Bn%7D& alt=&\boldsymbol{n}& eeimg=&1&&,则有&br&&img src=&/equation?tex=%5Ciiint_V%5Cnabla%5Ccdot%5Cboldsymbol%7Bu%7D%3D%5Ciint_S%5Cboldsymbol%7Bu%7D%5Ccdot%5Cboldsymbol%7Bn%7D& alt=&\iiint_V\nabla\cdot\boldsymbol{u}=\iint_S\boldsymbol{u}\cdot\boldsymbol{n}& eeimg=&1&&也就是说,向量&img src=&/equation?tex=%5Cboldsymbol%7Bu%7D& alt=&\boldsymbol{u}& eeimg=&1&&在区域&img src=&/equation?tex=V& alt=&V& eeimg=&1&&中的和等于&img src=&/equation?tex=%5Cboldsymbol%7Bu%7D& alt=&\boldsymbol{u}& eeimg=&1&& 在这个区域的边界&img src=&/equation?tex=S& alt=&S& eeimg=&1&&的表面法向上投影的和。若&img src=&/equation?tex=%5Cboldsymbol%7Bu%7D& alt=&\boldsymbol{u}& eeimg=&1&& 为流体的速度,那么上面的式子即是说,&i&出入区域&/i&&i&&img src=&/equation?tex=V& alt=&V& eeimg=&1&&的流量&/i&可以用流体&i&垂直于曲面S的流入和流出之和&/i&来描述。&/p&&img src=&/50/v2-4a78a82b5a7e21afec07a_b.png& data-rawwidth=&250& data-rawheight=&188& class=&content_image& width=&250&&&p&有了以上的准备,我们就可以开始推导流体模拟中最常用的方程——NS方程了。&/p&&p&&img src=&/50/v2-b1bef562fcb0f332fae636_b.png& data-rawwidth=&512& data-rawheight=&282& class=&origin_image zh-lightbox-thumb& width=&512& data-original=&/50/v2-b1bef562fcb0f332fae636_r.png&&&br&如上图(查看这张棒棒哒但是被知乎坑了的动图,请点&a href=&/?target=http%3A//www.columbia.edu/%7Eyf2320/unnamed.gif& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&i class=&icon-external&&&/i&&/a&)所示,由于流体在宏观上是连续的,数值模拟对连续物体的处理往往采用离散近似。这里我们把我们要考虑的流体当作由很多的粒子(particles)组成(这些“粒子”未必是水分子,也可以是一个水滴的大小,当然,离散的精细程度决定了模拟效果,也会影响计算量)。我们对流体的模拟就是在每个时刻屏幕上绘制粒子的位置。求解NS方程就是为了求解每个粒子在任何一个时刻的状态。&/p&&p&对于一个粒子,我们记它的质量为&img src=&/equation?tex=m& alt=&m& eeimg=&1&&,加速度为&img src=&/equation?tex=%5Cmathbf%7Ba%7D& alt=&\mathbf{a}& eeimg=&1&&, 受力为&img src=&/equation?tex=%5Cmathbf%7BF%7D& alt=&\mathbf{F}& eeimg=&1&&,速度为&img src=&/equation?tex=%5Cmathbf%7Bu%7D+%5Cequiv+%28u%28t%29%2Cv%28t%29%2Cw%28t%29%29& alt=&\mathbf{u} \equiv (u(t),v(t),w(t))& eeimg=&1&&, 那么我们有&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0A%5Ccentering%0A%5Cmathbf%7BF%7D+%3D+m+%5Cmathbf%7Ba%7D%2C+%5Cquad+%5Cmathbf%7Ba%7D+%5Cequiv+%5Cfrac%7BD+%5Cmathbf%7Bu+%7D%7D%7BD+t%7D%2C+%0A%5Cend%7Bequation%7D%0A& alt=&\begin{equation}
\centering
\mathbf{F} = m \mathbf{a}, \quad \mathbf{a} \equiv \frac{D \mathbf{u }}{D t},
\end{equation}
& eeimg=&1&&&br&进一步得到&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0Am+%5Cfrac%7BD%5Cmathbf%7Bu%7D%7D%7BDt%7D+%3D+%5Cmathbf%7BF%7D.%0A%5Cend%7Bequation%7D%0A& alt=&\begin{equation}
m \frac{D\mathbf{u}}{Dt} = \mathbf{F}.
\end{equation}
& eeimg=&1&&&br&这里我们用了一个比较奇怪的记号,&img src=&/equation?tex=D& alt=&D& eeimg=&1&&。因为&img src=&/equation?tex=%5Cmathbf%7Bu%7D& alt=&\mathbf{u}& eeimg=&1&&不仅是时间的函数,也是空间位置&img src=&/equation?tex=%5Cmathbf%7Bx%7D& alt=&\mathbf{x}& eeimg=&1&&的函数,如果我们只考虑速度场在空间中固定一点的速度变化量(&img src=&/equation?tex=D& alt=&D& eeimg=&1&&)的话,那么由链式法则,显然&img src=&/equation?tex=D& alt=&D& eeimg=&1&& 和&img src=&/equation?tex=%5Cpartial& alt=&\partial& eeimg=&1&& 满足&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0A%5Cfrac%7BD%5Cmathbf%7Bu%7D%7D%7BDt%7D+%3D+%5Cfrac%7B%5Cpartial+%5Cmathbf%7Bu%7D%7D%7B%5Cpartial+t%7D+%2B+%5Cnabla+%5Cmathbf%7Bu%7D+%5Ccdot+%5Cfrac%7B%5Cpartial+%5Cmathbf%7Bx%7D%7D%7B%5Cpartial+t%7D%3D+%5Cfrac%7B%5Cpartial+%5Cmathbf%7Bu%7D%7D%7B%5Cpartial+t%7D+%2B+%5Cnabla+%5Cmathbf%7Bu%7D+%5Ccdot+%5Cmathbf%7Bu%7D%0A%5Cend%7Bequation%7D& alt=&\begin{equation}
\frac{D\mathbf{u}}{Dt} = \frac{\partial \mathbf{u}}{\partial t} + \nabla \mathbf{u} \cdot \frac{\partial \mathbf{x}}{\partial t}= \frac{\partial \mathbf{u}}{\partial t} + \nabla \mathbf{u} \cdot \mathbf{u}
\end{equation}& eeimg=&1&&&br&&img src=&/equation?tex=D& alt=&D& eeimg=&1&&在流体力学中是很常用的记号,叫做物质导数(material derivative)。下一节会进一步说明。&br&&br&对于一个流体,我们还需要考虑它的受力,比如重力&img src=&/equation?tex=m%5Cmathbf%7Bg%7D& alt=&m\mathbf{g}& eeimg=&1&&, 自身的压力,粘滞力,和其他一些外力。一般来说,对于水或者油之类的流体,我们认为其不可压缩,流体自身内部的压力取决了流体的不可压缩性。这个压力来自于流体内部的压强差。对于一个很小的区域,如果我们记区域表面积为&img src=&/equation?tex=A& alt=&A& eeimg=&1&&,压强差最大方向的长度为&img src=&/equation?tex=z& alt=&z& eeimg=&1&&,那么&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0AF+%3D+-+dP+%5Ccdot+dA+%3D+m%5Ccdot+a+%3D-+%5Crho+%5C%2C+dA+%5C%2Cdz+%5Ccdot+a%0A%5Cend%7Bequation%7D& alt=&\begin{equation}
F = - dP \cdot dA = m\cdot a =- \rho \, dA \,dz \cdot a
\end{equation}& eeimg=&1&&&br&可以得到&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0AF+%3D+m+%5Ccdot+a+%3D+m+%5Ccdot+%5Cfrac%7B-dP%7D%7B%5Crho+%5C%2C+dZ%7D+%3D+-V+%5Cnabla+p.%0A%5Cend%7Bequation%7D%0A& alt=&\begin{equation}
F = m \cdot a = m \cdot \frac{-dP}{\rho \, dZ} = -V \nabla p.
\end{equation}
& eeimg=&1&&&br&对于粘滞力,它显示了流体粒子与其周围粒子移动速度的关系。由于&img src=&/equation?tex=%5Cnabla%5E2& alt=&\nabla^2& eeimg=&1&& 可以描述用来描述其与周围粒子平均速度的差距,我们可以定义粘滞力为&img src=&/equation?tex=m+%5Cnu+%5Cnabla%5E2+%5Cmathbf%7Bu%7D& alt=&m \nu \nabla^2 \mathbf{u}& eeimg=&1&&。 其中&img src=&/equation?tex=%5Cnu& alt=&\nu& eeimg=&1&&为 运动黏度系数。 &br&&br&如果我们不另外考虑外力,那么根据合力与分力的关系,对于一个流体粒子,我们有&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0Am%5Cfrac%7BD+%5Cmathbf%7Bu%7D%7D%7BD+t%7D+%3D+m%5Cmathbf%7Bg%7D+-+V+%5Cnabla+p+%2B+m+%5Cnu+%5Cnabla+%5Ccdot+%5Cnabla+%5Cmathbf%7Bu%7D.%0A%5Cend%7Bequation%7D& alt=&\begin{equation}
m\frac{D \mathbf{u}}{D t} = m\mathbf{g} - V \nabla p + m \nu \nabla \cdot \nabla \mathbf{u}.
\end{equation}& eeimg=&1&&&br&由于我们的流体粒子可以无限小,为了避免方程两端趋近于0,我们把两边同除以&img src=&/equation?tex=m& alt=&m& eeimg=&1&&, 得到&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0A%5Cfrac%7BD+%5Cmathbf%7Bu%7D%7D%7BD+t%7D+%3D+%5Cmathbf%7Bg%7D+-+%5Cfrac%7B1%7D%7B%5Crho%7D%5Cnabla+p+%2B+%5Cnu%5Cnabla+%5Ccdot+%5Cnabla+%5Cmathbf%7Bu%7D.%0A%5Cend%7Bequation%7D& alt=&\begin{equation}
\frac{D \mathbf{u}}{D t} = \mathbf{g} - \frac{1}{\rho}\nabla p + \nu\nabla \cdot \nabla \mathbf{u}.
\end{equation}& eeimg=&1&&&br&这就是流体力学中“臭名昭著”(化掉)的NS方程。&br&&br&只要给足够大的压强,流体实际上是可以压缩的。但可压缩流体的计算很复杂,以至于我们一般把水或者油等流体看作不可压缩。(想象一下液压千斤顶的工作原理)&br&&img src=&/50/v2-fcf9b15a463de2a_b.jpg& data-rawwidth=&2048& data-rawheight=&1365& class=&origin_image zh-lightbox-thumb& width=&2048& data-original=&/50/v2-fcf9b15a463de2a_r.jpg&&&br&&br&(只有在如水下爆炸等极端情况下,水的可压缩性才会体现出来)&br&&br&不可压缩流体的一个简单假设是,在流体里任何一“小块”&img src=&/equation?tex=%5COmega& alt=&\Omega& eeimg=&1&&所占的体积不随时间而改变。如果我们记体积的边缘为&img src=&/equation?tex=%5Cpartial+%5COmega& alt=&\partial \Omega& eeimg=&1&&, 那么此假设表明该体积的变化率为0,也即&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0A%5Cfrac%7BdV%28+%5COmega%29%7D%7Bdt%7D+%3D+%5Ciint_%7B%5Cpartial+%5COmega%7D+%5Cmathbf%7Bu%7D+%5Ccdot+%5Cmathbf%7Bn%7D+%3D+0.%0A%5Cend%7Bequation%7D& alt=&\begin{equation}
\frac{dV( \Omega)}{dt} = \iint_{\partial \Omega} \mathbf{u} \cdot \mathbf{n} = 0.
\end{equation}& eeimg=&1&&&br&由高斯定理,我们得到等价表达式&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0A%5Ciiint_%7B%5COmega%7D+%5Cnabla+%5Ccdot+%5Cmathbf%7Bu%7D+%3D+0.%0A%5Cend%7Bequation%7D& alt=&\begin{equation}
\iiint_{\Omega} \nabla \cdot \mathbf{u} = 0.
\end{equation}& eeimg=&1&&&br&注意到,对流体的任何部分,这个表达式都是成立的。而积分号内部是一个连续函数,由反证法,我们可以得到&br&&img src=&/equation?tex=%5Cbegin%7Bequation%7D%0A%5Cnabla+%5Ccdot+%5Cmathbf%7Bu%7D+%3D+0%0A%5Cend%7Bequation%7D%0A& alt=&\begin{equation}
\nabla \cdot \mathbf{u} = 0
\end{equation}
& eeimg=&1&&&br&这样就得到了不可压缩性。不可压缩的流体衍生出来的向量场我们叫它无散(divergence-free)场(因为&img src=&/equation?tex=%5Cnabla& alt=&\nabla& eeimg=&1&&表示散度)。&br&&br&下图是一个典型的不可压缩流的速度场(图片&a href=&/?target=https%3A//people.sc.fsu.edu/%7Ejburkardt/m_src/navier_stokes_2d_exact/navier_stokes_2d_exact.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&出处&i class=&icon-external&&&/i&&/a&)&br&&img src=&/50/v2-cacd8b87bbcb_b.png& data-rawwidth=&605& data-rawheight=&560& class=&origin_image zh-lightbox-thumb& width=&605& data-original=&/50/v2-cacd8b87bbcb_r.png&&&br&&br&下一节我们将讲述为什么我们需要物质导数,以及利用物质守恒定律推出流体满足的另外一个方程——连续方程。&br&&/p&&br&&p&Reference:&/p&&p&Bridson, Robert. &i&Fluid simulation for computer graphics&/i&. CRC Press, 2015.&/p&&p&_(:3」∠)_ _(?ω?”∠)_ _(:з)∠)_ ∠( ? 」∠)_ _(:зゝ∠)_&br&请毫不犹豫地关注我们:&br&我们的网站:&a href=&/?target=https%3A//graphicon.io& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GraphiCon&i class=&icon-external&&&/i&&/a&&br&知乎专栏:&a href=&/graphicon& class=&internal&&GraphiCon图形控&/a&&br&公众号:GraphiCon&/p&&p&如果你有什么想法,建议,或者想加入我们,你可以:&br&给我们发邮件:&a href=&mailto:hi@graphicon.io&&&u&hi@graphicon.io&/u&&/a&&br&加入我们的QQ群:SIQGRAPH()&br&加入我们的slack群:&a href=&/?target=https%3A///& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GraphiCon&i class=&icon-external&&&/i&&/a&&/p&&img src=&/50/v2-e96f2a5b3d9e23d2362e6e_b.png& data-rawwidth=&201& data-rawheight=&201& class=&content_image& width=&201&&&p&本作品采用&a href=&/?target=http%3A//creativecommons.org/licenses/by-nc-nd/4.0/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议&i class=&icon-external&&&/i&&/a&进行许可。&/p&
承接上回,继续介绍一些数学工具,为了引出我们今天的主角,纳维斯托克斯(Navier--Stokes, NS)方程做准备。 拉普拉斯算子 拉普拉斯(Laplacial)算子\nabla^2又称拉氏算子,是一个标量场的梯度场的散度,而在某些文献中亦写成\nabla\cdot\nabla 或者 \Delta的…
&img src=&/50/v2-be9d69cd5a65f2eaadfdd_b.jpg& data-rawwidth=&1719& data-rawheight=&831& class=&origin_image zh-lightbox-thumb& width=&1719& data-original=&/50/v2-be9d69cd5a65f2eaadfdd_r.jpg&&&p&这篇文章将基于MatCap的思想,在Unity中实现具有高度真实感的MatCap车漆Shader。采用MatCap思想的Shader,用低廉的计算成本,就可以达到类似PBS非常真实的渲染效果,可谓是在移动平台实现接近次时代渲染效果的一种优秀解决方案。&/p&&br&&p&本文以车漆Shader为例,但MatCap思想能实现的,并不局限于车漆Shader。本来准备给本文取名《一种基于MatCap的低计算成本、高真实感的移动平台Shader解决方案》的,但这个名字太大了,遂改之。&/p&&p&先看一下最终的效果图(渲染于Unity 5.5.0)。&/p&&img src=&/v2-be9d69cd5a65f2eaadfdd_b.jpg& data-rawwidth=&1719& data-rawheight=&831& class=&origin_image zh-lightbox-thumb& width=&1719& data-original=&/v2-be9d69cd5a65f2eaadfdd_r.jpg&&&img src=&/v2-189acfb38bd5ebf58a3dda7_b.jpg& data-rawwidth=&1719& data-rawheight=&864& class=&origin_image zh-lightbox-thumb& width=&1719& data-original=&/v2-189acfb38bd5ebf58a3dda7_r.jpg&&&img src=&/v2-389cd62e942c8aacd595fc8af490c6be_b.jpg& data-rawwidth=&1718& data-rawheight=&866& class=&origin_image zh-lightbox-thumb& width=&1718& data-original=&/v2-389cd62e942c8aacd595fc8af490c6be_r.jpg&&&br&&br&&h3&一、MatCap概述&/h3&&p&Material Capture(材质捕获),通常被简称为MatCap,在Zbrush、Sculptris、Mudbox等3D软件中有比较多的使用。&/p&&p&MatCap Shader的基本思路是,使用某特定材质球的贴图,作为当前材质的视图空间环境贴图(view-space environment map),来实现具有均匀表面着色的反射材质物体的显示。考虑到物体的所有法线的投影的范围在x(-1,1),y(-1,1),构成了一个圆形,所以MatCap 贴图中存储光照信息的区域是一个圆形。&/p&&p&基于MatCap思想的Shader,可以无需提供任何光照,只需提供一张或多张合适的MatCap贴图作为光照结果的“指导”即可。如下图:&/p&&img src=&/v2-aa8b531e73be59587f1e_b.jpg& data-rawwidth=&400& data-rawheight=&300& class=&content_image& width=&400&&&img src=&/v2-66b4fc7c820efa5ec0eaec4_b.jpg& data-rawwidth=&400& data-rawheight=&236& class=&content_image& width=&400&&&p&这两张图来自(&a href=&/?target=http%3A//digitalrune.github.io/DigitalRune-Documentation/html/9a8c8b37-b996-477a-aeab-5d92714be3ca.htm& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&digitalrune.github.io/D&/span&&span class=&invisible&&igitalRune-Documentation/html/9a8c8b37-b996-477a-aeab-5d92714be3ca.htm&/sp

我要回帖

更多关于 unity 判断对象类型 的文章

 

随机推荐