Unity3D for VR 学习(9): Unity Shader 光照模型 (illumination model)

关于光照模型

所谓模型,一般是由学术算法发起, 经过大量实际数据验证而成的可靠公式

现在还记得2009年做TD-SCDMA移动通信算法的时候,曾经看过自由空间传播模型(Free space propagation Model),目的为了得出移动信号的传播损耗。当时是基于普通的PC实时运算,非常非常耗时–如北京五环内的传播模型渲染GIS图用了超过20分钟。

 

光照模型来源有2类:

  • 一类是基于学术论文的算法,如Lambert模型、Phong模型.
  • 另一类基于算法的变种–在实际生产实践中修正得到的模型,如HalfLambert模型、BlinnPhong模型.

 

光照模型公式:

surfaceColor = emissive + ambient + diffuse + specular

          物体表面的颜色是自发光(emissive)、环境反射(ambient)、漫反射(diffuse)和镜面反射(specular)等光照作用的总和。   每种光照作用取决于表面材质性质(例如亮度和材质颜色)和光源的性质(例如光的位置和颜色)。

一般而言,光照模型只考虑漫反射镜面反射

 

光照模型分类

当光照射到物体表面时,一部分被物体表面吸收,另一部分被反射,对于透明物体而言,还有一部分光穿过透明体,产生透射光。被物体吸收的光能转化为热量,只有反射光和透射光能够进入眼睛,产生视觉效果(物体呈现的亮度和颜色)。   所以,物体表面光照颜色由入射光、物体材质,以及材质和光的交互规律共同决定。

另外,光与物体最基本的交互方式就是反射,遵循反射定律:反射光与入射光位于表面法向两侧,对理想反射面(如镜面),入射角等于反射角,观察者只能在表面法向的反射方向一侧才能看到反射光

 

image  Unity3D for VR 学习(9): Unity Shader 光照模型 (illumination model) image thumb

 

Unity内置光照模型

在Unity表面着色器(Surface Shader) ,Unity内置了2个光照模型,对于大部分场景选择使用即可。

源码路径:C:\Program Files\Unity\Editor\Data\CGIncludes\Lighting.cginc

Lambert 漫反射

fixed4 LightingLambert (SurfaceOutput s, UnityGI gi)

Unity3D for VR 学习(9): Unity Shader 光照模型 (illumination model) 610439 20160215014739767 915631471

     粗糙的物体表面向各个方向等强度地反射光,这种等同地向各个方向散射的现象称为光的漫反射(diffuse reflection)。   产生光的漫反射现象的物体表面称为理想漫反射体,也称为朗伯(Lambert)反射体。Lambert 模型较好地表现了粗糙表面上的光照现象,如石灰粉刷的墙壁、纸张、黑板等。

 

BlinnPhong 镜面反射 (Jim Blinn 1977)

它是以Phong模型为基础的,效果是能让高光更加柔和,更平滑;但是真实感还没Phong模型强,主要是这个模型运算速度要快,即运算效率高。

fixed4 LightingBlinnPhong (SurfaceOutput s, half3 viewDir, UnityGI gi)

Unity3D for VR 学习(9): Unity Shader 光照模型 (illumination model) 610439 20160215014809751 2110682232

一个光滑物体被光照射时, 可以在某个方向上看到很强的反射光,这是因为在接近镜面反射角的一个区域
内,反射了入射光的全部或绝大部分光强,该现象称为镜面反射。诸如金属材质的反射光泽。  镜面反射认为光强与反射光线和视线的夹角相关。

其中在OpenGL 和Direct3D 渲染管线中,Blinn-Phong 就是默认的渲染模型。

 

一些光照模型概念:

  • Fresnel菲涅尔反射:在观察水面的时候,垂直看下去,清澈见底,看远处的水面,向镜子一样,这个就是菲涅尔效应。菲涅尔效应可以说是无处不在,不同的材质效果不同而已。
  • 镜面反射的颜色通常是材料的颜色而不是光源的颜色。
  • 塑料使用白色的镜面高光,漫反射成分大而镜面反射成分小。
  • 金属的反射一般发生在表面,漫反射分量很小,有时甚至可以忽略,粗糙度的值m也很小。
  • Cook-Torrance 模型: 用模型模拟了金属和塑料材质,考虑到了入射角变化时发生的颜色偏移。
  • Bank BRDF光照模型:BRDF就是双向反射分布函数的意思,它描述了入射光线在某个反射角度的反射光的相对能量。所以给定不同的BRDF函数,就能实现不同的光照效果,如各向异性光照效果–金属表面拉丝等效果。

参考文档:

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

2016年VR的大事件: Vision VR/AR Summit 2016 大会

     正月初五,刚从老家回到西安,打开TWitter,就看到铺天盖地的 #vision summit 2016# 大会的消息。

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb

我细细过了一遍,发现,这个对于2016年的VR而言,非常具有里程碑意义.

 

下面, 我摘录出几个亮点供大家参考:

#vision summit 2016#大会 由Unity公司主办,2016年2月10~11日,美国加州

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb1

 

参会的涵盖了国外的主要的VR、AR平台厂商。

如VR的Oculus、Sony、Steam VR、Google CardBoard。

如AR厂商Microsoft Hololens、Vuforia、Google Tango。

 

其中主要是以Unity为主游戏引擎的生态圈,融合了国际一流大厂们.

