微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET)

    网上火热的“微软.NET年芳15”文章,我也得写点什么嘛,毕竟我还是现任的微软MVP

image  微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET) image thumb

 

摘录网上的“.NET 15周年”信息如下:

微软的 .NET 框架本周迎来了 15 岁生日。.NET 的第一个版本在 2002 年 2 月 13 日作为的 Visual Studio.NET 的一部分首次公开亮相。过去 15 年,.NET 框架从一个流行的闭源软件开发平台,变成了一个开源的跨平台开发工具,微软是在 2014 年 11 月 12 日宣布开源 .NET,支持竞争对手 Linux 和 OS X 的平台。微软称,Visual Studio 2017 将于 2017 年 3 月 7 日发布,而 Visual Studio 也迎来了 20 岁生日,开源的 .NET Core 也将发布 2.0版本

 

      我梳理了一下目前的.NET技术栈: 业务是基于Unity3D(跨平台游戏开发引擎),开发AR、VR 应用; 绝大部分项目采用单机版,使用的编程语言是C#(Mono.NET),不过最近交流的AR几个业务,明显用户已经不太满足单机版,有更多的需求需要用到联网的服务器功能,故技术选型就势在必行了:也算是未雨绸缪吧。

 

技术栈选型

我的技术选型有2点原则

  1. 成熟,有助于提高开发效率
  2. 人多,有坑大家一起填

这2点原则是基于第一次创业失败的经验总结。约在3年前吧,刚开始创业的时候,特别排斥C#.NET技术(互联网创业,还用C#,说出去都不好意思啊),虽然用的是Unity3D游戏引擎(C#),但是服务器选择却是非.NET倾向的。 那个时候对比了多种技术栈:

  • PHP游戏服务器
  • Node.js游戏服务器
  • C++游戏服务器
  • JAVA游戏服务器

权衡再三选择是Java,采用的是SmartFoxServer商用服务器框架,JAVA和C#非常类似、学习成本低一些。

种种原因,后来呢,第一次创业失败了,我的反思是“创业成功大部分原因是CEO的引导有方,创业失败大部分原因是CTO的选择失误导致”。

当时我没有考虑到成本意识、对一个5人研发团队,C#、Java并行的沟通风险有多大:

如果当时客户端是C#技术栈、服务器端也是C#技术栈,则可以从5人技术团队裁掉1人,节约20%人力成本,而且减少沟通后可以提高30%的开发效率,进而有效的推动产品快速扑向市场的速度,可惜这个是“事后诸葛亮”。

这个“事后诸葛亮”,我一直如噎在喉.

       不过去年在一次微软技术峰会,看到网易案例的演讲,我的眼睛亮了起来:“微软技术大神徐玉涛Tory做的分享—网易《绝代双骄》将游戏后端从Java转为新一代.NET Core,研发效率提升20%,服务器成本节省50%”–在线视频为证。

 

故对选择一款C#的服务器,以打通AR、VR客户端、服务器端,提高开发效率,生产力加速,我个人非常期待

Photon

商用

成熟

C# (Mono.NET)

ASP.NET Core

开源

 2.0还未发布

.NET Core

Unity Network Unity自带 局域网

C# (Mono.NET)

有SmartFoxServer的实践经验,故雷同的商用服务器Photon我本次做了专门的实践.

Photon支持3种类型服务器

  • LoadBalancing: 负载均衡服务器
  • MMO: 大型MMO网游服务器
  • Lite: 大厅、房间类型服务器 (相对简单一些)

Photon授权策略:免费版100个CCU (对于绝大部分AR、VR项目足以了);  商用版3500美金不限制CCU (约2.5万人民币)。

image  微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET) image thumb1

 

云服务器选型

我的选型原则也就2点:

  1. 速度:国内访问速度快、快、快!
  2. 便宜:方便我们这种初创型公司节约不必要的成本

虽然同样是世纪互联在运营,但是早期的AZure在我心目中,网速那是非常非常的慢、价格也是高高在上(和国内的阿里、百度等云比起来,毫无优势可言),故原来使用过阿里云、七牛云,国际版高大上的AZure和AWS都未曾考虑过。

但是呢,微软MVP内部微信群、邮件组,AZure的讨论还是很多的,尤其是“自2017年2月1日起,Azure虚拟机价格大幅下调,最高降价幅度高达60%!”这则消息讨论了好几天,我也渐渐回头持续关注AZure。

通过信息搜集,我了解的AZure信息碎片如下:

Windows Azure在北京、上海部署有服务器

中国的AZure是个”国内版本”–和国际版AZure采用物理隔离方案

MySQL也是比较符合目前国内接地气的方案;

AZure新的特效,国际版和国内版部署时间约差不多是3个月至半年。

https://www.azure.cn/  是国内版本的网站

 

在Azure上搭建Photon服务器(C#.NET)

image  微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET) image thumb2

 

未来AR产品软件框架是这样的,服务器端需要搭建游戏逻辑Photon、以及资源更新AssetBundle–更新资源。MySQL目前是国内很多公司的标配–社区版免费。

 

image  微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET) image thumb3

Photon运行方式很简单,选择服务器实例,启动应用即可,符合GUI操作习惯。

 

image  微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET) image thumb4

AZure虚拟机,提供了C盘,直接把需要的软件和数据,通过远程桌面拷贝过去即可,windows Server 2012英文系统,还是挺方便的。

 

几个有用的小技巧:

  • TT模板:database first–自动生成模型代码

原创文章来源: 使用T4模板生成MySql数据库实体类

