skia库的3D变换研究

本文深入研究了Skia库中的3D坐标变换,特别是SkCamera3D类如何通过投影矩阵和模型视图矩阵实现3D效果。详细解析了相机定位、投影算法以及模型变换矩阵的生成过程,对于理解2D空间中的3D变换提供了清晰的思路。
摘要由CSDN通过智能技术生成

skia库的3D坐标变换其实也是无奈之举。参照SKCamera.cpp:

 

首先,定义了一个虚拟相机:

 

void SkCamera3D::reset() {
    fLocation.set(0, 0, -SkIntToScalar(576));   // 8 inches backward
    fAxis.set(0, 0, SK_Scalar1);                // forward
    fZenith.set(0, -SK_Scalar1, 0);             // up

    fObserver.set(0, 0, fLocation.fZ);

    fNeedToUpdate = true;
}

 求出相机的投影矩阵。这个代码里面叫做方位(orientation)

 

这个方位矩阵完成了从3D空间到2D空间的投影,

 

这个方法研究了很久,具体分析见注释,不多讲了。

 

相当于OpenGL的glFrustum函数。代码如下:

void SkCamera3D::doUpdate() const {
    SkUnit3D    axis, zenith, cross;

    fAxis.normalize(&axis);

    {
        SkScalar dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&fZenith, axis);

        zenith.fX = fZenith.fX - SkUnitScalarMul(dot, axis.fX);
        zenith.fY = fZenith.fY - SkUnitScalarMul(dot, axis.fY);
        zenith.fZ = fZenith.fZ - SkUnitScalarMul(dot, axis.fZ);

        (void)((SkPoint3D*)(void*)&zenith)->normalize(&zenith);
    }

	/*
		[-z, 0, x]   [cross.fX, 	cross.fY, 		cross.fZ]
		[0, -z, y]* [zenith.fx, 	zenith.fY, 	zenith.fZ]
		[0,  0,  1]  [axis.fx, 	axis.fY,   		axis.fZ]

		下面这个矩阵式是投影矩阵:
		
		[-z, 0, x] 
		[0, -z, y]
		[0,  0,  1] 

		投影到x,y z位置的投影矩阵,其中x, y恒为0。如果不为0,经推算,
		这个矩阵是有错误的,用三角形相似,可以算出,应该是下面这个样子

		[-z, 0, x-zx] 
		[0, -z, y-zy]
		[0,  0,  1] 
	
	*/
	
    SkUnit3D::Cross(axis, zenith, &cross);

    {
        SkMatrix* orien = &fOrientation;
        SkScalar x = fObserver.fX;
        SkScalar y = fObserver.fY;
        SkScalar z = fObserver.fZ;

        orien->set(SkMatrix::kMScaleX, SkUnitScalarMul(x, axis.fX) - SkUnitScalarMul(z, cross.fX));
        orien->set(SkMatrix::kMSkewX,  SkUnitScalarMul(x, axis.fY) - SkUnitScalarMul(z, cross.fY));
        orien->set(SkMatrix::kMTransX, SkUnitScalarMul(x, axis.fZ) - SkUnitScalarMul(z, cross.fZ));
        orien->set(SkMatrix::kMSkewY,  SkUnitScalarMul(y, axis.fX) - SkUnitScalarMul(z, zenith.fX));
        orien->set(SkMatrix::kMScaleY, SkUnitScalarMul(y, axis.fY) - SkUnitScalarMul(z, zenith.fY));
        orien->set(SkMatrix::kMTransY, SkUnitScalarMul(y, axis.fZ) - SkUnitScalarMul(z, zenith.fZ));
        orien->set(SkMatrix::kMPersp0, axis.fX);
        orien->set(SkMatrix::kMPersp1, axis.fY);
        orien->set(SkMatrix::kMPersp2, axis.fZ);
    }
}

 

 

然后在patchToMatrix方法中,使用此投影矩阵对当前的变换UVO坐标系进行了变换,代码如下:

 

   

