Unity3D的一半黑白一半彩色材质Shader

特效人员可能需要一个模型上半部分显示黑白,下半部分显示正常的纹理颜色这样的效果。并且可以通过参数可调节黑白范围。

脚本

拿到每个顶点的世界坐标系的值,比对Y值,大于就黑白,小于就正常。关键在于找到顶点的最高点和最低点。 于是有了这个脚本绑定在对应的Mesh Object上。用来告知shader这个Mesh的最高点和最低点。

using UnityEngine;

public class MeshVerticesInfo : MonoBehaviour
{
    private Vector4 mBeginVertex = Vector4.zero;
    private Vector4 mEndVertex = Vector4.zero;
    private float mMeshHeight = 0.0f;

    void Start()
    {
        MeshFilter meshFilter = GetComponent<MeshFilter>();

        if (meshFilter == null)
        {
            return;
        }

        Mesh mesh = meshFilter.mesh;

        if (mesh == null)
        {
            return;
        }

        Vector3[] vertices = mesh.vertices;

        mBeginVertex.y = float.MaxValue;
        mEndVertex.y = float.MinValue;

        // 拿到最低点和最高点
        int i = 0;
        foreach (Vector3 vertex in vertices)
        {
            if (i <= 0)
            {
                mBeginVertex = vertex;
                mEndVertex = vertex;

                mBeginVertex = transform.TransformPoint(vertex);
                mEndVertex = transform.TransformPoint(vertex);
            }

            Vector3 vertexWorldPos = transform.TransformPoint(vertex);

            if (vertexWorldPos.y <= mBeginVertex.y)
            {
                mBeginVertex = vertexWorldPos;
            }

            if (vertexWorldPos.y > mEndVertex.y)
            {
                mEndVertex = vertexWorldPos;
            }

            ++i;
        }

        mMeshHeight = mEndVertex.y - mBeginVertex.y;
    }
    void Update()
    {
        renderer.material.SetVector("_BeginVertex", mBeginVertex);
        renderer.material.SetVector("_EndVertex", mEndVertex);
        renderer.material.SetFloat("_MeshHeight", mMeshHeight);
    }
}

Shader

Continue reading Unity3D的一半黑白一半彩色材质Shader

Unity3D的高光shader(BumpedSpecular)改进:支持高光贴图

Unity3D引擎确实将shader的编写方便了不少(当然我也用过Unreal Engine 4那极其强大的结合Blueprint材质编辑器),Surface Shader的出现也方便了很多.

Unity自带的BumpedSpecular shader可以得到一些反射高光的效果,但是却不能支持高光贴图,改进了一下,代码如下:

Shader "Custom/BumpedHighlightSpecular" 
{
    Properties 
    {
        _Color ("Main Color", Color) = (1,1,1,1)
        _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
        _Shininess ("Shininess", Range (0.03, 1)) = 0.078125
        _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
        _BumpMap ("Normalmap", 2D) = "bump" {}
        _SpecularTextureGloss("Specular(RGB)Gloss(A)", 2D) = "black" {}
        _SpecularMultiple("Specular Multiple", Float) = 1
    }
    SubShader 
    { 
        Tags 
        { 
            "RenderType"="Opaque"  
        }
        LOD 400

        CGPROGRAM
        #pragma surface surf BlinnPhong

        sampler2D _MainTex;
        sampler2D _BumpMap;
        fixed4 _Color;
        half _Shininess;
        sampler2D _SpecularTextureGloss;
        half _SpecularMultiple;
        fixed4 _SpecularColor;

        struct Input 
        {
            half2 uv_MainTex;
            half2 uv_BumpMap;
            half2 uv_SpecularTextureGloss;
        };

        void surf (Input IN, inout SurfaceOutput o) 
        {
            fixed4 mainTexture = tex2D(_MainTex, IN.uv_MainTex);
            fixed4 specularTexture = tex2D(_SpecularTextureGloss,(IN.uv_SpecularTextureGloss.xyxy).xy);
            fixed4 specular = _Shininess.xxxx + specularTexture.aaaa;
            fixed4 specular2 = specularTexture + _SpecColor;
            fixed4 gloss = specular2 * _SpecularMultiple.xxxx;

            _SpecColor = _SpecColor * specularTexture;

            o.Albedo = mainTexture.rgb * _Color.rgb;
            o.Gloss = gloss;
            o.Alpha = mainTexture.a * _Color.a;
            o.Specular = specular ;
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
        }

        ENDCG
    }

    FallBack "Specular"
}

用的光照模型依然是BlinnPhong,多了一张高光贴图,另外高光贴图的颜色也会影响到最终的颜色.

多了个_SpecularMultiple参数来叠加高光效果.

一个C#的泛型工厂

遇到类似以下情况:

class Base
{
    public enum TestType
    {
        TYPE_A,
        TYPE_B
    }
}

class TypeA:Base
{

}

class TypeB:Base
{

}

根据枚举类型不同而创建不同子类的时候,我们需要一个工厂来进行管理。
在泛型工厂中,需要保存构造函数指针和对应的枚举类型映射,于是需要有添加创建器的操作。
一个很方便使用的泛型工厂代码如下:

Continue reading 一个C#的泛型工厂

函数封装的改进(C++ 11不定长模板的加入)

在上文中,介绍了一种使用C++ 11动态类型推导封装函数的方法,但是依然没有摆脱函数不定参数封装的传统模式。今天借助C++ 11的不定长模板参数功能,来解决掉这个问题.

首先回顾一下要达到的要求:

  • 可以支持封装静态函数(全局函数)和类成员函数
  • 被封装的函数可以接受任意数量和类型的参数
  • 调用者可以拿到直接的真正的被封装函数的返回值和返回类型
  • 封装后的成员函数,可以在真正运行的时候才接受绑定的this object