Vision summit 2016从2015年中就启动报名和运作的, 是有”预谋”的一次峰会.

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb2

这张曲线图,和目前国内的预测大概一致:  2016~2020年, VR会以直线速度增长; AR增长相对平缓,大约3年后的2018年才会大幅度增长–随着微软的HoloLens问世吧.

 

90%的Gear VR虚拟现实项目是基于Unity开发

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb3

 

Oculus VR的创始人Palmer Luckey: Gear VR中约90%的虚拟现实内容是基于Unity引擎开发的。

目前移动游戏市场的主要开发引擎为Unity,故这个结果也合情合理–人才有了,新技术才会比较好的普及。

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb4

 

Oculus跟Unity还宣布了一项让所有Rift用户, 都可以获得引擎Unity Pro版本四个月试用期限的合作

75美金/月 * 4月 = 300美金

实际上, Unity Person版本开发VR没有任何障碍, 故此举仅仅是市场噱头,而已.

 

Unity将支持VR场景内开发

 

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb5

Unreal Engine 4内容制作VR引擎,1周前的新闻,在VR圈掀起小高潮; 短短1周过后,Unity就以这个短短5分钟的视频做了回应: 我们也在搞呢.  相比低效的VR开发工作流程,在虚拟现实中开发VR能让程序员们实时了解程序运行结果,应该会极大提高工作效率。

不过,从演示看,主要是场景布局和搭建,对于写脚本为主的VR程序而已,貌似用处不大.

 

Unity原生支持Cardboard

而不再需要任何插件,这个有利于统一目前琳琅满目的CardBoard VR市场,尤其是国内,*** SDK, 均源自Google CardBoard SDK源码.  

有利于减少VR程序员的学习成本,减低VR内容制作的成本.

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb6

前不久的新闻,ID大神Carmack说,他要投入主要精力负责Mobile VR的位置跟踪研发。

 

Unity原生支持SteamVR

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb7

老板一高兴,就发硬件了: 美国电子游戏厂商Valve的CEO Gabe Newell宣布说所有参加了大会的开发者都将免费获赠一套HTC Vive Pre开发者套装 (HTC Vive Pre的价值约500美元+)

 

Oculus为Unity发布口型和声音协调同步插件

Oculus VR为虚拟现实内容制作工具Unity 5发布了一款叫Oculus OVRLipSync的插件,允许开发者将化身的嘴唇运动跟声音同步起来,让虚拟现实体验更真实自然。

杜绝假唱!

 

image  2016年VR的大事件: Vision VR/AR Summit 2016 大会 image thumb8

 

大会网址:http://visionsummit2016.com/

 

备注:

上面Unity宣布的一些功能,预计最快2016.3 Unity 5.4版本会部分开始支持。

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

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

image  Unity3D for VR 学习(7): 360°全景照片 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°全景照片 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°全景照片 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°全景照片 image thumb10

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

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

image  Unity3D for VR 学习(7): 360°全景照片 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数学模型

Unity3D for VR 学习(1): 又一个新玩具 暴风魔镜 4(Android)

  2016年伊始,有了VR虚拟现实硬件设备:  暴风魔镜4–好奇者的新玩具 .

image  Unity3D for VR 学习(1): 又一个新玩具 暴风魔镜 4(Android) image thumb4

     

       2015年下半年的朋友圈中各种VR、AR的新闻层次不穷,搞的我也心痒痒的:好歹咱也是职业的Unity3D程序员,高大上的Oculus rift dk2咱玩不起,搞个廉价的VR产品也玩玩嘛! 故, 暴风魔镜 4刚发布就在网上预订了。    原估计春节前能到货,没有想到元旦前就到货了,高兴 — 业内良心嘛。

   

迫不及待玩了几天,小结如下:

1 魅蓝 Note 2 + 魔镜4 的VR效果不错,完全对得起159元的预订价,比预期的好得多,类似在家看3D大片效果。通过魔镜自带的Unity demo–第一人称漫游、第三人称漫游、360°全景图片等。(VR 的3D效果,无法予以言表)

2 CardBoard类似的VR不费电,也不用连接电线,这点不错,完美复用了手机的软硬件,这样有利于大幅度降低VR商业门槛。

3 VR不仅仅是资本市场火热,技术市场同样也在酝酿中–由硬件、软件技术的革新推动产业的升级。 未来3年 VR的潜力不可小觑的。

 

image  Unity3D for VR 学习(1): 又一个新玩具 暴风魔镜 4(Android) image thumb5

 

 

魔镜4存在如下问题:

  1. 放入手机的卡槽不严密,脑袋一晃就咔嚓盒子打开了,就得重新“合上仓门”。
  2. 不知道是魔镜app问题,还是手机问题,或者是魔镜VR SDK问题,魔镜里面的视图–一会方向就有问题了,陀螺仪问题? 这点在Unity VR demo中不明显; 在魔镜app中非常明显,感觉很难用。
  3. 魔镜 Unity VR 的SDK导入后,如果是Unity 5.3的话,需要修改一下废弃的API函数,这点应该和Unity与时俱进嘛, 目前VR开发者中Unity占大头的。

 

吐槽一下:

距离《可以WPF编程的玩具:Xbox 360 Kinect–开箱》 已经1.5年了,放弃Kinect原因很简单,社区太冷淡,今年夏天通过*宝把Kinect卖了。