在Stage3D中实现X-Ray透视效果

在stage3D中实现火炬之光遮挡透视的X-ray效果实际上很简单。
渲染流程如下

1.渲染所有物体+场景
2.正常渲染主角
3.使用X-ray Shader再渲染一次主角

这个渲染顺序是必须的,必须把遮挡物体都事先渲染完毕。
之后展开第3点看看具体的渲染流程

设置 depthFunc 为 Greater, writeDepth 为 false
此时渲染的主角只会显示在 遮挡物体上
之后对该角色的像素着色器,使之显示单一颜色,无论蓝色,黄色,红色都可以
为了使Shader只渲染出轮廓,可以将 相机位置当做灯光位置和模型的法线点乘,求出夹角,将该值作为输出像素的透明度值和RGB进行混合输出。

像素着色器AGAL如下

        public override function getFragmentProgram():ByteArray
        {
            var shaderConstant : ShaderConstants;
            var index : uint;
            index = m_params.fragmentShaderConstants.push(new ShaderConstants(fcXray)) - 1;
            shaderConstant= m_params.fragmentShaderConstants[index];
            shaderConstant.vector = Vector.<Number>([
                10/255,72/255,247/255,0.5,
                1,0,0,0]);
            shaderConstant.numRegisters = 2;
           
            var code : String =
                "nrm ft1.xyz v"+vProjPos+".xyzn" +
                "dp3 ft0.a ft1.xyz fc"+fcCameraPos+".xyzn"+
                "sat ft0.a ft0.an"+
                "mov ft0.r fc"+fcXray+".rn"+
                "mov ft0.g fc"+fcXray+".gn"+
                "mov ft0.b fc"+fcXray+".bn"+
                "mul ft0.rgb ft0.rgb ft0.aaan"+
                "mov oc ft0n";
           
            return new AGALMiniAssembler().assemble(Context3DProgramType.FRAGMENT, code);  
        }

fcXray 的值为
[10/255,72/255,247/255,0.5,1,0,0,0]
占用两个寄存器,前3个值可以调节输出的颜色
fcCameraPos 为外部传入的相机位置,已经归一化和反转过

移植:在Cocos2d-X中实现水波滤镜

因为工作原因,开始转向cocos2d-x开发方向了。
自然的,凭着之前引擎的经验偏向底层渲染研究。

在此期间看了两本书
《cocos2d-x 权威指南》
《cocos2d-x 高级开发教程》
不得不说,第一本书完全是API介绍,也许适合新手入门,但是于我,只是烂书一本。
第二本书还是不错的,最后的项目有全套源码,教程的风格也很有我的风格,哈哈,就是文章中不一定贴全代码,只贴核心,后期还要自己领会补全。

言归正传,在 《高级开发教程》 中有一篇是实现海底水纹效果的文章,里面代码不全,且代码和Shader并没有对应起来,于是我决定按照自己思路来将其补全,并贴上完整源码。

先上效果图:
waveCover
动态GIF图地址: http://pan.baidu.com/share/link?shareid=1858640040&uk=3221530571

继续阅读“移植:在Cocos2d-X中实现水波滤镜”

使用四元数来渲染蒙皮动画

原本没计划使用四元数来渲染蒙皮动画的,但是由于stage3D的限制,因此考虑在AOI3D中,使用四元数来渲染吧。

由于Adobe的畏首畏尾,尽可能的向下兼容,又不肯把兼容的选择权交给开发者,因此stage3D只提供了128个常量寄存器。
在3D程式中,一般使用 4*4 矩陣來描述骨骼的旋轉-位移-縮放。 而一個常量寄存器只能存儲 4個浮點型(在DX10中已经支持整数型了,不过stage3D只有DX9级别),
因此我们只能渲染 128/4 = 32 根骨骼, 为了确定模型的位置,我们还需要上传 ModelViewProjection 矩阵, 那么我们只能使用31根骨骼了,而且还要放弃各种灯光及滤镜效果了。

使用四元数的优势,开源引擎Ogre的模型文件就是使用四元数进行蒙皮了,这也是AOI3D能支持蒙皮动画的原因。 一个四元数使用 x,y,z,w 来表示 3个轴的旋转,因此它只需要使用1个寄存器, 之后再使用一个寄存器来存储位移即可。 对于缩放,一般蒙皮动画的缩放都是 1, 因此可以忽略。 那么我们可以渲染 124 / 2 = 62 跟骨头了。

这里不讲解四元数的原理,直接说使用。
你可以通过支持四元数的动画文件直接解析出四元数的值,也可以通过 Matrix3D 来获取四元数

var datas : Vector.<Vector3D> = matrix3D.decompose(Orientation3D.QUATERNION);

datas 是一个数组,其实[0]是位移,[1]是四元数,[2]是缩放
有了四元数后,我们将其通过常量上传到Shader中,然后再Shader中,重新组装成一个 3 * 4 矩阵,然后其他都和之前的一样。

四元数转换回矩阵的方式(CPU模拟)

            var quaIndex : uint = index / 2;
            var rotation : Vector3D = m_jointConstant[quaIndex];
            var translate : Vector3D = m_jointConstant[quaIndex + 1];
           
            var mat : Matrix3D = new Matrix3D();

            var vt0 : Vector3D = new Vector3D();
            vt0.x = 2 * rotation.x * rotation.y;
            vt0.y = 2 * rotation.x * rotation.z;
            vt0.z = 2 * rotation.x * rotation.w;
            vt0.w = 2 * rotation.y * rotation.z;
            var vt1 : Vector3D = new Vector3D();
            vt1.x = 2 * rotation.y * rotation.w;
            vt1.y = 2 * rotation.z * rotation.w;
            vt1.z = rotation.x * rotation.x;
            vt1.w = rotation.y * rotation.y;
            var vt2 : Vector3D = new Vector3D();
            vt2.x = rotation.z * rotation.z;
            vt2.y = rotation.w * rotation.w;
           
            var rawData : Vector.<Number> = mat.rawData;
            xx - yy - zz + ww
            rawData[0] = vt1.z - vt1.w - vt2.x + vt2.y;
            2xy - 2zw
            rawData[1] = vt0.x - vt1.y;
            2xz + 2yw
            rawData[2] = vt0.y + vt1.x;
            0
            rawData[3] = translate.x;
            2xy + 2zw
            rawData[4] = vt0.x + vt1.y;
            -xx + yy - zz + ww
            rawData[5] = vt1.w - vt1.z - vt2.x + vt2.y;
            2yz + 2xw
            rawData[6] = vt0.w - vt0.z;
            0
            rawData[7] = translate.y;
            2xz + 2yw
            rawData[8] = vt0.y - vt1.x;
            2yz - 2xw
            rawData[9] = vt0.w + vt0.z;
            -xx - yy + zz + ww
            rawData[10] = vt2.x - vt1.z - vt1.w + vt2.y;
            0
            rawData[11] = translate.z;
            0
            rawData[12] = 0;
            0
            rawData[13] = 0;
            0
            rawData[14] = 0;
            1
            rawData[15] = 1;

            mat.rawData = rawData;

这个矩阵的解析已经被手动转置过了,如果使用CPU渲染,需要手动转置回来,又或者在创建rawData的时候就设置好其索引。