• 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;
    }
  • 相关阅读:
    Experimental Educational Round: VolBIT Formulas Blitz D
    Experimental Educational Round: VolBIT Formulas Blitz C
    windows10 IOT +Azure会议概要总结
    财务报表开发实例分析:几个通用维度介绍与关键点
    eclipse中LogCat有时不显示信息的简单解决办法
    友情提醒:欲开发android5.0以上应用,请全部更新开发工具至最新
    高通、猎户机型Android典型bootloader分析
    Ubuntu 14.04 中安装 VMware10 Tools工具
    Linux内核中的GPIO系统之(3):pin controller driver代码分析
    linux内核中的GPIO系统之(2):pin control subsystem
  • 原文地址:https://www.cnblogs.com/xiaonanxia/p/9131908.html
Copyright © 2020-2023  润新知