这篇文章要解决的就是第二点.
封装依然得分为静态(全局)函数和类成员函数分别封装.

静态(全局)函数

代码:

template<class Func, class ... Args>
class Callable
{
private:
    Func functor_;

public:
    Callable(Func functor)
        : functor_(functor)
    {

    }

    auto run(Args&&... args)    ->decltype(functor_(args...))
    {
        return functor_(args...);
    }
};

template <class Ret, class ...Args>
auto MakeCallable(Ret(*func)(Args...)) ->decltype(new Callable<Ret(*)(Args...), Args...>(func))
{
    return new Callable<Ret(*)(Args...),Args...>(func);
}

和前文不同的地方在于,多了一个…Args模板参数,这个表示不定长模板参数,使用…来标记.这样就能做到匹配到任意参数个数的函数,不再需要像前文那样根据参数个数不同写多个这样的包装函数实现.

Continue reading 函数封装的改进(C++ 11不定长模板的加入)

一种函数Callback的封装(使用到C++11)

本文想要实现的函数封装功能有如下:

  1. 可以支持封装静态函数(全局函数)和类成员函数
  2. 被封装的函数可以接受任意数量和类型的参数
  3. 调用者可以拿到直接的真正的被封装函数的返回值和返回类型
  4. 封装后的成员函数,可以在真正运行的时候才接受绑定的this object

原理

无论是__cdecl(缺省)还是__thiscall函数,我们都可以通过函数指针存储起来,借助C++的模板,于是我们可以存储任意类型的函数指针。同时借助C++11的返回值类型推导,于是我们可以让使用者拿到真正的函数返回值. 如果你不熟悉C++11的返回值类型推导内容,请点击这里.

实现

静态函数(全局函数)

先直接看代码:

template <class Func> 
class Callback0 
{
public:
    typedef decltype(Func()) result_type;

    Callback0(Func functor)
        : functor_(functor)
    { }

    auto run() ->decltype(Func())
    { 
        return functor_();
    }
private:
    Func functor_;
};

template <class Func,class A0>
class Callback1
{
public:
    typedef decltype(Func()(a0)) result_type;

    Callback1(Func functor)
        : functor_(functor)
    {

    }

    auto run(A0 a0) ->decltype(Func()(a0))
    {
        return functor_(a0);
    }

private: 

    Func functor_;
};

template <class Func, class A0, class A1>
class Callback2
{
public:
    typedef decltype(Func()(a0,a1)) result_type;

    Callback2(Func functor)
        : functor_(functor)
    {

    }

    auto run(A0 a0, A1 a1) ->decltype(Func()(a0, a1))
    {
        return functor_(a0,a1);
    }

private:

    Func functor_;
};

template <class Ret>
auto MakeCallback(Ret(*func)()) ->decltype(new Callback0<Ret(*)()>(func))
{
    return new Callback0<Ret(*)()>(func);
}

template <class Ret, class A0>
auto MakeCallback(Ret(*func)(A0)) ->decltype(new Callback1<Ret(*)(A0), A0>(func))
{
    return new Callback1<Ret(*)(A0), A0>(func);
}

template <class Ret, class A0, class A1>
auto MakeCallback(Ret(*func)(A0, A1))   ->decltype(new Callback2<Ret(*)(A0,A1), A0,A1>(func))
{
    return new Callback2<Ret(*)(A0,A1), A0, A1>(func);
}

这里实现了一个最多支援两个参数的静态函数封装,但是支援无限多个参数,也只需要再写多个类似的结构即可。这个部分其实是和早前的C++规范是类似的。

Continue reading 一种函数Callback的封装(使用到C++11)

Cocos2d-x 程序移植到Windows Phone 8小记

将一个在iOS和Android上运行良好的Cocos2d-x 程序移植到Windows Phone 8的设备。

网路上面有一些文章,大体可以按照这些文章来进行,下面说几点遇到的坑:

1.E_OUTOFMEMORY

程序移植过去后,在Lumia 920上运行的好好的,但是在更高配的Lumia 1520却报

D3D11 ERROR: ID3D11Device::CreateTexture2D: Returning E_OUTOFMEMORY, meaning memory was exhausted

这样的错误,实在匪夷所思。毕竟Lumia 1520的配置远比Lumia 920要好。

不管怎么样,参照:http://msdn.microsoft.com/zh-cn/library/windowsphone/develop/jj681682%28v=vs.105%29.aspx  这篇文章,添加了ID_FUNCCAP_EXTEND_MEM标记后运行OK了。

2.使用自己的shader

这实在耗费了我一些时间,另外开文在此:http://www.ispinel.com/2014/07/03/12393/

3.libcurl

这个工程在cocos2dx\platform\third_party\wp8\libcurl,添加进解决方案,然后把项目引用其就行。

4.多线程

由于之前的代码广泛使用到pthread,遇到这块的时候,我一方面参考了CCPThreadWinRT.h这个文件设定(后来也被废除),另外一方面使用了C++ 11的std::thread以及Parallel Patterns Library进行取代.

5.中文输入法

这个问题解决的我实在想说一句WTF。如果在UI库上,激活输入法是通过CCEGLView::setIMEKeyboardState方式,那么会遇到问题。在Windows Phone 8下面会出现在中文输入法时,会同时接收到用户输入的英文和中文,而在Windows Phone 8.1下面,则完全不能接受到中文。即便Cocos2d-x 的官方sample也是如此。解决方式:项目改成XAML模式,当要激活键盘输入时,通过CCEGLView::OpenXamlEditBox来打开一个单独的专门输入页面。

Continue reading Cocos2d-x 程序移植到Windows Phone 8小记