void SkCamera3D::patchToMatrix(const SkPatch3D& quilt, SkMatrix* matrix) const {
    if (fNeedToUpdate) {
        this->doUpdate();
        fNeedToUpdate = false;
    }

    const SkScalar* mapPtr = (const SkScalar*)(const void*)&fOrientation;
    const SkScalar* patchPtr;
    SkPoint3D       diff;
    SkScalar        dot;

    diff.fX = quilt.fOrigin.fX - fLocation.fX;
    diff.fY = quilt.fOrigin.fY - fLocation.fY;
    diff.fZ = quilt.fOrigin.fZ - fLocation.fZ;

    dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&diff,
                        *(const SkUnit3D*)(((const SkScalar*)(const void*)&fOrientation) + 6));

    //	patchPtr的结构为{U,V,ORIGIN}其中U,V 代表列向量
	// ORIGIN 是坐标原点
	
    patchPtr = (const SkScalar*)&quilt;
	
	/*
	 其中matrix表示一个3x3的矩阵
	第一行代表U的系数,第二行是V的系数,第三行是diff 的系数
	matrix 的每一列代表的是一个坐标轴。
	*/
	//第一列
	matrix->set(SkMatrix::kMScaleX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
    matrix->set(SkMatrix::kMSkewY,  SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
    matrix->set(SkMatrix::kMPersp0, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot));

	//第二列
    patchPtr += 3;
    matrix->set(SkMatrix::kMSkewX,  SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
    matrix->set(SkMatrix::kMScaleY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot)); 
    matrix->set(SkMatrix::kMPersp1, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot));

	//第三列
    patchPtr = (const SkScalar*)(const void*)&diff;
    matrix->set(SkMatrix::kMTransX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
    matrix->set(SkMatrix::kMTransY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
    matrix->set(SkMatrix::kMPersp2, SK_UnitScalar1);
	
}

 到此为止,也就把这个变换的思路给理解清楚了。跟OpenGL的思路类似。

 

 

第一。进行模型视图变换,通过一个patch的东西,UVO坐标系。

第二。进行投影变换。定位相机,具体参照如下函数:

 

 

 

	/*
	由于x,y,z 与cross的点积,就是在cross方向上的投影。因此,对于x,y,z向量来说。

	从世界坐标转换到相机坐标系的矩阵就是


	 [cross.fX, 	cross.fY, 		cross.fZ]
	  [zenith.fx, 	zenith.fY, 	zenith.fZ]  
	[axis.fx,	  axis.fY,		  axis.fZ]

由于是标准正交基,
他的转置或者说,逆矩阵,就是相机矩阵

	[cross.fX,	   zenith.fx,	  axis.fx]
	[cross.fY,   zenith.fY,  axis.fY]	 
	[ cross.fZ ,zenith.fZ, axis.fZ]


  
	
	
		[-z, 0, x]   [cross.fX, 	cross.fY, 		cross.fZ]
		[0, -z, y]* [zenith.fx, 	zenith.fY, 	zenith.fZ]
		[0,  0,  1]  [axis.fx, 	axis.fY,   		axis.fZ]

		下面这个矩阵式是投影矩阵:
		
		[-z, 0, x] 
		[0, -z, y]
		[0,  0,  1]  
	
	*/

 

增加一些注释

 

 

下面增加一些系统介绍

 

SKCamera3D中关于模拟3D的算法研究

 

SkCamera是一个可以支持3D变换的组件,通过一些矩阵变换,最终获得了3D的效果。与OpenGL的思路基本相同,分为两个矩阵,一个是投影矩阵,一个是模型视图矩阵。

下面就这两个矩阵,并结合SkCamera的代码进行讲解。

1.       投影矩阵。

投影矩阵是通过SkCamera3D这个类进行生成和管理。SkCamera3D这个的作用还在于将一个SkPatch3D的一个坐标系转换为投影后的坐标系,通过patchToMatrix完成。

总而言之,这个类负责投影的相关工作。

 

class SkCamera3D {

public:

    SkCamera3D();

 

    void reset();

void update();

void patchToMatrix(const SkPatch3D&, SkMatrix* matrix) const;

    SkPoint3D   fLocation;

    SkPoint3D   fAxis;

    SkPoint3D   fZenith;

    SkPoint3D   fObserver;

private:

    mutable SkMatrix    fOrientation;

    mutable bool        fNeedToUpdate;

    void doUpdate() const;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值