• 最近由于公司业务需要,需要输出深度图,并且游戏最终画面效果要同时输出RGB图和深度图,并分别占据屏幕不同的比例。之前公司同事的做法是用一个相机获取RGB,另一个相机获取深度图(需要用shader对画面进行后期处理...

    最近由于公司业务需要,需要输出深度图,并且游戏最终画面效果要同时输出RGB图和深度图,并分别占据屏幕不同的比例。之前公司同事的做法是用一个相机获取RGB,另一个相机获取深度图(需要用shader对画面进行后期处理),然后相机在屏幕显示比例不同达到最终要求。效果是可行的,但是总感觉很别扭,过于复杂。实际上shader能够输出深度图,本身又有RGB数据,理论上可以用算法一次输出最终分割效果,试了试果然成功了。下面一步步来看如何实现。

    一、 后期渲染

    屏幕后期渲染需要用到回调函数OnRenderImage:

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
          Graphics.Blit(src, dest);
    }

    其中src是屏幕图像,dest是处理后输出的图像,Graphics.Blit函数可以通过材质来对屏幕图像进行加工得到所要的输出图像,比如:
    depthMat是上的shader是获取深度图的shader
    Graphics.Blit(src, dest, depthMat);将会输出屏幕深度图。
    注意这个回调所在的脚本所挂载的对象需要有Camera组件。
    这个回调以及相关方法的细节Unity官方都有,在此不细讲。

    二、 深度图

    有了这个回调之后我们就可以获取深度图了。
    下面是获取深度图的shader:

    Shader "Custom/Depth" {
            SubShader{
            Tags{ "RenderType" = "Opaque" }
    
            Pass{
    
            ZTest Always Cull Off ZWrite Off
    
            CGPROGRAM
    
            // Use shader model 3.0 target, to get nicer looking lighting
            #pragma glsl
            #pragma fragmentoption ARB_precision_hint_fastest
            #pragma target 3.0
            #pragma vertex vert
            #pragma fragment frag
            #include "unityCG.cginc"
            sampler2D _CameraDepthTexture;
    
        struct v2f {
            float4 pos : SV_POSITION;
            float4 scrPos:TEXCOORD0;
        };
        //Vertex Shader
        v2f vert(appdata_base v) {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            o.scrPos = ComputeScreenPos(o.pos);
            return o;
        }
    
        //Fragment Shader
        float4 frag(v2f i) :COLOR{
        float depthValue = 1 - Linear01Depth(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r);
        return float4(depthValue, depthValue, depthValue, 1.0f);
        }
            ENDCG
        }
        }
            FallBack "Diffuse"
    }

    脚本很简单,关键是用内置深度变量_CameraDepthTexture,对他使用屏幕坐标映射可以获取屏幕坐标的深度数据。获取深度数据后将其缩放到0~1区间并发挥。
    有了shader后我们要创建一个Material,将shader赋给它,然后在脚本中获取到这个材质。
    假设材质为depthMat,则输出深度图的回调为:

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
          Graphics.Blit(src, dest, depthMat);
    }

    原始图:
    这里写图片描述
    处理后深度图:
    这里写图片描述

    三、 文字深度

    上面的内容都是游戏内对象,有时候我们需要文字也有深度,在3D环境下(比如裸眼3D,VR)能够有3D空间效果,这个时候就需要对文字进行处理让它具有深度。具体操作步骤如下:
    1. 建立Canves Group
    建立一个空对象,添加Canvas Group组件
    这里写图片描述
    2. 建立文本Canvans
    在上述Canvans Group下建立空子对象,添加Canvas Renderer组件,Canvas组件,Text组件,Text组件的Material需要添加一个材质,Unity自带材质就行,路径为LegacyShaders/Transparent/Cutout/Diffuse(这是新版路径,旧版可能在LegacyShaders目录下。Cutout目录下不止一个shader可以实现且效果不同,自行尝试):
    这里写图片描述
    3. 建立图片Canvans
    和上述Canvas文本一样的操作,不过最后一个组件是Image而不是Text。图片的各项属性和文本基本相同,有两个地方需要注意:一个是图片要在文本的前方(我这里是Z坐标比文本小),但是Canvas的Order in Layer属性比文本低。
    注意这里图片的材质和颜色就是文字的颜色。如果图片不在文字前方,文字将会显示黑色。
    这里写图片描述
    4. 相机视野内文字后方必须有背景,不然RGB图中文字会被图片遮挡不显示。
    上述步骤最后效果:
    RGB图:
    这里写图片描述
    深度图:
    这里写图片描述
    注意:这个方法中的shader除了用来处理文字还可以用来处理有透明通道的图片,让图片的颜色部分产生深度。只需要将材质挂在一个plane上,材质的图片选择对应的图片即可。图片压缩格式要求为DXT5。
    四、 RGB+深度图
    大多数时候我们要的并不是单纯的深度图,而是RGB和深度图的组合,比如左边是RGB,右边是深度图,在经过画面处理合成后可以显示深度效果(我们公司的裸眼3D显示器就是这么干的),根据设备不同,显示比例也不同,有的还需要特殊的分割,比如3X3或者2X2。这里仅仅实现一种简单的输出:边框占画面的十分之一,左边是RGB图,右边是深度图,并且RGB图宽度是深度图的两倍。
    Shader如下:

    Shader "Custom/RGBDepth" {
        Properties{
            _MainTex("Base (RGB)", 2D) = "white" {}
        }
        SubShader{
        Tags{ "RenderType" = "Opaque" }
    
        Pass{
        Cull off
    
        ZTest Always Cull Off ZWrite Off
    
        CGPROGRAM
        //Physically based Standard lighting model, and enable shadows on all light types
        //#pragma surface surf Standard fullforwardshadows
    
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma glsl
        #pragma fragmentoption ARB_precision_hint_fastest
        #pragma target 3.0
        #pragma vertex vert
        #pragma fragment frag
        #include "unityCG.cginc"
    
    
    
        sampler2D _CameraDepthTexture;
        uniform sampler2D _MainTex;
    
        struct v2f {
            float4 pos : SV_POSITION;
            float4 scrPos:TEXCOORD0;
            float2 uv : TEXCOORD1;
        };
        //Vertex Shader
        v2f vert(appdata_base v) {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            o.scrPos = ComputeScreenPos(o.pos);
            o.uv = v.texcoord.xy;
            return o;
        }
    
        //Fragment Shader
        float4 frag(v2f i) :COLOR{
            int width = 2560;//画面宽度放大比率
            int height = 1440;//画面高度放大比率
            int frame = 128;//边框宽度
            int RGBWidht = 1536;//RGB图宽度放大比率
            int depthWidth = 768;//深度图宽度放大比率
            int targetHeight = 1184;//输出图像部分高度
    
            float x = i.uv.x*width;
            float y = i.uv.y*height;
            //生成四边框
            if (x<frame)
                return float4(1.0f, 1.0f, 1.0f, 1.0f);
            if (x>width - frame)
                return float4(1.0f, 1.0f, 1.0f, 1.0f);
            if (y<frame)
                return float4(1.0f, 1.0f, 1.0f, 1.0f);
            if (y>height - frame)
                return float4(1.0f, 1.0f, 1.0f, 1.0f);
    
            y = (y-frame);
            //分割画面
            if (x < frame + RGBWidht) {
                x = (x - frame) / RGBWidht;
                y= y / targetHeight;
                return tex2D(_MainTex, float2(x , y ));
            }
            else {
                x= (x - frame - RGBWidht) ;
                i.scrPos.x= x / depthWidth;
                i.scrPos.y = y / targetHeight;
                float4 uv = UNITY_PROJ_COORD(i.scrPos);
                float depthValue = 1 - Linear01Depth(tex2Dproj(_CameraDepthTexture, uv).r);
                return float4(depthValue, depthValue, depthValue, 1.0f);
            }
            return float4(0.0f, 0.0f, 0.0f, 1.0f);
            }
                ENDCG
            }
        }
        FallBack "Diffuse"
    }

    转载请注明出处:http://blog.csdn.net/ylbs110/article/details/53366112

    可以看到shader并不长,和上面的depth很像,我就不注释太多,多出的部分是用来分割画面的。算法的主要内容是判断当前像素的横坐标是处于RGB图还是深度图范围内(if (x < frame + RGBWidht)),如果在RGB图范围内,则将x,y坐标换算为RGB图像对应像素点的坐标,然后将该颜色返回。在深度图范围内也同样处理。最终效果如下:
    这里写图片描述

    展开全文
  • 目前3D手游的开发主要工具是使用Unity3D引擎,于是,如何在Unity3D的开发过程中结合入物理功能变一个需要仔细考虑的问题。 我们考察的2种物理效果实现方案:Unity3D物理引擎和Havok物理引擎。 Unity3d物理引擎介绍...

    背景

      在重度手游的研发过程当中,游戏中的车辆模拟,场景互动,特效展示等功能很多时候需要物理引擎的介入,以提供丰富的交互体验。目前3D手游的开发主要工具是使用Unity3D引擎,于是,如何在Unity3D的开发过程中结合入物理功能变一个需要仔细考虑的问题。

      我们考察的2种物理效果实现方案:Unity3D物理引擎和Havok物理引擎。

      Unity3d物理引擎介绍

      Unity3d在内部集成了PhysX物理引擎,为其提供了物理模拟能力。

      Physx是目前使用最为广泛的物理引擎,PhysX目前由Nvidia公司开发并维护,特点是免费且带N卡的GPU物理计算加速功能(1)。

      PhysX被很多游戏大作所采用,使用PhysX制作的游戏:


                                      

    4.X版本的Unity3D集成的是2.8.3的PhysX,该版本较为老旧。在Unity5中将集成PhysX3.3,较2.8.3版本在功能和性能上有较大幅的提升,但是目前unity5并不是非常稳定。

      Havok物理引擎介绍

      Havok物理引擎是由Havok公司开发的老牌物理引擎,与PhysX不同,Havok专注于CPU端+多线程模拟方案,并且与PhysX的强大市场推广以及免费策略不同,Havok授权很严格,而且基本不提供试用版本下载(2)。

      使用Havok引擎的游戏大作在数量上与使用PhysX的不相上下,而且很多令人印象深刻:

      Unity3D物理与Havok物理的功能对比

      Unity3D集成的PhysX物理功能:

      Unity3D通过其提供的各种Component访问PhysX的物理功能,打开菜单栏中的Component->Physics便可以看到各种组件:


      其中,

      Rigidbody提供了刚体的访问接口。

      各种XXXCollider提供了3种碰撞包围体方案(Primitive,Mesh,Terrain)。

      WheelCollider组件提供了车轮模拟方案。

      XXXCloth组件提供了布料模拟方案。

      XXXJoint组件提供了关节与连接点模拟方案。

      PhysicsMaterial资源类型提供了表面物理材质描述功能。

      在Unity3D中每一种物理组件都有对应的编辑界面,且即拖即用,非常方便。



      PhysX引擎目前已经涵盖各个平台,且跨平台特性已经融入Unity3D的跨平台机制中,用户无需再关注跨平台开发。

      由于PhysX与Unity3D的深度结合以及Unity3D的闭源特性,修改PhysX的底层模拟机制基本不可能。

      Havok物理引擎功能介绍:

      Havok物理引擎以C++库的形式,通过组件式的方式,提供了丰富的物理功能。包括了:

    • Rigidbody刚体模拟。
    • 5种碰撞包围体模拟方案(Primitive,Convex,Mesh,Compount,Terrain)
    • 完整的VehicleKit车辆模拟方案。
    • HavokCloth不了模拟方案。
    • Constraint关节与连接点模拟方案。
    • HKX物理资源数据描述格式以及对应序列化与反序列化接口。
    • HavokDestruction破碎模拟方案。


      Havok并没有开发官方的Unity3D结合插件,市面上也没有第三方的结合插件可以使用,需要自行开发,有一定的开发成本。

      Havok具备称述的跨平台能力,但是由于没有结合入Unity3D的跨平台机制,在发布到多平台上需要一点额外的工作量。

      Havok在获得授权之后,用户可以修改与定制各个层级的物理功能。

      Unity和Havok主要物理功能对比