注意在VS2015下,需要把.ttinclude后缀修改为.tt才可以。 同时,需要修改 数据库连接为你自己的 9 var connectionString = @"server=127.0.0.1;uid=root;pwd=12345678;charset=utf8;";

AZure开5505 UDP端口

  • 前端、后端C#链接类快捷方式:节约成本的王道

image  微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET) image thumb5

即,直接可以实现前段和后端复用模型实体代码、传递的Code枚举代码,以减少文档重复工作量。

以关键的OperationCode为例,如果前端是C#、后端是Java,则无法做到代码级别复用,即需要写文档来约束。而相同的C#代码后,注释好.cs文件,前端和后端是同样的代码,则沟通起来畅快了很多。

public enum MyOperationCodes : byte
    {
        /// <summary>
        /// LoginOperation
        /// </summary>
        LoginOperation = 100,

        /// <summary>
        /// EchoOperation
        /// </summary>
        EchoOperation = 101,

        /// <summary>
        /// GameOperation
        /// </summary>
        GameOperation = 102,

        /// <summary>
        /// ChatOperation
        /// </summary>
        ChatOperation = 103,

        /// <summary>
        /// ChatOperation
        /// </summary>
        CCUOperation = 104,
    }

  • MySQL ORM (Dapper框架):

    我选择了轻量级、火热的Dapper(stackoverflow网站就用它来做ORM),而且开源。 image  微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET) image thumb6

    使用SQL部分代码如下:

Db aa = DbInit();

 
var a3 = aa.Query<device>("select * from device where uuid=@uuid", new { uuid = "aabb636238694579824398" });

 

device dv = new device
{
     UUID = "aabb" + DateTime.Now.Ticks.ToString(),
     DeviceModel = "IOS",
     DeviceName = "Iphone 5s",
     RegisterDate = DateTime.Now.AddDays(-1),
     UpdateDate = DateTime.Now,
     TokenID = DateTime.Now.Ticks.ToString()
};
long ret = aa.Devices.InsertOrUpdate(dv);

