cocos设置 相机矩阵和投影矩阵 源码浅析

在cocos中,最后设置视口大小,相机矩阵,裁剪矩阵是在setProjection方法中,源码如下:

void Director::setProjection(Projection projection)
{
    //屏幕的可绘制区域,设计分辨率,fix模式下不和设计分辨率一样,其余和设计分辨率相等

    Size size = _winSizeInPoints;
    //设置适口,吧自己调整后的设计分辨率,换算成屏幕分辨率,设置绘制区域
    setViewport();

    switch (projection)
    {
        case Projection::_2D:
        {
            //单位化裁剪矩阵
            loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);

#if CC_TARGET_PLATFORM == CC_PLATFORM_WP8
            if(getOpenGLView() != nullptr)
            {
                multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, getOpenGLView()->getOrientationMatrix());
            }
#endif
            
            
            Mat4 orthoMatrix;
            /*创建自定义投影矩阵*/
            /*
             left  视图体的最小 X 值。
             right 视图体的最大 X 值。
             bottom 视图体的最小 Y 值。
             top  视图体的最大 Y 值。
             zNearPlane 视图体的最小 Z 值。
             zFarPlane  视图体的最大 Z 值。
             返回值  正交投影矩阵。
             */
            Mat4::createOrthographicOffCenter(0, size.width, 0, size.height, -1024, 1024, &orthoMatrix);
           
            multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, orthoMatrix);
          
            loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
            break;
        }
            
        case Projection::_3D:
        {
            //得到观察者的Z,以设计分辨率为单位
            /*
             (_winSizeInPoints.height / 1.1566f);,为什么是1.1566,
             根据相似三角形,通过这个值,得到的裁剪面正好就是 z为0的裁剪面,也就是2d中我们设置精灵的地方
原理可参考 http://blog.sina.com.cn/s/blog_6084f5880102v26q.html
*/ float zeye = this->getZEye(); //投影矩阵和View矩阵 Mat4 matrixPerspective, matrixLookup; //初始化一个单位矩阵,初始化投影矩阵为单位矩阵 loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); #if CC_TARGET_PLATFORM == CC_PLATFORM_WP8 //if needed, we need to add a rotation for Landscape orientations on Windows Phone 8 since it is always in Portrait Mode GLView* view = getOpenGLView(); if(getOpenGLView() != nullptr) { multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, getOpenGLView()->getOrientationMatrix()); } #endif // issue #1334 //创建裁剪矩阵 /* cocos直接使用设计分辨率winSizeInPoints来设置投影矩阵和观察点,并没有将其缩放到屏幕的实际分辨率,而只有视口设置为实际分辨率。 透视矩阵定义的视锥体的长宽比和视口的长宽比是相等的,这样才会保证不会变形 视口的大小为 设计分辨率*scale(scalex和scaley大部分情形一样),所以他们的比是一样的 我的理解:设计分辨率下的坐标相当于 世界坐标,然后乘以相机矩阵,得到相机下的坐标,然后乘以裁剪矩阵,得到裁剪后的规范化坐标,基本没用到实际分辨率 */ //参数一:垂直张角 参数二 宽高比,参数三:近端距离。距离eyez,也就是眼睛z坐标的距离 参数四:远端距离 参数五:生成的裁剪矩阵 /* [x,y,z,1] 乘以 这个矩阵之后,变为[x'z,y'z,zz',wz],w=1,然后新的除以z,就是透视除法,把x',y',z' 变为(0,1)范围 // 经过投影变换后,物体坐标变换到了裁剪坐标系,经过OpenGL自动执行的透视除法后,变换到规范化设备坐标系中。透视除法就是将裁剪坐标系中坐标都除以齐次坐标的过程。 做过实验:把距离摄像机的近端距离由10变为100,发现 最后的图像没有变大,原因: 经过投影之后,x'=x/(z*aspect*tan(fov/2))这是投影之后的x坐标,范围为(-1,1),可以看到大小只受 aspect 和fov的影响,aspect不变,fov也不变,所以图像大小不变,受影响的只是z‘,不过投影到2d图像上, 这个用不到 */ Mat4::createPerspective(60, (GLfloat)size.width/size.height, 10, zeye+size.height/2, &matrixPerspective); //Mat4::createPerspective(60, (GLfloat)size.width/size.height, 100, zeye+size.height/2, &matrixPerspective); //单位矩阵先乘以投银矩阵,得到投影矩阵 multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, matrixPerspective); Vec3 eye(size.width/2, size.height/2, zeye), center(size.width/2, size.height/2, 0.0f), up(0.0f, 1.0f, 0.0f); //生成相机坐标矩阵 /* 参数一:眼睛的位置,正中间 参数二:眼睛看向哪里,看向视口中点 参数三 头顶朝向,一班都是010 */ Mat4::createLookAt(eye, center, up, &matrixLookup); //通过测试得知 cameraPos.z=projectPos.w ,w就是z Vec4 cameraPos=matrixLookup *Vec4(100,200,0,1); Vec4 projectPos= matrixPerspective *cameraPos; //loopup矩阵右乘裁剪矩阵 也就是p*V multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, matrixLookup); //初始化模型视口矩阵?? loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); break; } case Projection::CUSTOM: // Projection Delegate is no longer needed // since the event "PROJECTION CHANGED" is emitted break; default: CCLOG("cocos2d: Director: unrecognized projection"); break; } _projection = projection; // GL::setProjectionMatrixDirty(); _eventDispatcher->dispatchEvent(_eventProjectionChanged); }

 下面再看看生成投影矩阵的代码:

void Mat4::createPerspective(float fieldOfView, float aspectRatio,
                                     float zNearPlane, float zFarPlane, Mat4* dst)
{
    GP_ASSERT(dst);
    GP_ASSERT(zFarPlane != zNearPlane);

    float f_n = 1.0f / (zFarPlane - zNearPlane);
    float theta = MATH_DEG_TO_RAD(fieldOfView) * 0.5f;
    if (fabs(fmod(theta, MATH_PIOVER2)) < MATH_EPSILON)
    {
        CCLOGERROR("Invalid field of view value (%f) causes attempted calculation tan(%f), which is undefined.", fieldOfView, theta);
        return;
    }
    float divisor = tan(theta);
    GP_ASSERT(divisor);
    float factor = 1.0f / divisor;

    memset(dst, 0, MATRIX_SIZE);

    GP_ASSERT(aspectRatio);
    dst->m[0] = (1.0f / aspectRatio) * factor;
    dst->m[5] = factor;
    dst->m[10] = (-(zFarPlane + zNearPlane)) * f_n;
    //测试的值m[11]控制着裁剪矩阵(x'z,x'y,z'z,z)的z,左手坐标系m[11]为1,右手坐标系m[11]为-1,控制着
    //最后x,y,z 的正负,不管是左手坐标系还是右手坐标系,得出的裁剪矩阵x',y',z'的坐标应该是一样的
    //而在世界坐标转换为 相机坐标的时候,z坐标正好相反,所以通过m[11]使2者的裁剪坐标一致
    dst->m[11] = -1.0f;
    dst->m[14] = -2.0f * zFarPlane * zNearPlane * f_n;
}
原文地址:https://www.cnblogs.com/xiaonanxia/p/9131908.html