lol进不去游戏界面面会有上一帧物体留下的轮廓

&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=&/v2-7c2f999ac220a_b.png& data-rawwidth=&567& data-rawheight=&541& class=&origin_image zh-lightbox-thumb& width=&567& data-original=&/v2-7c2f999ac220a_r.png&&&h2&&b&约定:&/b&&/h2&&p&假定游戏是开房间/匹配进战斗的类型,是Statefull的,不借用数据库和微服务实现业务。&/p&&p&每个玩家同时最多存在两个后端逻辑服务器(接管)处理其业务。&/p&&br&&p&其中一个&通常&保持不变(且在玩家链接到网关时立即进行分配),用于处理游戏房间外的游戏逻辑(譬如抽宝箱,购买道具等),记作 &i&PrimaryServer&/i&;另外一个作为游戏房间内的业务处理,会随着每次匹配而变动/重新分配,记作&i&SlaveServer&/i&。&/p&&br&&h2&&b&主要架构:&/b&&/h2&&p&1:&u&&b&网关服务器&/b&&/u&&/p&&p&网关用于转发玩家和逻辑服务器之间的通信。网关和逻辑服务器是一套消息定义&i&ProtoGateLanServer&/i&,玩家和&服务器&则是另外一套消息定义,彼此独立;网关会把玩家发送的游戏消息通过&i&ProtoGateLanServer中的一个UpMsg&/i&转发给逻辑服务器。(也即,修改玩家游戏相关协议不会改动网关)&/p&&p&这里只介绍比较重要的一个类型,玩家在网关上的设计:&/p&&br&&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&k&&struct&/span& &span class=&n&&ClientSession&/span&
&span class=&p&&{&/span&
&span class=&n&&SocketSession&/span& &span class=&n&&netSession&/span&&span class=&p&&;&/span&
&span class=&kt&&int64_t&/span&
&span class=&n&&clientID&/span&&span class=&p&&;&/span&
&span class=&n&&SocketSession&/span& &span class=&n&&primaryServer&/span&&span class=&p&&;&/span&
&span class=&n&&SocketSession&/span& &span class=&n&&slaveServer&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&其中 &i&netSession&/i&是玩家在网关上的网络会话对象;&i&clientID &/i&则作为玩家在整个集群中的运行时ID,&i&&u&&b&需要保证每个网关上不重复&/b&&/u&&/i&(譬如采用 &i&[网关ID:自增ID:时间] &/i&来确定)&/p&&p&&i&primaryServer &/i&是接管此玩家游戏房间外/非战斗逻辑的逻辑服务器链接,&i&slaveServer&/i&则是房间内/战斗业务逻辑服务器(后者可为null,譬如玩家在没有战斗的时候)&/p&&br&&p&网关接收到玩家的消息后优先发送给&i&slaveServer&/i&,如果不存在则发送给&i&primaryServer。&/i&&/p&&br&&p&2:&u&&b&逻辑服务器&/b&&/u&&/p&&p&根据前面所述,逻辑服务器分为两种类型:Primary和Slave。&/p&&p&玩家在逻辑服务器上的会话对象结构:&/p&&div class=&highlight&&&pre&&code class=&language-cpp&&&span&&/span&&span class=&k&&struct&/span& &span class=&n&&PlayerSession&/span&
&span class=&p&&{&/span&
&span class=&n&&SocketSession&/span& &span class=&n&&connectionServerSession&/span&&span class=&p&&;&/span&
&span class=&kt&&int64_t&/span&
&span class=&n&&clientID&/span&&span class=&p&&;&/span&
&span class=&n&&Any&/span&
&span class=&n&&userData&/span&&span class=&p&&;&/span&
&span class=&c1&&// 逻辑层对象&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&&i&connectionServerSession &/i&是此玩家所在网关在逻辑服务器上的网络会话, &i&clientID &/i&则就是玩家在网关上分配的运行时ID。 逻辑服务器上采用 map&int64_t, PlayerSession& 管理会话列表,当收到网关转发的玩家消息时,根据&i&clientID&/i&查找&i&PlayerSession &/i&,然后交给其 &i&userData&/i&进行处理。&/p&&p&同样,玩家的逻辑对象也需要包含一个 &i&PlayerObject &/i&对象,用于给玩家发送消息(通过&i&ProtoGateLanServer&/i&中一个DownMsg消息,携带clientID 即可,网关通过&i&clientID&/i&查询玩家session即可发送给玩家;根据网络库差异则实现有所不同,譬如用我的网络库中,DownMsg可携带玩家在网关上的SessionID,逻辑服务器下发消息时携带它,网关收到后直接调用send接口,不用进行一次map查询)。&/p&&p&&i&整个集群中,通常只有Slave类型的逻辑服务器会链接/读写DB。&/i&&/p&&p&3:&u&&b&全局服务器&/b&&/u&&/p&&p&逻辑服务器之间的通信全部通过全局服务器进行中转,一般而言消息量不会特别高;比如玩家大部分时间时在战斗中,也只有战斗匹配时和结束时才通过全局服务器转发相关消息而已。&/p&&p&全局服务器也维护架构中的全局业务逻辑。&/p&&br&&br&&h2&&b&难点问题及其处理&/b&:&/h2&&p&1:&u&&b&定位/标识玩家的问题&/b&&/u&&/p&&p&逻辑服务器发送消息给网关,让其转发消息给玩家时,只允许携带玩家的&i&clientID&/i&,不能携带任何业务上的ID(以让网关去定位玩家),这样保证网关与逻辑无关。&/p&&p&内部逻辑服务器之间在业务通信时,若需要定位玩家,则只允许在消息中携带玩家的Name或数据库UID等逻辑唯一标识,在逻辑层面通过它们去查找玩家。 切记不能通过&i&clientID&/i&去定位玩家。&/p&&br&&p&2:&u&&b&登陆上下线&/b&&/u&&/p&&p&登陆消息通过 网关-&Primary逻辑服务器,后者处理成功后告诉全局服务器进行登记,全局服务器判断玩家是否已经在线。若已经在线,则返回失败,并踢掉先前的账号,踢号流程务必按照:&/p&&p&全局服务器-&逻辑服务器-&网关服务器—&i&网关踢掉玩家网络链接—然后再 =& 通知逻辑服务器某玩家下线 =& 通知全局服务器移除玩家。&/i&&/p&&p&所有的 -& (注意不是 =&)
仅做转发,不做业务/数据/状态上的任何处理。&/p&&p&玩家下线流程与上面斜线部分基本一致(只不过会延迟执行下线流程--给予断线重连机会)。 也就是说踢号流程是内部服务器通知网关服务器:1、主动断开玩家网络。2、立即走一次下线流程。&/p&&br&&p&3:&u&&b&断线重连&/b&&/u&&/p&&p&首先需要网关检测玩家网络断开时,延迟n秒后才通知内部服务器某&i&clientID&/i&的玩家掉线,以此延迟回收玩家逻辑对象。断线重连流程是,玩家新建链接,并发送重连消息包到任意网关(即允许跟之前所链接的网关不一样)(理所当然,客户端需要上传自己的UID和Password之类),网关广播给所有内部逻辑服务器。逻辑服务器通过UID查询玩家,如果找到则根据新的链接信息重写其 (本文上面有定义)PlayerSession 的值(业务数据不需要任何变动)。那么当网关延迟到期,通知旧的clientID掉线时,在逻辑服务器里就不会/能再做任何操作了,因为这个clientID已经没有关联到逻辑对象了。 &/p&&p&然后再进行两个操作:1、成功处理断线重连请求的LogicServer告诉网关,请求将玩家的primaryServer设置成它。2、通知全局服务器修改玩家会话的ClientID和所属CSID等(并非具体业务上需要, 主要用于玩家匹配进入战斗后,全局服务器需要告诉战斗逻辑服务器构建玩家 PlayerSession)。&/p&&br&&br&&h2&&b&各类型服务宕机和恢复:&/b&&/h2&&p&1: 网关服务器宕机:&br&
PrimaryServer延迟执行此网关上的所有玩家掉线处理(等待玩家通过其他网关进行重连)&br&
GameServer延迟执行此网关上的所有玩家掉线处理(退出战斗)(等待玩家通过其他网关进行重连)&br&&br&2:非战斗逻辑服务器(PrimaryServer)宕机:&/p&&br&&p&
所有服务器执行此服务器上的玩家断线处理(通常会有回档问题,无可避免,当然你也可以在重要操作后立即写数据库,减小损失)&br&&br&3:战斗逻辑服务器断开(SlaveSrever)宕机:&br&
所有服务器执行此服务器上的玩家退出/结束战斗处理(无可避免)&br&&br&4:全局服务器宕机:&br&PrimaryServer在再次重连全局服务器成功后,尝试发送当前所有在线的玩家数据&br&全局服务器会基于此重建在线玩家表,即可恢复服务&br&SlaveSrever则不作处理,只需定时重连全局服务器即可。(譬如待玩家战斗结束,直接发送战斗结果给全局服务器就行)&/p&&p&PS:额外说两点,各个服务器之间的TCP Srvice具备自动重连功能。集群的开启,与各个服务器的启动顺序无关。&/p&&br&&br&&h2&&b&单点全局服务器的处理/改进办法:&/b&&/h2&&p&1:所有采用分布式数据库做数据持久化,从而将全局逻辑直接放到各个LogicServer(可能还需要事务)去做的情况(从来去掉全局服务器),本文不做讨论,因为某些业务无法(方便/高效的)用数据库的数据结构/类型去表示,所以本文假设必然存在一个全局服务器保持内存状态去处理全局业务(譬如匹配功能,组队功能等,当然再额外拆分出多个匹配服务器什么的做sharding也是可以的,另谈)。&br&&/p&&p&2:先将全局服务器:逻辑服务器之间消息转发业务和全局业务分离&/p&&p&2:将消息转发业务改成逻辑服务器互联;或者通过第三方分布式消息中间件(但一般都不适合游戏),主要担心消息的乱序问题。(为啥没有通过网关转发,还是担心乱序问题,除非你的应用无所谓消息顺序)&/p&&p&4:再将全局业务通过多个全局服务器,采用Raft管理起来,完成高可用的全局业务处理。(因为全局服务器可能含有一些状态,所以要考虑高可用,譬如全局匹配队列信息,组队信息等,如果没有它们这些状态,那直接将按照上一小结所描述的服务快速恢复即可。&/p&&br&&br&&h2&&b&网关和逻辑服务器的动态增添(提高系统承载)&/b&:&/h2&&p&采用&b&etcd&/b&维护各个服务器信息,且逻辑服务器服务器定期轮询它们;譬如增加一个新网关,它告诉etcd。 逻辑服务器拿到最新的网关列表,筛选出新的网关服务器,并进行链接。&/p&&br&&br&&h2&&b&数据库写入问题的处理办法:&/b&&/h2&&p&在需要写入数据的业务处理中,先判断玩家是否在线,在线则将消息发送给玩家所在LogicServer进行处理,以尽量确保一个玩家的数据总是在一个节点去修改(假定服务器都是有状态的,不是stateless那种借用数据库和微服务的)。若不在线,尽量采用事务,或者采用list等操作去增添(修改)数据(避免直接update set)。&/p&&br&&p&最后,关于弱联网/Stateless的大服架构,譬如COC等,可参考《大型网站技术架构》一书。&/p&
本文所讨论的整体架构图:约定:假定游戏是开房间/匹配进战斗的类型,是Statefull的,不借用数据库和微服务实现业务。每个玩家同时最多存在两个后端逻辑服务器(接管)处理其业务。 其中一个"通常"保持不变(且在玩家链接到网关时立即进行分配),用于处理游…
&img src=&/v2-b215b3f8eb880ad77e081b7ffd685a9e_b.jpg& data-rawwidth=&592& data-rawheight=&332& class=&origin_image zh-lightbox-thumb& width=&592& data-original=&/v2-b215b3f8eb880ad77e081b7ffd685a9e_r.jpg&&&h2&写在前面&/h2&&p&最近闲来无事, 因此给自己开了个新坑 - 为一款科幻风格的塔防游戏Hacker F-301(骇客F-301)编写着色器特效. 在这个过程中有了一些心得体会, 因此打算写出来分享. 希望大家多提宝贵意见, 不吝斧正.&/p&&br&&h2&Topics&/h2&&p&本文将介绍如下几种特效:&/p&&li&Inking (模型描边)&/li&&li&Hologram (模型的全息图)&/li&&li&See - Through (渲染出物体被遮挡的部分, 类似于穿墙透视效果 - 屏幕后期特效)&/li&&li&Force Field (力场护盾效果)&/li&&li&Video Glitch (模拟LCD显示屏受到电子干扰的效果 - 屏幕后期特效. &/li&&br&&h2&Inking (模型描边, Outline)&/h2&&p&何为Inking? &/p&&p&Inking是附加在&b&蒙皮网格&/b&上的模型特效, 它用比较细的灰黑色的线条&b&勾勒出网格的轮廓&/b&. 这样做的好处是能够从背景更加&b&清晰&/b&&b&地勾画&/b&出这个网格, 尤其是在&b&对比度&/b&比较低的区域中. Inking特效的应用场景特别多, 大家耳熟能详的&b&&u&LOL&/u&&/b&中就出现了它的踪影:&br&&/p&&img src=&/v2-41bc0ba2a1fc6c29d5fc4c_b.jpg& data-rawwidth=&425& data-rawheight=&407& class=&origin_image zh-lightbox-thumb& width=&425& data-original=&/v2-41bc0ba2a1fc6c29d5fc4c_r.jpg&&&img src=&/v2-608be38de54c5bb29c9e38_b.jpg& data-rawwidth=&425& data-rawheight=&407& class=&origin_image zh-lightbox-thumb& width=&425& data-original=&/v2-608be38de54c5bb29c9e38_r.jpg&&&p&上面的两张图来源于一篇对LOL渲染流程分析的博客[1]. 第一张图中是原图, 而第二张图是加入Inking特效后的结果. 我们看到, 加入Inking后, 所有的模型能够更容易地从背景中区分出来, 起到了&b&Bump Up&/b&的作用. 这个例子中使用了比较粗的Inking线条, 这样也增添了一分&b&漫画风格&/b&的质感.&/p&&br&&p&&b&Inking的实现方法(综述)&/b&&/p&&p&Inking的实现方法有很多种, 大体上可以分为操作点元和操作片元两大类. 视具体情况决定使用哪一种Inking:&br&&/p&&ul&&li&Fresnel(菲尼尔)方法 - 非常类似于Rim Lighting, 使用视线方向和点法线方向的点积来判断边缘, 并将边缘高亮化.&/li&&li&优点: 效率高; 不需要单独的Pass就可以实现; 几乎所有的平滑的边缘都会得到高亮效果; 甚至对透明和半透明物体也有效. 缺点: 无法控制Inking线条的粗细, 这是因为Fresnel方法是针对于模型法线和摄像机视线的, 从而导致其仅与每个表面的法线方向有关, 而与表面的深度信息无关.&/li&&/ul&&br&&img src=&/v2-79cb72d03ad_b.jpg& data-rawwidth=&236& data-rawheight=&294& class=&content_image& width=&236&&&br&&ul&&li&Mesh Doubling (复制网格) - 非常类似于卡通Toon特效. 需要一个单独的Pass来实现. 重新绘制一个将所有表面都沿着法线方向延展过的模型, 然后将正面剪裁掉. 这也是我采用的方案&/li&&li&优点: 效率高; 平台适应性好; 可以控制Inking的线条粗细. 缺点: 线条并不连续, 在平滑表面的表现虽然很好, 但是在锐利的表面上经常会出现断层; 只能绘制最外层轮廓, 而不对内部结构做任何处理.&br&&/li&&/ul&&br&&img src=&/v2-eb29dfdfed7b5_b.png& data-rawwidth=&800& data-rawheight=&398& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&/v2-eb29dfdfed7b5_r.png&&&br&&ul&&li&Edge Detection (边缘检测) - Unity自带的屏幕后期处理特效[2]. 使用Sobel Filter[3]进行描边的算法, 其基本原理是检测多个相邻的像素的深度差值, 使用一个3x3的采样块来对原图求卷积, 将深度信息差值比较大的部分过滤出来. LOL中的Inking使用的就是这个方法.&/li&&li&优点: 既可以用作屏幕后期特效, 又可以作为模型特效; 描边准确; 线条粗细可控. 缺点: 比上述两种方案都要昂贵得多, 但是其性能开销恒定, 与被处理的图像没有任何关系;&br&&/li&&/ul&&img src=&/v2-faf7f219e7562bb03be1_b.jpg& data-rawwidth=&550& data-rawheight=&290& class=&origin_image zh-lightbox-thumb& width=&550& data-original=&/v2-faf7f219e7562bb03be1_r.jpg&&&ul&&li&使用几何着色器 - 检验临近的多边形以确定邻边和夹角, 再单独构建轮廓的几何体.&/li&&li&优点: 目前为止最为精确的做法; 很容易控制线条的粗细. 缺点: 建议买一台给力点的工作站或服务器; 一般只能用于离线渲染;&/li&&/ul&&br&&p&&b&具体实现策略&/b&&/p&&p&采用了&b&Mesh Doubling (复制网格)&/b&的方法. 这里必须要解决的问题是线条的不连续性. 其思路是不严格地将表面沿着法线方向延展, 而是在标准化的&b&点元位置和法线方向之间取一个恰当的参数来做插值&/b&, 这样做的好处是表面在延展的过程中也会尽量向点元方向靠拢, 尽量地减少了新网格的&b&撕裂感&/b&.&br&&/p&&img src=&/equation?tex=P_%7Bnew%7D%3DP_%7Bold%7D%2B+L+%5Ctimes+W_%7Boutline%7D+%2F+D_%7Bcam%7D& alt=&P_{new}=P_{old}+ L \times W_{outline} / D_{cam}& eeimg=&1&&&br&&img src=&/equation?tex=L+%3D+Normalize%28MV_%7BIT%7D+%5Ctimes+lerp%28V%2C+N%2C+f%29%29& alt=&L = Normalize(MV_{IT} \times lerp(V, N, f))& eeimg=&1&&&br&&p&其中, L表示偏移向量; W表示轮廓线条粗细; D是物体和摄像机间的距离. V是标准化后的顶点坐标, 表示方向; N是顶点向量; f是插值参数.&br&&/p&&p&&img src=&/v2-3b1fd6c19fbe5e83e7cf493cac8d2709_b.png& data-rawwidth=&552& data-rawheight=&319& class=&origin_image zh-lightbox-thumb& width=&552& data-original=&/v2-3b1fd6c19fbe5e83e7cf493cac8d2709_r.png&&上图更加清晰地阐述了撕裂和不连续的情况. 如果不进行插值, 那么这种方法可以适用于球形等表面变化均匀且光滑的几何体, 但是对于立方体则无能为力.&br&&/p&&img src=&/v2-f83a2cf35fa780e47de52ede51916b15_b.png& data-rawwidth=&586& data-rawheight=&438& class=&origin_image zh-lightbox-thumb& width=&586& data-original=&/v2-f83a2cf35fa780e47de52ede51916b15_r.png&&&p&上图中的立方体的延展向量使用了参数0.032作为插值, 撕裂感便不复存在了.&/p&&p&这里给出Inking特效的核心程序代码(非常简短), 同时附上部分Implementation Notes:&/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&n&&vertexOutput&/span& &span class=&n&&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&&vertexOutput&/span& &span class=&n&&o&/span&&span class=&p&&;&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/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=&n&&float3&/span& &span class=&n&&dir&/span& &span class=&o&&=&/span& &span class=&n&&normalize&/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=&n&&xyz&/span& &span class=&p&&);&/span&
&span class=&n&&float3&/span& &span class=&n&&dir2&/span& &span class=&o&&=&/span& &span class=&n&&v&/span&&span class=&p&&.&/span&&span class=&n&&normal&/span&&span class=&p&&;&/span&
&span class=&n&&dir&/span& &span class=&o&&=&/span& &span class=&n&&lerp&/span& &span class=&p&&(&/span& &span class=&n&&dir&/span&&span class=&p&&,&/span& &span class=&n&&dir2&/span&&span class=&p&&,&/span& &span class=&n&&_Factor&/span& &span class=&p&&);&/span&
&span class=&n&&dir&/span& &span class=&o&&=&/span& &span class=&n&&mul&/span& &span class=&p&&(&/span& &span class=&p&&(&/span& &span class=&n&&float3x3&/span& &span class=&p&&)&/span& &span class=&n&&UNITY_MATRIX_IT_MV&/span&&span class=&p&&,&/span& &span class=&n&&dir&/span& &span class=&p&&);&/span&
&span class=&n&&float2&/span& &span class=&n&&offset&/span& &span class=&o&&=&/span& &span class=&n&&TransformViewToProjection&/span& &span class=&p&&(&/span& &span class=&n&&dir&/span&&span class=&p&&.&/span&&span class=&n&&xy&/span& &span class=&p&&);&/span&
&span class=&n&&offset&/span& &span class=&o&&=&/span& &span class=&n&&normalize&/span& &span class=&p&&(&/span& &span class=&n&&offset&/span& &span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&dist&/span& &span class=&o&&=&/span& &span class=&n&&distance&/span& &span class=&p&&(&/span& &span class=&n&&mul&/span& &span class=&p&&(&/span& &span class=&n&&UNITY_MATRIX_M&/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=&n&&_WorldSpaceCameraPos&/span& &span class=&p&&);&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/span&&span class=&p&&.&/span&&span class=&n&&xy&/span& &span class=&o&&+=&/span& &span class=&n&&offset&/span& &span class=&o&&*&/span& &span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/span&&span class=&p&&.&/span&&span class=&n&&z&/span& &span class=&o&&*&/span& &span class=&n&&_OutlineWidth&/span& &span class=&o&&/&/span& &span class=&n&&dist&/span&&span class=&p&&;&/span&
&span class=&k&&return&/span& &span class=&n&&o&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&_Factor即为插值参数. 变换法向量要注意使用Model View矩阵的转置逆矩阵. 为了保证最终的线条粗细维持世界坐标上的恒定, 而不随摄像机的移动发生改变, 因此延展的像素位置要除以摄像机距离. 最终片元着色器函数只需要一句return _C即可. 最后必须注意剪裁掉正面, 否则绘制出的不会是轮廓, 而是将模型包裹起来的保鲜膜
...&br&&/p&&img src=&/v2-004b561a1fdfeec2a482bd8b33c95804_b.png& data-rawwidth=&1366& data-rawheight=&853& class=&origin_image zh-lightbox-thumb& width=&1366& data-original=&/v2-004b561a1fdfeec2a482bd8b33c95804_r.png&&&img src=&/v2-1a8c4e105d9303abdc1918_b.png& data-rawwidth=&1366& data-rawheight=&853& class=&origin_image zh-lightbox-thumb& width=&1366& data-original=&/v2-1a8c4e105d9303abdc1918_r.png&&&br&&p&上面两张图是加入特效的前后对比. 我们看到使用Inking后炮塔能够更加&犀利&地从背景中呈现出来.&/p&&p&一定程度上, Inking有点类似于SSAO. 两者都是尝试在几何体的交界处加入更深层次的阴影以让画面更有对比度. 关于SSAO我的&a href=&/p/?refer=MeowShader& class=&internal&&知乎专栏第四篇&/a&有过介绍.&/p&&h2&Hologram (模型的全息图)&/h2&&p&何为Hologram? &/p&&p&全息图是一般以激光为光源, 将被摄物体记录为3D光场(Light Field)所构成的三维图像. 一般以干涉条纹的形式存在.&/p&&img src=&/v2-9f6ba038b041_b.jpg& data-rawwidth=&1276& data-rawheight=&1024& class=&origin_image zh-lightbox-thumb& width=&1276& data-original=&/v2-9f6ba038b041_r.jpg&&&img src=&/v2-9be6e19d1bb0e8ad15d49b3dff971a4e_b.jpg& data-rawwidth=&610& data-rawheight=&343& class=&origin_image zh-lightbox-thumb& width=&610& data-original=&/v2-9be6e19d1bb0e8ad15d49b3dff971a4e_r.jpg&&&p&上图中第一张图是全息投影仪的概念效果, 第二张图是质量效应(Mass Effect)中的特效.&/p&&p&Hologram一般可以用作单位建造的&b&预览效果&/b&&b&和&/b&&b&呈现结构&/b&的效果.&/p&&img src=&/v2-b215b3f8eb880ad77e081b7ffd685a9e_b.jpg& data-rawwidth=&592& data-rawheight=&332& class=&origin_image zh-lightbox-thumb& width=&592& data-original=&/v2-b215b3f8eb880ad77e081b7ffd685a9e_r.jpg&&&img src=&/v2-5f90e48e0f3d62dd0dac5_b.jpg& data-rawwidth=&785& data-rawheight=&393& class=&origin_image zh-lightbox-thumb& width=&785& data-original=&/v2-5f90e48e0f3d62dd0dac5_r.jpg&&&h4&第一次尝试(Naive 方法)&/h4&&p&计算模型的每个片元的屏幕坐标, 然后对一个条纹状纹理采样即可. 为了防止单一的条纹过于无聊, 同时还引入了一个Noise Map来进行干扰. 代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&n&&v2f&/span& &span class=&n&&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&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/span& &span class=&o&&=&/span& &span class=&n&&UnityObjectToClipPos&/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=&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&&xy&/span&&span class=&p&&;&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&screenPos&/span& &span class=&o&&=&/span& &span class=&n&&ComputeScreenPos&/span& &span class=&p&&(&/span& &span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/span& &span class=&p&&);&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&dist&/span& &span class=&o&&=&/span& &span class=&n&&distance&/span& &span class=&p&&(&/span& &span class=&n&&mul&/span& &span class=&p&&(&/span& &span class=&n&&UNITY_MATRIX_M&/span&&span class=&p&&,&/span& &span class=&n&&float4&/span& &span class=&p&&(&/span& &span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span& &span class=&p&&)&/span& &span class=&p&&),&/span& &span class=&n&&_WorldSpaceCameraPos&/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&&fixed4&/span& &span class=&n&&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&&COLOR&/span&
&span class=&p&&{&/span&
&span class=&n&&fixed4&/span& &span class=&n&&finalColor&/span&&span class=&p&&;&/span&
&span class=&n&&float2&/span& &span class=&n&&uvNormal&/span& &span class=&o&&=&/span& &span class=&n&&UnpackNormal&/span& &span class=&p&&(&/span& &span class=&n&&tex2D&/span& &span class=&p&&(&/span& &span class=&n&&_NormalTex&/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=&p&&)&/span& &span class=&o&&/&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&dist&/span&&span class=&p&&;&/span&
&span class=&n&&float2&/span& &span class=&n&&screenUV&/span& &span class=&o&&=&/span& &span class=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&screenPos&/span&&span class=&p&&.&/span&&span class=&n&&xy&/span& &span class=&o&&/&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&screenPos&/span&&span class=&p&&.&/span&&span class=&n&&w&/span& &span class=&o&&+&/span& &span class=&n&&float2&/span& &span class=&p&&(&/span& &span class=&n&&_TilingX&/span& &span class=&o&&*&/span& &span class=&n&&_Time&/span&&span class=&p&&.&/span&&span class=&n&&y&/span&&span class=&p&&,&/span& &span class=&n&&_TilingY&/span& &span class=&o&&*&/span& &span class=&n&&_Time&/span&&span class=&p&&.&/span&&span class=&n&&y&/span& &span class=&p&&)&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&dist&/span& &span class=&o&&*&/span& &span class=&n&&_Distance&/span&&span class=&p&&;&/span&
&span class=&n&&fixed3&/span& &span class=&n&&color&/span& &span class=&o&&=&/span& &span class=&n&&_Color&/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&&screenUV&/span& &span class=&o&&+&/span& &span class=&n&&uvNormal&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&_Emission&/span&&span class=&p&&;&/span&
&span class=&k&&fixed&/span& &span class=&n&&alpha&/span& &span class=&o&&=&/span& &span class=&n&&_Color&/span&&span class=&p&&.&/span&&span class=&n&&a&/span& &span class=&o&&*&/span& &span class=&n&&max&/span& &span class=&p&&(&/span& &span class=&n&&min&/span& &span class=&p&&(&/span& &span class=&n&&color&/span&&span class=&p&&.&/span&&span class=&n&&r&/span&&span class=&p&&,&/span& &span class=&n&&color&/span&&span class=&p&&.&/span&&span class=&n&&g&/span& &span class=&p&&),&/span& &span class=&n&&color&/span&&span class=&p&&.&/span&&span class=&n&&b&/span& &span class=&p&&);&/span&
&span class=&k&&return&/span& &span class=&n&&fixed4&/span& &span class=&p&&(&/span& &span class=&n&&color&/span&&span class=&p&&,&/span& &span class=&n&&alpha&/span& &span class=&p&&);&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&得到的结果自然也是Naive的
:&br&&/p&&img src=&/v2-2f683f9d230dfe9ad867ad_b.jpg& data-rawwidth=&300& data-rawheight=&252& class=&content_image& width=&300&&&p&对比上面的两张效果图, 我们发现一个问题: 虽然当前的这个Hologram特效能够显示出干涉条纹, 但是整个物体的深度和法线信息全部丢失, 给人以一种乱糟糟的线条感.&/p&&br&&h4&第二次尝试(将深度和法线信息纳入考量)&/h4&&p&仔细观察前面的两张效果图, 我们看到dot(viewDirection, normalDirection)(以下简称为点积)越大则越昏暗. 为了让整个特效更加有层次感, 我选择的方案是分别计算点积大的区域和点积小的区域来的颜色信息. 同时我更新了对干涉条纹的计算方法: 为了防止整个全息图特效的颜色过淡, 先给予一个统一的强度_Strength, 然后再加上对干涉条纹的采样值.&/p&&img src=&/equation?tex=%5Calpha+%3D+F_%7Bsoft%7D+%5Ctimes+%28Z_%7Bscene%7D+-+Z_%7Bobject%7D%29%0A& alt=&\alpha = F_{soft} \times (Z_{scene} - Z_{object})
& eeimg=&1&&&br&&img src=&/equation?tex=S_%7Bedge%7D+%3D+%281+-+abs%28N+%5Ccdot+V%29%29+%5E+%7BI_%7Bedge%7D%7D+%5Ctimes+M_%7Bedge%7D& alt=&S_{edge} = (1 - abs(N \cdot V)) ^ {I_{edge}} \times M_{edge}& eeimg=&1&&&br&&img src=&/equation?tex=S_%7Bsurf%7D+%3D+%28abs%28N+%5Ccdot+V%29%29+%5E+%7BI_%7Bsurf%7D%7D+%5Ctimes+M_%7Bsurf%7D& alt=&S_{surf} = (abs(N \cdot V)) ^ {I_{surf}} \times M_{surf}& eeimg=&1&&&br&&img src=&/equation?tex=C_%7Bhologram%7D+%3D+tex%28T_%7Bmain%7D%2C+wcoord+%2B+UV_%7Btime%7D%29& alt=&C_{hologram} = tex(T_{main}, wcoord + UV_{time})& eeimg=&1&&&br&&img src=&/equation?tex=C+%3D+%5Calpha+%2A+%28S_%7Bedge%7D+%2B+S_%7Bsurf%7D%29+%2A+%28C_%7Bnormal%7D+%2B+C_%7Bhologram%7D%29& alt=&C = \alpha * (S_{edge} + S_{surf}) * (C_{normal} + C_{hologram})& eeimg=&1&&&br&&p&得到的结果如下: &/p&&img src=&/v2-cb1b5d00f32e124d2b6269_b.jpg& data-rawwidth=&300& data-rawheight=&252& class=&content_image& width=&300&&&br&&p&着色器代码如下: &/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&n&&v2f&/span& &span class=&n&&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&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/span& &span class=&o&&=&/span& &span class=&n&&UnityObjectToClipPos&/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=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&projPos&/span& &span class=&o&&=&/span& &span class=&n&&ComputeScreenPos&/span& &span class=&p&&(&/span& &span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/span& &span class=&p&&);&/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&&xy&/span&&span class=&p&&;&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&normalDir&/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=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&posWorld&/span& &span class=&o&&=&/span& &span class=&n&&mul&/span& &span class=&p&&(&/span& &span class=&n&&UNITY_MATRIX_M&/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=&k&&return&/span& &span class=&n&&o&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&n&&fixed4&/span& &span class=&n&&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&&COLOR&/span&
&span class=&p&&{&/span&
&span class=&k&&fixed&/span& &span class=&n&&alpha&/span& &span class=&o&&=&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span&
&span class=&k&&float&/span& &span class=&n&&sceneZ&/span& &span class=&o&&=&/span& &span class=&n&&LinearEyeDepth&/span& &span class=&p&&(&/span& &span class=&n&&SAMPLE_DEPTH_TEXTURE_PROJ&/span& &span class=&p&&(&/span& &span class=&n&&_CameraDepthTexture&/span&&span class=&p&&,&/span& &span class=&n&&UNITY_PROJ_COORD&/span& &span class=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&projPos&/span& &span class=&p&&)&/span& &span class=&p&&)&/span& &span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&partZ&/span& &span class=&o&&=&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&projPos&/span&&span class=&p&&.&/span&&span class=&n&&z&/span&&span class=&p&&;&/span&
&span class=&k&&float&/span& &span class=&n&&fade&/span& &span class=&o&&=&/span& &span class=&n&&saturate&/span& &span class=&p&&(&/span& &span class=&n&&_InvFade&/span& &span class=&o&&*&/span& &span class=&p&&(&/span& &span class=&n&&sceneZ&/span& &span class=&o&&-&/span& &span class=&n&&partZ&/span& &span class=&p&&)&/span& &span class=&p&&);&/span&
&span class=&n&&alpha&/span& &span class=&o&&*=&/span& &span class=&n&&fade&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&viewDirection&/span& &span class=&o&&=&/span& &span class=&n&&normalize&/span& &span class=&p&&(&/span& &span class=&n&&_WorldSpaceCameraPos&/span&&span class=&p&&.&/span&&span class=&n&&xyz&/span& &span class=&o&&-&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&posWorld&/span&&span class=&p&&.&/span&&span class=&n&&xyz&/span& &span class=&p&&);&/span&
&span class=&n&&float4&/span& &span class=&n&&objectOrigin&/span& &span class=&o&&=&/span& &span class=&n&&mul&/span& &span class=&p&&(&/span& &span class=&n&&unity_ObjectToWorld&/span&&span class=&p&&,&/span& &span class=&n&&float4&/span& &span class=&p&&(&/span& &span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span&&span class=&p&&,&/span& &span class=&mf&&1.0&/span& &span class=&p&&)&/span& &span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&dist&/span& &span class=&o&&=&/span& &span class=&n&&distance&/span& &span class=&p&&(&/span& &span class=&n&&_WorldSpaceCameraPos&/span&&span class=&p&&.&/span&&span class=&n&&xyz&/span&&span class=&p&&,&/span& &span class=&n&&objectOrigin&/span&&span class=&p&&.&/span&&span class=&n&&xyz&/span& &span class=&p&&);&/span&
&span class=&n&&float2&/span& &span class=&n&&wcoord&/span& &span class=&o&&=&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&projPos&/span&&span class=&p&&.&/span&&span class=&n&&xy&/span& &span class=&o&&/&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&projPos&/span&&span class=&p&&.&/span&&span class=&n&&w&/span&&span class=&p&&;&/span&
&span class=&n&&wcoord&/span&&span class=&p&&.&/span&&span class=&n&&x&/span& &span class=&o&&*=&/span& &span class=&n&&_Inter&/span&&span class=&p&&.&/span&&span class=&n&&y&/span&&span class=&p&&;&/span&
&span class=&n&&wcoord&/span&&span class=&p&&.&/span&&span class=&n&&y&/span& &span class=&o&&*=&/span& &span class=&n&&_Inter&/span&&span class=&p&&.&/span&&span class=&n&&z&/span&&span class=&p&&;&/span&
&span class=&n&&wcoord&/span& &span class=&o&&*=&/span& &span class=&n&&dist&/span& &span class=&o&&*&/span& &span class=&n&&_Inter&/span&&span class=&p&&.&/span&&span class=&n&&x&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&nMask&/span& &span class=&o&&=&/span& &span class=&n&&_Strength&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&hMask&/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&&wcoord&/span& &span class=&o&&+&/span& &span class=&n&&float2&/span& &span class=&p&&(&/span& &span class=&mo&&0&/span&&span class=&p&&,&/span& &span class=&n&&_Time&/span&&span class=&p&&.&/span&&span class=&n&&x&/span& &span class=&o&&*&/span& &span class=&n&&_Inter&/span&&span class=&p&&.&/span&&span class=&n&&w&/span& &span class=&p&&)&/span& &span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&fresnel&/span& &span class=&o&&=&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&n&&abs&/span& &span class=&p&&(&/span& &span class=&n&&dot&/span& &span class=&p&&(&/span& &span class=&n&&viewDirection&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&normalDir&/span& &span class=&p&&)&/span& &span class=&p&&),&/span& &span class=&n&&_FresPow&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&_FresMult&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&bLayer&/span& &span class=&o&&=&/span& &span class=&n&&lerp&/span& &span class=&p&&(&/span& &span class=&n&&_bLayerColorA&/span&&span class=&p&&,&/span& &span class=&n&&_bLayerColorB&/span&&span class=&p&&,&/span& &span class=&n&&fresnel&/span& &span class=&p&&);&/span&
&span class=&k&&float&/span& &span class=&n&&fresnelOut&/span& &span class=&o&&=&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&mi&&1&/span& &span class=&o&&-&/span& &span class=&n&&abs&/span& &span class=&p&&(&/span& &span class=&n&&dot&/span& &span class=&p&&(&/span& &span class=&n&&viewDirection&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&normalDir&/span& &span class=&p&&)&/span& &span class=&p&&),&/span& &span class=&n&&_FresPowOut&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&_FresMultOut&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&bLayerC&/span& &span class=&o&&=&/span& &span class=&n&&_bLayerColorC&/span& &span class=&o&&*&/span& &span class=&n&&fresnelOut&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&final&/span& &span class=&o&&=&/span& &span class=&n&&saturate&/span& &span class=&p&&(&/span& &span class=&p&&(&/span& &span class=&n&&hMask&/span& &span class=&o&&+&/span& &span class=&n&&nMask&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&p&&(&/span& &span class=&n&&bLayer&/span& &span class=&o&&+&/span& &span class=&n&&bLayerC&/span& &span class=&p&&)&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&alpha&/span&&span class=&p&&;&/span&
&span class=&k&&return&/span& &span class=&n&&float4&/span& &span class=&p&&(&/span& &span class=&n&&final&/span& &span class=&o&&*&/span& &span class=&n&&_Fade&/span&&span class=&p&&,&/span& &span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&&p&&b&See - Through(&/b&&b&透视特效&/b&&b&)&/b&&br&&/p&&p&何为透视特效? &/p&&img src=&/v2-e6b0b04fefcc3a_b.jpg& data-rawwidth=&473& data-rawheight=&473& class=&origin_image zh-lightbox-thumb& width=&473& data-original=&/v2-e6b0b04fefcc3a_r.jpg&&&p&游戏中总有一些非常重要的物体, 需要确保玩家在任何时候都能以某种方式看到. 比如Hitman系列中玩家可以使用这种透视的方式来知道敌人的位置以确定自己的战术. 而RTS类游戏(比如红色警戒3)中被遮挡的单位也会以另一种颜色被渲染出来, 防止玩家不知道其存在.&/p&&h4&如何实现透视特效&/h4&&p&将游戏物体分为两层: Occluder(遮挡)层和Behind(后面)层. 特效要实现的目标是将Behind层被Occluder层遮挡的部分渲染出来. 这里使用两个摄像机, 分别渲染两个层的深度信息, 得到两张Render Target(以下简称RT). 将所有Behind RT深度大于对应Occluder RT深度的部分以另一种方式渲染出来, 而不对其余部分做任何处理, 并将结果放到一个新的RT中. 最终画一个全屏幕的Quad, 将这个RT直接Apply即可.&/p&&p&为了增加渲染结果的层次感并反应被遮挡物体的结构, 渲染Behind层的摄像机可以同时渲染法线信息, 然后在渲染最终RT的时候将颜色强度与法线方向挂钩即可.&/p&&br&&p&以下是渲染结果:&/p&&img src=&/v2-b6bb46b4e7b93a18f6ba_b.jpg& data-rawwidth=&300& data-rawheight=&252& class=&content_image& width=&300&&&p&如何让Rendering Path为Forward的摄像机得到场景的深度和法线信息呢? 使用单独的着色器规定其渲染行为, 然后使用Camera.RenderWithShader即可.&/p&&br&&p&规定摄像机渲染方式的着色器:&/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&k&&struct&/span& &span class=&n&&v2f&/span&
&span class=&p&&{&/span&
&span class=&n&&float4&/span& &span class=&n&&pos&/span& &span class=&o&&:&/span& &span class=&n&&POSITION&/span&&span class=&p&&;&/span&
&span class=&n&&float4&/span& &span class=&n&&nz&/span& &span class=&o&&:&/span& &span class=&n&&TEXCOORD0&/span&&span class=&p&&;&/span&
&span class=&p&&};&/span&
&span class=&n&&v2f&/span& &span class=&n&&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&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/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=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&nz&/span&&span class=&p&&.&/span&&span class=&n&&xyz&/span& &span class=&o&&=&/span& &span class=&n&&COMPUTE_VIEW_NORMAL&/span&&span class=&p&&;&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&nz&/span&&span class=&p&&.&/span&&span class=&n&&w&/span& &span class=&o&&=&/span& &span class=&n&&COMPUTE_DEPTH_01&/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&&fixed4&/span& &span class=&n&&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&&COLOR&/span&
&span class=&p&&{&/span&
&span class=&k&&return&/span& &span class=&n&&EncodeDepthNormal&/span& &span class=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&nz&/span&&span class=&p&&.&/span&&span class=&n&&w&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&nz&/span&&span class=&p&&.&/span&&span class=&n&&xyz&/span& &span class=&p&&);&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&处理两张RT的着色器:&br&&/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&n&&v2f&/span& &span class=&n&&vert&/span& &span class=&p&&(&/span& &span class=&n&&appdata_img&/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&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/span& &span class=&o&&=&/span& &span class=&n&&UnityObjectToClipPos&/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=&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&&xy&/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&&fixed4&/span& &span class=&n&&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&&COLOR&/span&
&span class=&p&&{&/span&
&span class=&k&&float&/span& &span class=&n&&behindDepth&/span&&span class=&p&&,&/span& &span class=&n&&occluderDepth&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&behindNormal&/span&&span class=&p&&,&/span& &span class=&n&&occluderNormal&/span&&span class=&p&&;&/span&
&span class=&n&&DecodeDepthNormal&/span& &span class=&p&&(&/span& &span class=&n&&tex2D&/span& &span class=&p&&(&/span& &span class=&n&&_Behind&/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&&behindDepth&/span&&span class=&p&&,&/span& &span class=&n&&behindNormal&/span& &span class=&p&&);&/span&
&span class=&n&&DecodeDepthNormal&/span& &span class=&p&&(&/span& &span class=&n&&tex2D&/span& &span class=&p&&(&/span& &span class=&n&&_Occluder&/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&&occluderDepth&/span&&span class=&p&&,&/span& &span class=&n&&occluderNormal&/span& &span class=&p&&);&/span&
&span class=&n&&fixed4&/span& &span class=&n&&scene&/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=&n&&fixed4&/span& &span class=&n&&pattern&/span& &span class=&o&&=&/span& &span class=&n&&tex2D&/span& &span class=&p&&(&/span& &span class=&n&&_PatternTex&/span&&span class=&p&&,&/span& &span class=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span& &span class=&o&&+&/span& &span class=&n&&_SinTime&/span&&span class=&p&&.&/span&&span class=&n&&w&/span& &span class=&o&&/&/span& &span class=&mi&&100&/span& &span class=&p&&)&/span& &span class=&o&&/&/span& &span class=&n&&_PatternScale&/span& &span class=&p&&);&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&behindDepth&/span& &span class=&o&&&&/span& &span class=&mo&&0&/span& &span class=&o&&&&&/span& &span class=&n&&occluderDepth&/span& &span class=&o&&&&/span& &span class=&mo&&0&/span& &span class=&o&&&&&/span& &span class=&n&&behindDepth&/span& &span class=&o&&&&/span& &span class=&n&&occluderDepth&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&k&&float&/span& &span class=&n&&factor&/span& &span class=&o&&=&/span& &span class=&mf&&0.1&/span& &span class=&o&&+&/span& &span class=&mf&&0.9&/span& &span class=&o&&*&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&n&&max&/span& &span class=&p&&(&/span& &span class=&n&&dot&/span& &span class=&p&&(&/span& &span class=&n&&float3&/span& &span class=&p&&(&/span& &span class=&mo&&0&/span&&span class=&p&&,&/span& &span class=&mo&&0&/span&&span class=&p&&,&/span& &span class=&mi&&1&/span& &span class=&p&&),&/span& &span class=&n&&behindNormal&/span& &span class=&p&&),&/span& &span class=&mf&&0.0&/span& &span class=&p&&),&/span& &span class=&mf&&1.2&/span& &span class=&p&&);&/span&
&span class=&k&&return&/span& &span class=&n&&fixed4&/span& &span class=&p&&(&/span& &span class=&n&&lerp&/span& &span class=&p&&(&/span& &span class=&n&&scene&/span&&span class=&p&&,&/span& &span class=&n&&_Color&/span&&span class=&p&&,&/span& &span class=&n&&lerp&/span& &span class=&p&&(&/span& &span class=&n&&factor&/span&&span class=&p&&,&/span& &span class=&n&&factor&/span& &span class=&o&&*&/span& &span class=&n&&pattern&/span&&span class=&p&&.&/span&&span class=&n&&r&/span&&span class=&p&&,&/span& &span class=&n&&_PatternWeight&/span& &span class=&p&&)&/span& &span class=&p&&)&/span& &span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&k&&else&/span&
&span class=&k&&return&/span& &span class=&n&&scene&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&h4&一个小问题&/h4&&p&这个实现的方式开销有点大: 对于每一组Occluder层和Behind层, 都需要用两个摄像机单独渲染一次(虽然只需要深度和法线, 不需要计算光照), 然后做一次全屏幕的后期特效. 那么对于像红色警戒3这样的游戏, 总共有6个阵营的单位, 而每个阵营的单位被遮挡后都要被渲染为不同的颜色. 如果采用我的方法的话, 那就相当于要用7个(6 * Behind + 1 * Occluder)摄像机加6层后期特效, 显然在性能上是不可能的.&/p&&p&根据我的猜测, 红色警戒3应该是为每个单位额外附加了一个材质, 这个材质只有ZTest Fail的时候才会被渲染. 不知道这个猜测是否正确, 希望能与大家交流. &/p&&br&&h2&ForceField (护盾特效)&/h2&&p&何为ForceField?&br&&/p&&img src=&/v2-6c3a86e9ebf331458cdac46dc4bb0b73_b.jpg& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-6c3a86e9ebf331458cdac46dc4bb0b73_r.jpg&&&p&上图为TitanFall 2的截图. 图中泰坦的周围有一层半球形力场护盾. 如果有子弹打击都护盾上, 则对应被击中位置还会产生纹理上的变化, 并扭曲背景.&/p&&br&&p&我们注意到这个ForceField与其他物体相交的位置都有高亮的特效. 关于相交高亮(Intersection Highlight)我的&a href=&/p/?refer=MeowShader& class=&internal&&专栏第二篇文章&/a&有过介绍.&/p&&p&如何实现ForceField?&/p&&p&实现护盾本身并不难 --- 只需要一个半透明的材质就足够了. 难的是如果护盾被击中的话, 需要在被击中的位置产生变化.&/p&&p&为了解决这个问题, 我们必须将碰撞点的位置从世界坐标转换到模型坐标. 然后根据每个点元和碰撞点的距离来决定动态纹理的Alpha值即可.&/p&&img src=&/equation?tex=%5Calpha+%3D+A_%7Binit%7D+%2B+%5Csum_%7Bi+%3D+1%7D%5E%7Bn%7D%7Bx%7D++%5Cfrac+%7BDist_%7Bi%7D%7D+%7BDist_%7Bmax%7D%7D+%5Ctimes+B& alt=&\alpha = A_{init} + \sum_{i = 1}^{n}{x}
\frac {Dist_{i}} {Dist_{max}} \times B& eeimg=&1&&&br&&p&为了能够支持多个碰撞点, 可以在着色器中使用n个4*4矩阵以代表4 * n个碰撞点. 每个片元遍历一次所有的碰撞点信息即可. 护盾被击中以后, 其动态纹理的影响会随着时间逐渐减少, 因此动态纹理的Alpha也需要随着时间减弱(见下图).&/p&&img src=&/v2-c889ad3e120e7d45f8f0a7763fcb3001_b.jpg& data-rawwidth=&300& data-rawheight=&252& class=&content_image& width=&300&&&p&时间可以通过脚本或着色器来控制. 只需要注意时间非负即可.&/p&&p&以下是着色器代码:&br&&/p&&div class=&highlight&&&pre&&code class=&language-glsl&&&span&&/span&&span class=&n&&v2f&/span& &span class=&n&&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&&v&/span&&span class=&p&&.&/span&&span class=&n&&vertex&/span& &span class=&o&&+=&/span& &span class=&n&&float4&/span& &span class=&p&&(&/span& &span class=&n&&v&/span&&span class=&p&&.&/span&&span class=&n&&normal&/span& &span class=&o&&*&/span& &span class=&n&&_MeshOffset&/span&&span class=&p&&,&/span& &span class=&mf&&0.0&/span& &span class=&p&&);&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&pos&/span& &span class=&o&&=&/span& &span class=&n&&UnityObjectToClipPos&/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=&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&&xy&/span&&span class=&p&&;&/span&
&span class=&n&&float3&/span& &span class=&n&&worldPosition&/span& &span class=&o&&=&/span& &span class=&n&&mul&/span& &span class=&p&&(&/span& &span class=&n&&UNITY_MATRIX_M&/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=&n&&float3&/span& &span class=&n&&viewDirection&/span& &span class=&o&&=&/span& &span class=&n&&normalize&/span& &span class=&p&&(&/span& &span class=&n&&worldPosition&/span& &span class=&o&&-&/span& &span class=&n&&_WorldSpaceCameraPos&/span& &span class=&p&&);&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&factor&/span& &span class=&o&&=&/span&
&span class=&p&&(&/span& &span class=&n&&dot&/span& &span class=&p&&(&/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=&n&&viewDirection&/span& &span class=&p&&)&/span& &span class=&p&&);&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span& &span class=&k&&int&/span& &span class=&n&&ii&/span& &span class=&o&&=&/span& &span class=&mo&&0&/span&&span class=&p&&;&/span& &span class=&n&&ii&/span& &span class=&o&&&&/span& &span class=&mi&&4&/span&&span class=&p&&;&/span& &span class=&n&&ii&/span&&span class=&o&&++&/span& &span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&n&&o&/span&&span class=&p&&.&/span&&span class=&n&&dist&/span&&span class=&p&&[&/span& &span class=&n&&ii&/span& &span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&distance&/span& &span class=&p&&(&/span& &span class=&n&&_CollisionPoints&/span&&span class=&p&&[&/span& &span class=&n&&ii&/span& &span class=&p&&].&/span&&span class=&n&&xyz&/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=&n&&xyz&/span& &span class=&p&&);&/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&&fixed4&/span& &span class=&n&&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&&COLOR&/span&
&span class=&p&&{&/span&
&span class=&n&&fixed4&/span& &span class=&n&&finalColor&/span&&span class=&p&&;&/span&
&span class=&n&&float2&/span& &span class=&n&&uvNormal&/span& &span class=&o&&=&/span& &span class=&n&&UnpackNormal&/span& &span class=&p&&(&/span& &span class=&n&&tex2D&/span& &span class=&p&&(&/span& &span class=&n&&_NormalTex&/span&&span class=&p&&,&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span& &span class=&o&&*&/span& &span class=&n&&_NormalScale&/span& &span class=&o&&+&/span& &span class=&n&&float2&/span& &span class=&p&&(&/span& &span class=&n&&_TilingX&/span& &span class=&o&&*&/span& &span class=&n&&_Time&/span&&span class=&p&&.&/span&&span class=&n&&y&/span&&span class=&p&&,&/span& &span class=&n&&_TilingY&/span& &span class=&o&&*&/span& &span class=&n&&_Time&/span&&span class=&p&&.&/span&&span class=&n&&y&/span& &span class=&p&&)&/span& &span class=&p&&)&/span& &span class=&p&&);&/span&
&span class=&n&&fixed3&/span& &span class=&n&&color&/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=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&uv&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&_MainScale&/span& &span class=&o&&+&/span& &span class=&n&&uvNormal&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&_Color&/span& &span class=&o&&*&/span& &span class=&n&&_Emission&/span&&span class=&p&&;&/span&
&span class=&k&&float&/span& &span class=&n&&fallOff&/span& &span class=&o&&=&/span& &span class=&n&&saturate&/span& &span class=&p&&(&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&mf&&1.0&/span& &span class=&o&&-&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&factor&/span&&span class=&p&&,&/span& &span class=&n&&_FallOff&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&factor&/span&&span class=&p&&,&/span& &span class=&n&&_FallOff2&/span& &span class=&p&&)&/span& &span class=&p&&);&/span&
&span class=&c1&&///Magic Number! &/span&
&span class=&k&&half&/span& &span class=&n&&alpha&/span& &span class=&o&&=&/span& &span class=&mf&&0.01&/span&&span class=&p&&;&/span&
&span class=&n&&alpha&/span& &span class=&o&&+=&/span& &span class=&n&&saturate&/span& &span class=&p&&(&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&n&&_CollisionTime&/span&&span class=&p&&.&/span&&span class=&n&&x&/span&&span class=&p&&,&/span& &span class=&mf&&0.5&/span& &span class=&p&&)&/span& &span class=&o&&-&/span& &span class=&p&&(&/span& &span class=&k&&float&/span& &span class=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&dist&/span&&span class=&p&&[&/span& &span class=&mo&&0&/span& &span class=&p&&]&/span& &span class=&p&&)&/span& &span class=&o&&/&/span& &span class=&n&&_MaxDistance&/span& &span class=&p&&)&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&_BrightnessCollision&/span& &span class=&o&&*&/span& &span class=&n&&max&/span& &span class=&p&&(&/span& &span class=&n&&sign&/span& &span class=&p&&(&/span& &span class=&n&&_CollisionTime&/span&&span class=&p&&.&/span&&span class=&n&&x&/span& &span class=&p&&),&/span& &span class=&mf&&0.0&/span& &span class=&p&&);&/span&
&span class=&n&&alpha&/span& &span class=&o&&+=&/span& &span class=&n&&saturate&/span& &span class=&p&&(&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&n&&_CollisionTime&/span&&span class=&p&&.&/span&&span class=&n&&y&/span&&span class=&p&&,&/span& &span class=&mf&&0.5&/span& &span class=&p&&)&/span& &span class=&o&&-&/span& &span class=&p&&(&/span& &span class=&k&&float&/span& &span class=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&dist&/span&&span class=&p&&[&/span& &span class=&mi&&1&/span& &span class=&p&&]&/span& &span class=&p&&)&/span& &span class=&o&&/&/span& &span class=&n&&_MaxDistance&/span& &span class=&p&&)&/span& &span class=&p&&)&/span& &span class=&o&&*&/span& &span class=&n&&_BrightnessCollision&/span& &span class=&o&&*&/span& &span class=&n&&max&/span& &span class=&p&&(&/span& &span class=&n&&sign&/span& &span class=&p&&(&/span& &span class=&n&&_CollisionTime&/span&&span class=&p&&.&/span&&span class=&n&&y&/span& &span class=&p&&),&/span& &span class=&mf&&0.0&/span& &span class=&p&&);&/span&
&span class=&n&&alpha&/span& &span class=&o&&+=&/span& &span class=&n&&saturate&/span& &span class=&p&&(&/span& &span class=&n&&pow&/span& &span class=&p&&(&/span& &span class=&n&&_CollisionTime&/span&&span class=&p&&.&/span&&span class=&n&&z&/span&&span class=&p&&,&/span& &span class=&mf&&0.5&/span& &span class=&p&&)&/span& &span class=&o&&-&/span& &span class=&p&&(&/span& &span class=&k&&float&/span& &span class=&p&&(&/span& &span class=&n&&i&/span&&span class=&p&&.&/span&&span class=&n&&dist&/span&&span class=&p&&[&/span& &span class=&mi&&2&/span& &span class=&p&&]&/span& &span class}

我要回帖

更多关于 android 识别物体轮廓 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信