var a1 = aa.Devices.All();

    • Json序列化工具:newtonsoft

    本来Unity5.3已结自带了Json序列化API,但是有bug,Json序列化可能为空,就选择了”老朋友”newtonSoft.dll了。

    • AZure开通端口
    • 应用跑起来后,端口一直不通,需要AZure开通端口–首先在Windows Server里面开通端口(TCP\UDP),然后再AZure里面开通:网络接口–网络安全组–入站安全规则、出站安全规则。

     

     

    PhotonLab 源码:https://git.oschina.net/xifarm/PhotonLab

     

    参考

    1. 时间飞快–连微软官方都开源了208个源码库
    2. 岁月在挑灯夜战中走过:亲历的软件框架
    3. 我的微软.net演进路线图
    4. 荣获2015微软.NET MVP称号!
    5. 写在.NET 15周年之际——致那些仍然爱着.NET的中国程序员们 来自微软MVP Tony Qu微信
    6. .NET 十五岁,谈谈我眼中的.NET 来自微软MVP  张善友的博客》

    Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》

         很高兴,能有机会回报Unity技术社区:我和雨松MOMO担任UUG北京站的负责人, 组织Unity技术交流和分享活动。

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb7

    本次北京UUG活动场地–微软大厦

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb8

    成功的UUG离不开默默无闻的付出:提前2小时到场的志愿者

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb9

    雨松走到那里, 都会有Unity3D 粉丝合影

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb28

    UUG活动正式开始,  雨松首先上台致辞

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb29

    什么是 Unity User Group?

    Unity在西安、南京、厦门等全国所有城市招募UUG领袖啦! 作为Unity User Group的组织者,我们希望你:

    • 热爱Unity, 热爱开发,愿意与更多Unity开发者分享这份热爱。
    • 有号召力,擅长组织线下活动,为本地的Unity开发者提供定期交流的平台。

    根据各地区UUG的活跃情况,Unity将为UUG组织者授予官方的UUG荣誉头衔,并根据UUG运营的实际情况为高质量的UUG活动提供相应的资源与宣传支持。

    如果你认为自己能胜任地区UUG领袖,请将简历以及相关的活动组织经验发送至:uugchina@unity3d.com

    王世元 分享的话题《TVR的游戏研发与设计之路》

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb31

    议题简介:使用Unity开发VR游戏的优势;从《再现甲午》、《星际穿越》、《Ace Banana》、《MIXIP》、《FlndingVR》的简介、游戏风格、游戏交互方式以及VR特性的利用进行经验分享。

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb32

    周日下午, 200人的场地坐的满满的

    王明杨 分享 话题《使用Unity开发一个虚拟影院》

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb33

    议题简介:通过一个移动VR影院的范例介绍Unity中将视频作为Texture的原理,在VR中开发沉浸式观影体验。

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb34

    制作VR虚拟影院的4个步骤

    潘博航 分享 《虚拟现实交互设计》

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb35

    议题简介:从交互设计角度探讨VR内容设计。将阐述Unity开发者目前阶段可能遇到的问题和限制,以实际的案例经验总结探索 VR 内容的交互形式和未来的可能性。

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb36

    大家耳熟能详的的VR消费级输入设备:HTC Vive、Oculus、PSVR

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb37

    《2016,VR元年》:你们是规则的制定者!

    北京UUG合影留言

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb27

    北京站大合照来一张。前排好多美女.

     

    北京UUG部分花絮

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb21

    开心的抽奖环节

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb23

    为你的热情点赞: 专程从天津赶到北京参加UUG活动开发者。

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb24

    Unity官方小礼品–人手一份

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb25

    坐在第一排的某童靴的笔记—好认真啊。

    image  Unity User Group 北京站图文报道:《Unity3D VR游戏与应用开发》 image thumb38

    会后部分讲师和UUG组织者合影,从左到右:潘博航、宣雨松、王明杨、王文刚刘玥

    一个女学生,对Unity特别感兴趣,摘录自她分享的朋友圈:

    “我是个门外汉,Unity刚入门,听起来有点吃力,但是,我觉得今天UUG最大的亮点就是分享的内容比较多比较丰富,特别是最后那个哥们分享了很多听起来很牛的东西,虽然我啥都没听懂。

    但是感觉应该很实用。

    参加UUG,  没错,下次活动还要来。”

    备注:

    Unite ShangHai 2016 (Unity3D开发者大会)

         拜Unite ShangHai 2016 大会所赐,又一次来上海–上次来上海是2007年做TD-SCDMA扫频仪数据接入工作的; 一晃,9年过去了,上海变化挺大的。

     

    上海.印象

    10号起来,在附近溜达溜达,静安公园、蔡元培雕像

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumbimage  Unite ShangHai 2016 (Unity3D开发者大会) image3 thumb

    路过了约3公里上海街区,周日上午都8点半了,马路上人很少且干净,还是上海人小资啊。

     

    晚上和杭州赶来的某著名物联网公司CTO胡键,一起共进晚餐,顺道吹吹牛。

    我们两人4年前是在西安OpenParty认识的,后来一起组织西安的IT活动,如《“西安IT人的社交圈”11月的活动总结》。   近半年,无独有偶,在“万众创业、大众创新”的时代,他从西安出走,带队来杭州专注物联网行业创业; 我则选择离开西安,在北京专注VR。

     

    上海人过马路很讲规矩:通过红外线检测,然后声音警告行人站在台阶上

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb1

    这一点,值得赞,可以极大加速路口车辆通过; 还可以保障安全

     

    上海豫园–城隍庙街区

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb2image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb3

    其中:左侧图片的西洋镜(拉洋片),这个小玩意算是中国最早的VR虚拟眼镜了吧。

     

    Unite 大会(KeyNote)

    上海是周一KeyNote大会,明显人数较去年北京活动少了很多,还是Unite换了新的CEO,控制成本吧。

    参展商也少很多,估计是北京的1/5参展商规模, 且会场外空间相对比较小,不利于排长队进行体验.

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb4

     

    Unity 5.4 VR性能优化增强30%

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb5image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb6

     

    Cinematic Image Effects 电影CG渲染效果—Realtime

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb7

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb8

    这个案例的亮点是连保守的日本marza工作室都用起来Unity做CG动画电影,间接反映出Unity在图像处理上面往AAA级画质发展的雄心和实力。

     

    Unity大中华区核心工作:服务支撑

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb9

    就我个人经验而谈,网速才是服务背后的支撑。如Asset Store下载插件,很慢。

    另外,听说官方也在搞Unity中文版。这个需求,我个人倒不是特别想要–少bug、性能提升更关键。

     

    Unity Labs:Unity法国的精英团队。

    image  Unite ShangHai 2016 (Unity3D开发者大会) image thumb10

    最近在聚焦VR中设计VR场景(VREditor):人带上VR头盔,用Touch手柄进行VR场景编辑。

     

    VR(虚拟现实)火热依旧–2016

    4.10号  VR有一天时间培训课,400+人参加

    4.12号 全天有VR重点技术课程,2000+人参加

    而整个Unite 大会才3天,从时间上看,2/3在谈论关于VR的技术和案例,从北京到上海,都是一个字:火。

    去年大会的主发言人–Unity 大中华区boss 符国新(Allan),去年开完Unite北京大会,就立马离职创业了,选择就是VR方向,呵呵。

     

    2015年参加Unite 北京大会,启蒙了我的VR认知

     

    image  Unite ShangHai 2016 (Unity3D开发者大会) image18 thumb

    2015.4 我首次听到关于VR、AR的演讲—原Unity亚太技术总监郭振平先生激情演讲

     

    image  Unite ShangHai 2016 (Unity3D开发者大会) image11 thumb

    2015.4 雨松MOMO粉丝们合影; 雨松左侧妹子是其女徒弟—小七不乖(现任Unity官方社区运营官)。

     

    image  Unite ShangHai 2016 (Unity3D开发者大会) image15 thumb

    2015.4 非常高兴的和Unity创始人David Helgason 合影留念

     

    小结:

    1. Unity开发者大会,对推动Unity技术在中国蓬勃发展功不可没,2012年Unite入华,目前已经5年了。
    2. VR技术的普及,因为Unity引擎在手游行业培养海量人才,故才得以爆发。
    3. 目前国内,VR、AR技术工作,80%的人选择的是Unity的引擎,人才基数多、迭代快。
    4. Unity 5.4 看起来不错,在画质渲染上,VR效率上,比较期待正式版发布。
    5. 相对上海精细小资,我更喜欢北京的大气浑厚:VR创业公司,北京占全国60%
    6. 拿着Android手机,出门佩戴充电宝是必须的,要不然高能量的PPT不拍照记不住。
    7. 参加开发者大会,不仅仅是听讲座,和网友见面也是必须的
    8. Unity官方今年在中国会大力推线下社区,加大走群众路线

    参考:

    去年大会博客链接: 《Unite Beijing 2015大型活动

    Unity3D for VR 学习(8): Unity Shader概述

       从西安到北京高铁上,一位VR老外团队的华人leader对VR技术做了画龙点睛:

    “3D游戏的核心部分在Render, 国内很多团队美术、程序中间缺失严重。所以3d游戏做不好。 VR这块更是至关重要。”

     

    故,欲VR,先Shader吧。

     

    1 CG规范: 计算机图形学(英语:computer graphics

    在Windows下图像渲染是DirectX; 在Linux下图形渲染是OpenGL;在苹果下新出的Metal渲染。而作为Unity3D程序,跨平台的特性则无须对这些平台一一掌握,仅需要从CG规范入手即可。

    image  Unity3D for VR 学习(8): Unity Shader概述 image thumb9

     

    CGPROGRAM…ENDCG这样一个代码块中,这是一段CG程序,它会帮助程序发布Unity3D到Android、Windows、IOS时候,转换为不同渲染底层功能的。

     

    2 Unity3D Shader分类

    • 表面着色器(Surface Shader) – Unity推荐的Shader,有光照需要的必须使用surface Shader。
    • 片段着色器(Fragment Shader) – 更偏向底层一些,也比较难写。
    • 固定管线着色器(Fixed Function Shader) – 目前废弃不用,仅针对老显卡。Vr时代用不着。
      从上面看到Unity实际上在主推Standard Surface Shader,在Unity5下,提供了如下4中Shader模板。

    image  Unity3D for VR 学习(8): Unity Shader概述 image thumb10

     

     

    渲染管线:从数据到最终成像的过程。

    image  Unity3D for VR 学习(8): Unity Shader概述 image thumb11

     

    3 Unity3D 光照模型

    光照模型想说明的事情是当光照到一个物体表面后,视角不同看到不同的光特效,如常规的漫反射、高光、镜面反射光等。 实际上也是在模拟真实情况下人看物体的视角效果,同样的3D模型,添加不同的光照模型效果会截然不同。

    如果物体是不透明的,则物体表面呈现的颜色仅有其反射光决定,通常把反射光考虑成环境反射光(Ambient Light)漫反射光(DIffuse Light)镜面反射光(Specular Light)三个分量的组合。

     

    4 Unity3D shader Tips

    C#编码是在CPU角度;而Shader编码是在GPU角度。

    看了几天书后,发现Shader真的很酷,可以做很多CPU下编程做不到(很难)的事情。

    例如半透明、UV动画、遮挡剔除、色彩斑斓的blend等。

    • uv动画:在shader中用uv缩放、uv偏转、uv旋转。如河流流水动画、UV精灵动画。
    • 遮挡剔除:cull back\front\off. 其中VR 360就用到了《Unity3D for VR 学习(7): 360°全景照片
    • 半透明–ZTest:用在自己GameObject上
    • 半透明—ZWriter:用在遮挡物GameObject上
    • AlphaTest(DX 11不支持)  半透明的花草树木效果
    • Blender混合:如透过有颜色的玻璃,可以实现色彩斑斓的透视效果
    • Fog:全局fog、局部fog

    5 Unity3D Shader 代码片段

    SurfaceOutput结构体的定义如下

    struct SurfaceOutput {

    half3 Albedo;     //像素的颜色

    half3 Normal;     //像素的法向值

    half3 Emission;   //像素的发散颜色

    half Specular;    //像素的镜面高光

    half Gloss;       //像素的发光强度

    half Alpha;       //像素的透明度

    };

     

    表面Shader代码片段

    Shader "Custom/Diffuse Texture" {

    Properties {

    _MainTex ("Base (RGB)", 2D) = "white" {}

    }

    SubShader {

    Tags { "RenderType"="Opaque" }

    LOD 200

    CGPROGRAM

    #pragma surface surf Lambert

    sampler2D _MainTex;

    struct Input {

    float2 uv_MainTex;

    };

    void surf (Input IN, inout SurfaceOutput o) {

    half4 c = tex2D (_MainTex, IN.uv_MainTex);

    o.Albedo = c.rgb;

    o.Alpha = c.a;

    }

    ENDCG

    }

    FallBack "Diffuse"

    }

     

    CG语法,可参看《GPU 编程与CG 语言之阳春白雪下里巴人.pdf

    Unity3D for VR 学习(7): 360°全景照片

        在VR应用中,有一个相对简单的虚拟现实体验,那就是360°全景照片浏览器, 他可以使得手机拍照的”全景”照片,  得以”恢复”当时拍照的场景全貌,  这个创意的确比单纯的2d图片更有震撼力一些,故本文就制作360全景照片的过程进行简单小结。

    image  Unity3D for VR 学习(7): 360&deg;全景照片 image thumb7

     

    Step 1:预期需求

    可以通过手机的陀螺仪,在魔镜中查看360°图片;不用依赖任何蓝牙输入,仅通过Gaze Input进行选择下一张360°图片(盯着缩放图2s,进度条旋转完成后,就切换图片;用Gear VR的磁铁触发功能也可以实现选择下一张图片)。

     

    Step 2: 准备材料

    硬件:暴风魔镜4  + 小米Note顶配版(2K屏幕)

    SDK:魔镜手柄、反畸变SDK_Unity版,或者 Google cardboard-unity,本文使用的是Googe CardBoard

     

    Step 3: 创建材质球

    360°照片,展开看就是一个投影的2D照片,这个在初中学习世界地理的时候将类似投影球形地图到2维平面上,原理类似,只不过一个360°照片的投影失真会小很多. 

    为了复原,需要建立一个3D Object\Sphere, 我们通过把图片贴到这个球体上,从内侧看效果。 然后拖放照片到Sphere上,球体放大10倍; 这个时候,发现一个问题在Game视图中看不到东西了,因为Camera没有在球心位置,修改Camera坐标为(0,0,0)位于球心。

    我们依然看不到图片,因为Spere默认用的是Unlit/Transparent Shade,他为了渲染高效,把背面给剔除了,而在这里,我们洽洽需要渲染背面,故得从新写个shade–关闭cull off。

    image  Unity3D for VR 学习(7): 360&deg;全景照片 image thumb8

    DoubleSided.Shader文件

    Shader "Unlit/DoubleSided"
    {
        Properties
        {
            _Color("Main Color", Color) = (1,1,1,1)
            _MainTex("Texture", 2D) = "white" {}
        }
            SubShader
        {
            //Ambient pass
            Pass
            {
                Name "BASE"
                Tags {"LightMode" = "Always" /* Upgrade NOTE: changed from PixelOrNone to Always */}
                Color[_PPLAmbient]
                SetTexture[_BumpMap]
                {
                    constantColor(.5,.5,.5)
                    combine constant lerp(texture) previous
                }
            SetTexture[_MainTex]
        {
            constantColor[_Color]
            Combine texture * previous DOUBLE, texture *constant
        }

            }

            //Vertex lights
            Pass{
            Name "BASE"
            Tags {"LightMode" = "Vertex"}
            Material
        {
            Diffuse[_Color]
            Emission[_PPLAmbient]
            Shininess[_Shininess]
            Specular[_SpecColor]
        }

            SeparateSpecular On
            Lighting On
            cull off
            SetTexture[_BumpMap]
            {
                constantColor(.5,.5,.5)
                combine constant lerp(texture) previous
            }
            SetTexture[_MainTex]
            {
                Combine texture *previous DOUBLE, texture *primary
            }
        }

        }

            FallBack "Diffuse", 1
    }

     

    替换了新的Shade后,效果好了很多了,唯一有问题的是我们看到的照片是反的,故修改Spere的.Z为-10.

    image  Unity3D for VR 学习(7): 360&deg;全景照片 image thumb9

     

    通过在Unity.Editor上Play效果,已经很不错了,至此完成了大部分的工作,下面开始”移植”到VR设备上

     

    Step 4: 添加VR Camera

    先禁用掉默认的MainCamera、light。

    然后添加VR摄像机—导入Google的CardboardSDKForUnity.unitypackage后,拖入CardboardMain.prefab到场景中。 这时候点击Play启动UnityEditor,即可看到双VR Camera的效果,点击调试快捷键:Alt + 移动鼠标,即可看到模拟的陀螺仪效果。

    这个时候打包为apk,安装到手机,用真机的陀螺仪体验,效果更酷。

     

    Step 5: 添加Gaze-and-Tap 用户界面

    完成《Step 4: 添加VR Camera》后,实际上已经完成了360°全景照片功能,但是这个仅仅是一个demo,有一个致命的痛点:只有1张照片,如果我有4张照片,每次独立打包为一个apk,这显然不合理。 故需要做一些VR交互。

    这里,我们添加4个Plane,通过上面章节的《Unity3D for VR 学习(5): VR Gaze Input》,期望可以实现通过Gaze一个Plane 3s,然后能够切换新的照片功能。

    添加Plane,坐标修改如下:

    image  Unity3D for VR 学习(7): 360&deg;全景照片 image thumb10

    添加一个脚本,实现响应EventSystem,可通过2种办法

    方法1: 添加Event Trigger脚本,响应PointerEnter、PointerExit等方法,如图

    image  Unity3D for VR 学习(7): 360&deg;全景照片 image thumb11

    方法2:脚本直接继承IPointerDownHandler、IPointerExitHandler等接口,这个看起来更符合程序员胃口

     

    我们通过控制Plan的Color.a来实现高亮选择,代码如下

    public void SetGazeAt(bool gazeAt)
        {
            Debug.Log("SetGazeAt: " + gazeAt);
            Renderer render = this.GetComponent<Renderer>();
            Color cor = render.material.color;
            cor.a = gazeAt ? 1f : 0.5f;
            render.material.color = cor;
        }

     

    另外,当Gaze 2s后,会动态替换material的Texture来实现替换360°照片,代码如下

    public void OnClick()
    {
        Renderer renderSrc = this.GetComponent<Renderer>();
        Renderer renderDst = pano.GetComponent<Renderer>();
        Texture tex = renderSrc.materials[0].GetTexture("_MainTex");
        renderDst.materials[0].SetTexture("_MainTex", tex);
    }

     

    备注:

    360°全景照片,非常大,有好几M,这个对手机app来说会造成文件太大,实际商用中,需要采用AB(AssetBundle)实现动态从网络侧更新360图片,那样会更酷一些(轻量级)。

     

    本文用到的完整的 demo Git地址: Git.OSChina.net

     

    昨天网上一个朋友问了一个VR问题:

    可否像3D Max那样,动态替换材质,如替换房间地板材质,实现全景照片替换?

    回答:不行,只能动态替换整个的全景照片;我昨晚细细思索,好像还是不行,除非建立3D模型,采用动态换装才能实现动态换360°照片中的大树、地板吧。

    Unity3D for VR 学习(6): 再次温故知新-3D数学

       一年前,系统学习过3D数学,并记录了一篇博客《C#程序员整理的Unity 3D笔记(十):Unity3D的位移、旋转的3D数学模型》。 一年后,再次温习之。

    坐标系:Unity3D使用左手笛卡尔坐标系(Descartes coordinate system)

    • 世界坐标系(world space):在一个游戏场景中,唯一。
    • 物体坐标系\局部坐标系(local\Object space):每个物体有各自的独立的坐标系。如桌子的物体坐标系中,扶手相对桌子腿位置。有时候,不需要对外暴漏太多细节。
    • 摄像机坐标系: 特殊的物体坐标系,用于定义物体在摄像机视野范围内,即那些物体会被摄像机绘制出来。
    • 惯性坐标系 (Intertial):一个“临时”坐标系,为了方便从世界坐标系到物体坐标系的转换,引入的新坐标系。原点和物体坐标系重合,坐标轴平行于世界坐标系。

    image  Unity3D for VR 学习(6): 再次温故知新-3D数学 image thumb4

     

     

    Unity3D提供的有用的坐标系转换工具:

    1 RectTransformUtility

    Vector2 WorldToScreenPoint(Camera cam, Vector3 worldPoint)

    Ray ScreenPointToRay(Camera cam, Vector2 screenPos)

     

    2 《Unity3D项目实战笔记(1):prefab的插件方式

    下面代码可以使得:a.prefab被加载到了A的子节点下面,要使得a显示b点位置,一个办法是把a挂接到B下; 另外一个办法是用transform提供的2个方法,转换局部坐标为世界坐标,实现动态定位。

    Vector3 pos = A.transform.TransformPoint(a.localPostion);

    b.localpostion = B.transform.InverseTransformPoint(pos);

     

    向量(Vector):  有大小和方向,没有位置

    任意一点,都可以从原点开始用向量来表达,这个也是点和向量非常容易为初学者搞混的地方。同时,因为向量和位置无关,故向量可以在坐标系中任何地方使用。

    • 向量数乘,添加通过标量和向量相乘来实现力的系数调节
    • 向量标准化,大小为1的向量。亦称”法线”
    • 负向量,和原向量大小相等,方向相反的向量
    • 向量加法,向量a和向量b收尾向量,从a的尾到b的头的向量。
    • 向量减法,雷同向量加法。
    • 向量点乘(内积),结果为标量,描述了两个向量的”相似”程度,点乘结果越大,向量越接近。

    a.b >0: [0, 90), 方向相同

    a.b = 0 :90°   向量正交

    a.b<0: (90,180] 方向相反

    • 向量叉乘(叉积), 结果为一个垂直于原来的两个向量。

    矩阵(Matrix), 这里主要是2*2, 3*3, 4*4方阵

    DirectX使用的是行向量,Ope6nGL使用列向量。方阵的行可被解释为基向量

    线性变换保持直线和平行线,原点未移动;包含平移的变换称作仿射变换。

    变换物体和变换坐标系区别:变换物体,如旋转20°,意味着物体上所有的点都需要进行重新计算,被移动到新的位置。而旋转坐标系时,物体上的点实际未移动,只是在另外一个坐标系中描述他的位置而已;有时候使用变换坐标系,可以节约底层计算量。(例如碰撞检测,需要涉及的物体在同坐标系中)

    线性变换,不会导致平移(原点位置不会发生改变);放射变换在线性基础上接着进行平移。

    矩阵的行列式,结果为一个标量。

    正交矩阵,如果矩阵是正交的,则与它的转置矩阵相乘,结果为单位矩阵。同时,也可知正交矩阵的转置矩阵为逆矩阵。

    正交投影(降维操作),也称作平行投影。3D投影到2D屏幕上,该平面称作投影平面。

    透视投影,投影线不再平行而是相交于一点–投影中心。类似“小孔成像”,投影是倒着的。

    欧拉角,使用三个角度来保存方位:heading,Y轴,(-180,180);pitch, X轴, (-90, 90); bank, Z轴,(-180,180).

    四元数(Quation),用4个数字表达方位,避免了欧拉角的”万向锁”、Slerp球形差值问题。

     

    参考书籍:

    赞一下,暴风魔镜4 + 小米Note顶配2K屏,具有里程碑意义,能连续玩VR 20分钟以上

    分辨率好、陀螺仪好。

    相比之下,千元机魅蓝Note 2就是个渣渣:远离千元机,如果你玩VR的话。

    VR未来,或许能圆一个小小的遗憾:5年前曾多次出差哈尔滨,刚好有冰雪大世界,心里还是蛮想看的,但是因为畏惧寒冷(-35°的长时间户外活动),故未能出行。如果能通过VR远程看,则再好不过了,呵呵。

    Unity3D for VR 学习(5): VR Gaze Input

          在VR中,最“贴切”的输入方式是眼神,即 VR Gaze Input,如盯着某UGUI UI、某GameObject,2s后触发事件–  显示ToolTip或者切换场景等。 因为这不需要蓝牙手柄输入,显得更自然且方便,风靡VR Gear的Lamper VR游戏,就是Gaze Input(陀螺仪)的典范—可顺利完成游戏的全部过程,的确很酷。

    image  Unity3D for VR 学习(5): VR Gaze Input image thumb6

     

    UGUI UI Gaze 选择

    1 GazeInputModule(继承自PointerInputModule),挂到EventSystem实现。

    2 通过输入按钮触发选择事件;或者Gaze 2s,触发选择事件。

       这里使用了raycasting,故要求Canvas是Word坐标系,且要求有raphic Raycaster组件,并挂上Center Eye Camera(其他L、R VR Eye未测试)。

    3 EventSystem上,disable StandaloneInputModule & TouchInputModule。

    4 会触发IPointEnter事件,通过查看Button的源码知道会触发Button.Hightlight高亮

     

    GameObject Gaze 选择

    5 在上面UGUI的基础上,给Camera添加PhysicsRaycaster组件

    6 给GameObject,如Cube添加相应EventTriger:通过添加EventTrigger组件或者一个继承IPointDown**的脚本,来响应对应的Event即可。

     

    备注:在UGUI开源工程中,有很多源码值得解读

    如:ExecuteEvents.cs,核心代码片段(IEventSystemHandler):

    public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor) where T : IEventSystemHandler
            {
                var internalHandlers = s_HandlerListPool.Get();
                GetEventList<T>(target, internalHandlers);
                //    if (s_InternalHandlers.Count > 0)
                //        Debug.Log("Executinng " + typeof (T) + " on " + target);

                for (var i = 0; i < internalHandlers.Count; i++)
                {
                    T arg;
                    try
                    {
                        arg = (T)internalHandlers[i];
                    }
                    catch (Exception e)
                    {
                        var temp = internalHandlers[i];
                        Debug.LogException(new Exception(string.Format("Type {0} expected {1} received.", typeof(T).Name, temp.GetType().Name), e));
                        continue;
                    }

                    try
                    {
                        functor(arg, eventData);
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e);
                    }
                }

                var handlerCount = internalHandlers.Count;
                s_HandlerListPool.Release(internalHandlers);
                return handlerCount > 0;
            }

     

    如:PhysicsRaycaster.cs ,核心代码片段(Physics.RaycastAll):

    public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
            {
                if (eventCamera == null)
                    return;

                var ray = eventCamera.ScreenPointToRay(eventData.position);
                float dist = eventCamera.farClipPlane – eventCamera.nearClipPlane;

                var hits = Physics.RaycastAll(ray, dist, finalEventMask);

                if (hits.Length > 1)
                    System.Array.Sort(hits, (r1, r2) => r1.distance.CompareTo(r2.distance));

                if (hits.Length != 0)
                {
                    for (int b = 0, bmax = hits.Length; b < bmax; ++b)
                    {
                        var result = new RaycastResult
                        {
                            gameObject = hits[b].collider.gameObject,
                            module = this,
                            distance = hits[b].distance,
                            worldPosition = hits[b].point,
                            worldNormal = hits[b].normal,
                            screenPosition = eventData.position,
                            index = resultAppendList.Count,
                            sortingLayer = 0,
                            sortingOrder = 0
                        };
                        resultAppendList.Add(result);
                    }
                }
            }

     

    源码下载地址:http://pan.baidu.com/s/1hqYX2Ry 

    【按住Alt + 鼠标。 到Cube上后,2s触发“Selected”事件】

     

    参考英文:Gaze Fuse Buttons in Unity

    Unity3D for VR 学习(4): 自绘摄像机的视口区域锥体

     

         在Unity Editor下,当选择Camera组件后,可呈现出Camera视口区域锥体,非常方便。但是当选择其他物体,如Cube后,就无法得知是否在Camera市口区内了,这里我找到了雨松MOMO的一篇博客《Unity3D研究院之获取摄像机的视口区域》,他用Camera.fieldOfView和Camera.aspect算出屏幕比例,然后再得出width、height(摄像机)绘制了四边形,非常酷,可以解决这个痛点。

      我在巨人肩膀上,做了一些拓展:

    自动获得Camera的farClipPlane和nearClipPlane

    void Start()
        {
            if (!theCamera)
            {
                theCamera = this.GetComponent<Camera>();
            }

            upperDistance = theCamera.farClipPlane;
            lowerDistance = theCamera.nearClipPlane;
            tx = theCamera.transform;
        }

     

    连线far和near ClipPlane

    void FindLower2UpperCorners()
    {
        Vector3[] corners_upper = GetCorners(upperDistance);
        Vector3[] corners_lower = GetCorners(lowerDistance);

        Debug.DrawLine(corners_lower[0], corners_upper[0], Color.blue);
        Debug.DrawLine(corners_lower[1], corners_upper[1], Color.blue);
        Debug.DrawLine(corners_lower[2], corners_upper[2], Color.blue);
        Debug.DrawLine(corners_lower[3], corners_upper[3], Color.blue);
    }

     

    挂接这个CameraViewEx.cs脚本到Camera组件同GameObject即可, 运行时有效:

    image  Unity3D for VR 学习(4): 自绘摄像机的视口区域锥体 image thumb5

     

    源码下载地址:http://git.oschina.net/xifarm/VR_Mojing/

     

    备注:

    不用这个脚本,直接在Game视图,打开Gizmos也可以看到Unity自带的Camera视口区域锥体。

    Unity3D for VR 学习(3): 暴风魔镜PC Input小改造–自己动手、丰衣足食

          在做手游的时候,80%时间是在PC调试的,例如业务逻辑、AI算法、核心玩法等。

    拿到魔镜提供的demo,晕了,必须得安装到Android机器上,才能调试,究其原因,有三:

    1. 需要用到手机陀螺仪
    2. 需要用到蓝牙手柄
    3. 需要用到魔镜的凸透镜

    这些都是客观原因,但是每次打包后,安装Android,再戴到头上,Debug、log啥的都看不清,呵呵。

    故就着手改造一下输入快捷键,从而可以方便的模拟手机陀螺仪输入、模拟蓝牙输入、模拟非VR Mode切换。

     

    1 模拟手机陀螺仪输入

    这个在mojing.cs有现成隐藏代码,在UpdateState()函数中。

    • 快捷键F: ok键
    • 快捷键Alt:按住,鼠标模拟GazePostion位移
    • 快捷键Ctrl:按住,鼠标模拟GazePostion旋转

    2 模拟蓝牙输入

    在学习了MojingInputManager.OnButtonDown\OnButtonUp后,知道只要传递过去string即可,其中用/分隔,第一位暂时不用,第二位用MojingKeyCode预定定义的数字。

    • 快捷键WASD:模拟蓝牙
    • 快捷键Q:模拟蓝牙OK

    3 模拟非VR Mode切换

    VR Mode切换,是通过设置Mojing.SDK.VRModeEnabled实现的,原理是对Left、Right Camera和Center Camera可见性进行隐藏或者显示实现切换,找到了Demo.cs脚本中,已经封装好了切换和返回主菜单函数,调用即可:GameObject.FindObjectOfType<Demo>().ToggleVRMode();

    • 快捷键R:模拟切换VR Mode
    • 快捷键B:模拟回到主菜单

     

    借用网络一句话:VR开发,比App开发,多一个Camera。 搭建好调试环境,就会好很多。

     

    源码下载:MojingInputManagerEditor.cs

    使用:在MojingInputManager.prefab添加MojingInputManagerEditor脚本,然后点击Apply即可。

    image  Unity3D for VR 学习(3):  暴风魔镜PC Input小改造&#8211;自己动手、丰衣足食 image thumb3

    Unity3D for VR 学习(2): 暴风魔镜框架探索

    学习一个新技术,有三个法宝:

    故,学习魔镜4技术,亦如是也。

    image  Unity3D for VR 学习(2):  暴风魔镜框架探索 image thumb

     

    暴风魔镜4是 硬件 + 暴风VR SDK软件的CardBoard VR解决方案

    硬件

    魔镜4硬件提供了展示容器工具,主要由2个凸透镜和塑料头盔构成。故为了看到效果酷炫的VR效果,必须要依赖5寸以上Android手机–720P以上,把通过暴风魔镜SDK做的App放入里面,通过暴风魔镜凸透镜才能看到VR的效果–这种Cardboard方式VR,是基于2年前Google CardBoard“启发”的。

    参考:暴风魔镜4适配机型列表(2015.12.2更新)

    网上也有说几十元,可以自己组装Google CardBoard的,不过对于DIY硬件,我没有太大动力,100多元钱,买个现成的暴风魔镜 4,非常好了。

     

    暴风SDK软件

    SDK软件,才是Unity3D程序员重点关注的。

    image  Unity3D for VR 学习(2):  暴风魔镜框架探索 image thumb1

    这张图,是魔镜提供的360度图片的Unity3D层次截图,这里我们可以看出部分SDK软件框架:

    1. UI:  这里主要采用UGUI,做了一个Button,返回主界面。 在VR中,UGUI 的3D UI效果就立显了,还有就是UGUI是Unity原生的,效果又不错,故做UI选择UGUI是大趋势。
    2. MojingMainMojingVRHead:负责陀螺仪数据接收,水平坐标x和垂直坐标y数据。
    3. MojingInputManager:负责手机蓝牙的输入控制,直接拿来复用即可,魔镜出厂适配了魔镜蓝牙和小米蓝牙控制器,一般而言,目前蓝牙手柄是比较常见的外设输入,可能随着Oculus Touch面世会升华。
    4. GazePointer\Pose3D:眼睛目视前方的小黄点。这个是VR应用中一个很酷的实践,当你没有蓝牙控制器,可通过这个轨迹点,实现功能选择。通过代码中,看到很多流利的英文注释,我搜索了一下github,原来当年google CardBoard代码,一模一样嘛,呵呵。
    5. MojingEye: 一个VR应用,要用到2个Camera,分别是Left、Right,类似人的左右眼。

    ———————————————————————————————————

    VR的常见Q&A

    1 PC和APP的应用能否复用?

    答复:不能复用。app应用是手机上的,PC用的*.exe的,不能复用。 如果是3D片源则可以。

    2 近视镜问题?

    答复:暴风魔镜4,提供的空间挺大的,我600度近视,带着眼镜,没有问题。且说,Oculus Rift提供了3组镜片,对于<=400度近视的,完全没有问题的。

    3 伤眼睛问题?

    答复:这个是我最担心的,不过试了几天,发现貌似不伤眼睛,网上查了一下—

    让人眼感受到具有深度信息的持续的3D渲染是虚拟现实最重要的部分。

    第一,为了产生深度信息,它给每个眼睛生成一张图片,这两张图片在视觉上有一点点偏移量,这样就可以模拟人眼的视差,所谓视差就是人脑处理在不同位置看到的物体而产生的深度信息。第二,产生更好的视觉效果,它将图片扭曲从而模拟人眼的球形表面,通过桶形畸变技术可以达到这个效果。

     

    ==> 在使用暴风魔镜4的时候,你的眼睛其实在任何时候都在观看远景。

    4 市场成熟度问题?

    答复:目前技术限制,依然是成熟前期,如果技术突破了:2k+屏幕、60FPS+、<20ms这3个要素,且VR价格大幅度降低,则是VR内容突飞猛进的市场阶段–或许2016Q3后会有不错的市场。

    5 用什么来开发VR产品?

    答复:主要是Unity3D,UE4,而其中Unity3D占据了70%以上,据说。 VR为了牛X在PC VR,但是初期暴风魔镜100多元的价格,做手机版App,正是Unity3D的长项也。

    6 图像抗镜片畸变?

    答复:畸变的英文单词distortion, 物体上的直线经过透镜成像后变成弯曲的现象。畸变是由于透镜的放大率随光束和主轴间所成角度改变而引起。 详细看百科:畸变.

     

    我在网上查了一些,新出的一本《Learning Virtual Reality》书,PDF电子版链接:http://pan.baidu.com/s/1c1lP64C

    image  Unity3D for VR 学习(2):  暴风魔镜框架探索 image thumb2

     

    搞了好久,终于弄明白,暴风魔镜和暴风影音是一伙的,非常好,尤其是大厂能与时俱进的,佩服!

    再次复习一下Unity3D的3D数学,我理解的,这个在VR技术,应用会很多:

    C#程序员整理的Unity 3D笔记(十):Unity3D的位移、旋转的3D数学模型