IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> TIT 计算机图形学 期末课设-青花瓷坛的实体模型 -> 正文阅读

[人工智能]TIT 计算机图形学 期末课设-青花瓷坛的实体模型

TIT 计算机图形学 期末课设-青花瓷坛的实体模型

前言

  • 实验中用到了很多知识点,这里只讲解其中的一部分,仅供参考.详细讲解可以看孔老师的视频课,具体学习程序中的类
  • 参考视频计算机图形学全套算法讲解和C++编码实现(共23讲配套源码)计算机图形学案例视频讲解以及主页相关算法。孔老师是我的代课老师,孔教授有十多年教学经验,视频课很不错,所有的源程序都基于他写的函数,并非小张写的。所有源程序都基于C+编译
  • 参考教材《计算机图形学-理论与实践项目化教程》 孔令德著,大家多多支持哇
  • 实验源码很多,没有传CSDN因为小张认为源码并不是我开的,只是拿老师的程序做了一些东西,拿这个赚积分和马内未免有点!这里直接放了青花瓷坛源程序,不方便访问Github的可以评论邮箱,或者参考我的Github访问。孔老师的视频课讲的很清楚,大家去B站就可以看啦!

一、项目描述

使用双三次贝塞尔曲线绘制青花瓷坛的线框模型,通过增加光照、材质、纹理,实现线框模型变为实体模型,达到模拟三维青花瓷坛的效果

image-20211222004717597

image-20211222004728058

二、项目设计

1.原理

映射原理: 双三次Bezier曲面的各处都具有了(u,v)值。将纹理uv与曲面uv同时规范化到[0,1]区间内,当查找到曲面纹理时,先将曲面uv放大到和纹理位图一样大小的尺寸,然后获取对应纹理图像上的颜色作为曲面上该点的漫反射率系数

image-20211222004836832

? image-20211222004913153

2.背面剔除算法原理:

视矢量与小平面法矢量的点积大于等于零(n·v>=0),cosα>0,表面可见

1.使用四边形网格的顶点计算面矢量并归一化

2.使用四边形网格的第一顶点与视点,计算矢量并归一化

3.计算法矢量与视矢量的点积,如果其值大于等于零,则绘制该网格,否则剔除该网格

3.球体图像纹理映射原理

3.1读入位图

纹理来自于位图,将位图作为资源导入,位图由DIB格式自动转换为DDB格式,每行存储4个字节的颜色数据,将位图转储到一维颜色数组中,就可以根据曲面网格点与位图去查询位图像素点的颜色,并将其作为材质的漫反射率和环境反射率,从而实现了将位图绑定到物体表面上

image-20211215193043390

4.算法步骤

1.将图像纹理加载到资源标签页中,将位图数据保存到一维数组中

2.定义revolution回转类,读入顶点表和表面表,圆环面全部细分为四边形网格

3.将位图绑定到回转体的顶点上

4.根据光源的位置,数量,视点的位置,材质属性,构造三维光照场景

5.使用画家算法,剔除回转体的不可见网格

6.使用三维透视投影算法,将回转体三维多边形投影为屏幕三维四边形

7.根据网格点的法矢量,插值计算小面内每一点的法矢量,将纹理作为该点的材质,调用光照模型计算小面内该点的光强

8.使用定时器改变圆环的转角生成旋转动画

2.相关算法

PhongShader算法: 对顶点的法矢量进行插值,得到小面内每一点的法矢量。对小面内的每一点都调用光照模型计算光强。计算光强需要四个参数,视点位置、当前点三维坐标、当前点法矢量、当前点的材质属性。如果当前点的材质的漫反射率取自一幅图像的相应点,这就为该物体添加了纹理。

向量线性插值计算:

v(t)=(tEnd-t)/(tEnd-tStart)*vStart+(t-tStart)/(tEnd-tStart)*vEnd;

t表示第一个向量的x(y)方向单位向量,tStart表示第一个点的x(y)方向单位向量,tEnd表示第二个点的x(y)方向单位向量,vStart起点的法向量,vEnd表示第二个点的法向量。

计算光强公式:

I=Ie +Id+Is =kaIa+f(d)[KdIpmax(N·L,0)+ksIpmax(N·H)n]

计算物体表面上任意一点P的光强I时,必须确定物体表面的单位矢量L、单位中分矢量H、单位法矢量N以及材质的漫反射率kd。当光源位置和视点位置不变时,光矢量L和中分矢量H是一个定值。影响光强的只有漫反射率kd和单位法矢量N。

Zbuffer算法:通过使用处理深度缓冲器,若当前点的深度值小于缓冲器中的深度值,则说明当前点离视点近,使用SetPixelV函数绘制当前点,否则放弃当前点。

当前像素点的深度值:z(x,y)=-z(x,y)=- A x + B y + D C \frac{Ax+By+D}{C} CAx+By+D?,C≠0,C≠0;

A,B,C代表法向量的坐标

3.模型

image-20220103234134907

三、部分代码

1.导入位图,格式为bmp

2.Texture类

class CTexture
{
public:
	CTexture(void);
	virtual~CTexture(void);
	void PrepareBitmap(UINT nIDResource);//准备位图
	void DeleteObject(void);//释放位图
public:
	BYTE* image;//存储位图一维数组
	BITMAP bmp;//BITMAP结构体变量
};

CTexture::CTexture(void)//数组初始化为空
{
	image = NULL;
}

CTexture::~CTexture(void)
{
}

void CTexture::PrepareBitmap(UINT nIDResource)//准备位图
{
	CBitmap NewBitmap;//DDB
	NewBitmap.LoadBitmap(nIDResource);//从资源视图中读入位图存储到Newbitmap中
	NewBitmap.GetBitmap(&bmp);//将CBitmap的信息保存到Bitmap结构体中,目的是为了得到图像的高度和宽度
	int nbytesize = bmp.bmWidthBytes * bmp.bmHeight;//计算位图字节数
	image = new BYTE[nbytesize];
	NewBitmap.GetBitmapBits(nbytesize, (LPVOID)image);//将位图数据赋值到一维数组image中
}

void CTexture::DeleteObject(void)//释放位图
{
	if(NULL != image)
		delete []image;
}

3.在贝塞尔曲线类中绑定纹理对象

void CBezierPatch::Tessellation(CMesh Mesh)//细分曲面函数
{
	double M[4][4];//系数矩阵M
	M[0][0] = -1, M[0][1] = 3,  M[0][2] = -3, M[0][3] = 1;
	M[1][0] = 3,  M[1][1] = -6, M[1][2] = 3,  M[1][3] = 0;
	M[2][0] = -3, M[2][1] = 3,  M[2][2] = 0,  M[2][3] = 0;
	M[3][0] = 1,  M[3][1] = 0,  M[3][2] = 0,  M[3][3] = 0;
	CP3 P3[4][4];//曲线计算用控制点数组
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			P3[i][j] = CtrPt[i][j];
	LeftMultiplyMatrix(M, P3);//系数矩阵左乘三维点矩阵
	TransposeMatrix(M);//计算转置矩阵
	RightMultiplyMatrix(P3, M);//系数矩阵右乘三维点矩阵
	double u0, u1, u2, u3, v0, v1, v2, v3;//u,v参数的幂
	double u[4] = { Mesh.BL.u,Mesh.BR.u ,Mesh.TR.u ,Mesh.TL.u };
	double v[4] = { Mesh.BL.v,Mesh.BR.v ,Mesh.TR.v ,Mesh.TL.v };
	for (int i = 0; i < 4; i++)
	{
		u3 = pow(u[i], 3.0), u2 = pow(u[i], 2.0), u1 = u[i], u0 = 1;
		v3 = pow(v[i], 3.0), v2 = pow(v[i], 2.0), v1 = v[i], v0 = 1;
		quadrP[i] = (u3 * P3[0][0] + u2 * P3[1][0] + u1 * P3[2][0] + u0 * P3[3][0]) * v3
			+ (u3 * P3[0][1] + u2 * P3[1][1] + u1 * P3[2][1] + u0 * P3[3][1]) * v2
			+ (u3 * P3[0][2] + u2 * P3[1][2] + u1 * P3[2][2] + u0 * P3[3][2]) * v1
			+ (u3 * P3[0][3] + u2 * P3[1][3] + u1 * P3[2][3] + u0 * P3[3][3]) * v0;
		quadrT[i].u = (pTexture->bmp.bmWidth - 1) * u[i]; 
		quadrT[i].v = (pTexture->bmp.bmHeight - 1) * v[i];
	}
}

22~27绑定纹理到四边形网格上,u、v数组是曲面四边形网格的顶点,quadrTu、quadrTv数组是相应纹理的顶点(没有计算四边形网格细分点的法向量,后续将使用球体的位置向量代替法向量)

4.ZBuffer

ZBuffer类中的PhongShader函数中,读取纹理位图的颜色数据,并将其设置为材质的漫反射率和环境反射率

//读入三角形每个顶点的坐标、点法向量和纹理坐标
void SetPoint
//纹理坐标插值函数
void LinearInterp
//顶点排序
void SortVertex
//定义纹理读取函数,该函数使用纹理坐标读出纹素的颜色值
/*从位图左下角开始读取,从而保证位图正向绘制*/
void GetTexture
class CZBuffer
{
    public:
    CZBuffer(void);
    virtual ~CZBuffer(void);
    void InitialDepthBuffer(int nWidth, int nHeight, double zDepth);//初始化深度缓冲区
    void SetPoint(CP3 P0, CP3 P1, CP3 P2, CVector3 N0, CVector3 N1, CVector3 N2, CT2 T0, CT2 T1, CT2 T2);
    void PhongShader(CDC* pDC, CP3 ViewPoint, CLighting* pLight, CMaterial* pMaterial, CTexture* pTexture);
    private:
    void SortVertex(void);//顶点排序
    void EdgeFlag(CPoint2 PStart, CPoint2 PEnd, BOOL bFeature);//边标记
    CVector3 LinearInterp(double t, double tStart, double tEnd, CVector3 vStart, CVector3 vEnd);//向量线性插值
    CT2 LinearInterp(double t, double tStart, double tEnd, CT2 texStart, CT2 texEnd);//纹理坐标线性插值
    CRGB GetTexture(int u, int v, CTexture* pTexture);//读取纹理
    protected:
    CP3 P0, P1, P2;//三角形的浮点数顶点
    CPoint3 point0, point1, point2;//三角形的整数顶点坐标
    CPoint2* SpanLeft; //跨度的起点数组标志
    CPoint2* SpanRight;//跨度的终点数组标志
    int nIndex;//记录扫描线条数
    double** zBuffer;//深度缓冲区
    int nWidth, nHeight;//缓冲区宽度与高度
};

CZBuffer::CZBuffer(void)
{
    zBuffer = NULL;
}

CZBuffer::~CZBuffer(void)
{
    for (int i = 0; i < nWidth; i++)
    {
        delete[] zBuffer[i];
        zBuffer[i] = NULL;
    }
    if (zBuffer != NULL)
    {
        delete zBuffer;
        zBuffer = NULL;
    }
}

//读入三角形每个顶点的坐标、点法向量和纹理坐标
void CZBuffer::SetPoint(CP3 P0, CP3 P1, CP3 P2, CVector3 N0, CVector3 N1, CVector3 N2, CT2 T0, CT2 T1, CT2 T2)
{
    this->P0 = P0, this->P1 = P1, this->P2 = P2;
    //P0点对应的纹理坐标
    point0.x = ROUND(P0.x);
    point0.y = ROUND(P0.y);
    point0.z = P0.z;
    point0.c = P0.c;
    point0.n = N0;
    point0.t = T0;
    //P1点对应的纹理坐标
    point1.x = ROUND(P1.x);
    point1.y = ROUND(P1.y);
    point1.z = P1.z;
    point1.c = P1.c;
    point1.n = N1;
    point1.t = T1;
    //P2点对应的纹理坐标
    point2.x = ROUND(P2.x);
    point2.y = ROUND(P2.y);
    point2.z = P2.z;
    point2.c = P2.c;
    point2.n = N2;
    point2.t = T2;
}

void CZBuffer::PhongShader(CDC* pDC, CP3 ViewPoint, CLighting* pLight, CMaterial* pMaterial, CTexture* pTexture)
{
    double	CurrentDepth = 0.0;//当前扫描线的深度
    CVector3 Vector01(P0, P1), Vector02(P0, P2);
    CVector3 fNormal = CrossProduct(Vector01, Vector02);
    double A = fNormal.x, B = fNormal.y, C = fNormal.z;//平面方程Ax+By+Cz+D=0的系数
    double D = -A * P0.x - B * P0.y - C * P0.z;//当前扫描线随着x增长的深度步长
    if (fabs(C) < 1e-4)
        C = 1.0;
    double DepthStep = -A / C;//计算扫描线深度步长增量
    SortVertex();
    //定义三角形覆盖的扫描线条数
    int nTotalLine = point1.y - point0.y + 1;
    //定义span的起点与终点数组
    SpanLeft = new CPoint2[nTotalLine];
    SpanRight = new CPoint2[nTotalLine];
    //判断三角形与P0P1边的位置关系,0-1-2为右手系
    int nDeltz = (point1.x - point0.x) * (point2.y - point1.y) - (point1.y - point0.y) * (point2.x - point1.x);//点向量叉积的z坐标
    if (nDeltz > 0)//三角形位于P0P1边的左侧
    {
        nIndex = 0;
        EdgeFlag(point0, point2, TRUE);
        EdgeFlag(point2, point1, TRUE);
        nIndex = 0;
        EdgeFlag(point0, point1, FALSE);
    }
    else//三角形位于P0P1边的右侧
    {
        nIndex = 0;
        EdgeFlag(point0, point1, TRUE);
        nIndex = 0;
        EdgeFlag(point0, point2, FALSE);
        EdgeFlag(point2, point1, FALSE);
    }
    for (int y = point0.y; y < point1.y; y++)//下闭上开
    {
        int n = y - point0.y;
        BOOL bInFlag = FALSE;//跨度内外测试标志,初始值为假表示三角形外部
        for (int x = SpanLeft[n].x; x < SpanRight[n].x; x++)//左闭右开
        {
            if (bInFlag == FALSE)
            {
                CurrentDepth = -(A * x + B * y + D) / C;//z=-(Ax+By+D)/C
                bInFlag = TRUE;
                x -= 1;
            }
            else
            {
                /*
            通过对三角形跨度端点的法向量进行线性插值,得到的当前点的归一化法向量(ptNormal)
            */
                CVector3 ptNormal = LinearInterp(x, SpanLeft[n].x, SpanRight[n].x, SpanLeft[n].n, SpanRight[n].n);
                ptNormal = ptNormal.Normalize();

                /*
                通过对三角形跨度两端点的纹理坐标进行线性插值,得到当前点的纹理坐标
                */

                CT2 Texture = LinearInterp(x, SpanLeft[n].x, SpanRight[n].x, SpanLeft[n].t, SpanRight[n].t);//纹理坐标线性插值
                Texture.c = GetTexture(ROUND(Texture.u), ROUND(Texture.v), pTexture);//读取纹理颜色
                pMaterial->SetDiffuse(Texture.c);//纹理作为材质漫反射率
                pMaterial->SetAmbient(Texture.c);//纹理作为材质环境反射率
                CRGB Intensity = pLight->Illuminate(ViewPoint, CP3(x, y, CurrentDepth), ptNormal, pMaterial);//Intensity计算当前点的光强

                /*
                处理深度缓冲器,若当前点的深度值小于缓冲器中的深度值,则说明当前点离视点近,使用SetPixelV函数绘制当前					点,否则放弃当前点
                */
                if (CurrentDepth <= zBuffer[x + nWidth / 2][y + nHeight / 2])//ZBuffer算法
                {
                    zBuffer[x + nWidth / 2][y + nHeight / 2] = CurrentDepth;
                    pDC->SetPixelV(x, y, COLOR(Intensity));
                }
                CurrentDepth += DepthStep;//更新当前点的深度值
            }
        }
    }
    if (SpanLeft)
    {
        delete[]SpanLeft;
        SpanLeft = NULL;
    }
    if (SpanRight)
    {
        delete[]SpanRight;
        SpanRight = NULL;
    }
}

void CZBuffer::EdgeFlag(CPoint2 PStart, CPoint2 PEnd, BOOL bFeature)
{
    int dx = PEnd.x - PStart.x;
    int dy = PEnd.y - PStart.y;
    double m = double(dx) / dy;
    double x = PStart.x;
    /*
    对跨度两边的纹理坐标进行线性插值,并将纹理值加入跨度左右数组
    */
    for (int y = PStart.y; y < PEnd.y; y++)
    {
        CVector3 ptNormal = LinearInterp(y, PStart.y, PEnd.y, PStart.n, PEnd.n);
        CT2 Texture = LinearInterp(y, PStart.y, PEnd.y, PStart.t, PEnd.t);
        if (bFeature)
            SpanLeft[nIndex++] = CPoint2(ROUND(x), y, ptNormal, Texture);
        else
            SpanRight[nIndex++] = CPoint2(ROUND(x), y, ptNormal, Texture);
        x += m;
    }
}

void CZBuffer::SortVertex(void)
{
    CPoint3 pt[3];
    pt[0] = point0;
    pt[1] = point1;
    pt[2] = point2;
    for (int i = 0; i < 2; i++)
    {
        int min = i;
        for (int j = i + 1; j < 3; j++)
            if (pt[j].y < pt[min].y)
                min = j;
        CPoint3 pTemp = pt[i];
        pt[i] = pt[min];
        pt[min] = pTemp;
    }
    point0 = pt[0];
    point1 = pt[2];
    point2 = pt[1];
}
/*
纹理坐标插值函数
*/
CVector3 CZBuffer::LinearInterp(double t, double tStart, double tEnd, CVector3 vStart, CVector3 vEnd)//向量线性插值
{
    CVector3 vector;
    vector = (tEnd - t) / (tEnd - tStart) * vStart + (t - tStart) / (tEnd - tStart) * vEnd;
    return vector;
}

CT2 CZBuffer::LinearInterp(double t, double tStart, double tEnd, CT2 texStart, CT2 texEnd)//纹理坐标线性插值
{
    CT2 texture;
    texture = (t - tEnd) / (tStart - tEnd) * texStart + (t - tStart) / (tEnd - tStart) * texEnd;
    return texture;
}

void CZBuffer::InitialDepthBuffer(int nWidth, int nHeight, double zDepth)//初始化深度缓冲
{
    this->nWidth = nWidth, this->nHeight = nHeight;
    zBuffer = new double *[nWidth];
    for (int i = 0; i < nWidth; i++)
        zBuffer[i] = new double[nHeight];
    for (int i = 0; i < nWidth; i++)//初始化深度缓冲
        for (int j = 0; j < nHeight; j++)
            zBuffer[i][j] = zDepth;
}
/*
定义纹理读取函数,该函数使用纹理坐标读出纹素的颜色值
*/
CRGB CZBuffer::GetTexture(int u, int v, CTexture* pTexture)
{
    v = pTexture->bmp.bmHeight - 1 - v;/*从位图左下角开始读取,从而保证位图正向绘制*/
    /*检测图片的边界,防止越界*/
    if (u < 0) u = 0;/*左*/ if (v < 0) v = 0;/*下*/
    if (u > pTexture->bmp.bmWidth - 1) 	u = pTexture->bmp.bmWidth - 1;/*右*/
    if (v > pTexture->bmp.bmHeight - 1)	v = pTexture->bmp.bmHeight - 1;/*上*/
    /*查找对应纹理空间的颜色值*/
    int position = v * pTexture->bmp.bmWidthBytes + 4 * u;//循环每一列,每行读四个字节,计算纹理坐标的位置	
    /*
    颜色在内存中排列方式是BGR,所以image数组的索引position代表B,+1代表G,+2代表R
    */
    return  CRGB(pTexture->image[position + 2] / 255.0, pTexture->image[position + 1] / 255.0, pTexture->image[position] / 255.0);
}

四、整体设计步骤

回转体模型主要分为四个回转体对象,十二个控制点,分别是revoBody1坛身主体1, revoBody2坛身主体2, revoBottom坛底,和revoLid坛盖

image-20211215213034410

十二个控制点模拟四段三次贝塞尔曲线,每个回转体读入控制点(ReadCubicBezierControlPoint)分别调用回转类(CRevolution::DrawRevolutionSurface),DrawRevolutionSurface完成曲面绘制

image-20211215215301486

在线框模型的基础上,添加了光照与材质以及纹理来实现将线框模型变为实体模型,这里是光照与材质

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i0ZIwW5C-1641224164223)(https://raw.githubusercontent.com/GC-ZF/Typora-img/main/img/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A623.png)]

这里初始化光照,使用了一个光源

image-20211215215337177

这里读入位图,为每个回转体设置纹理

image-20211216001646721

这里使用的是球体图像纹理映射实现回转体的实体模型,将两张位图映射到四个对象上

在这里插入图片描述

CBezierPatch::Tessellation,这里是顶点的纹理绑定,一个回转体上贴四张图片

image-20211215234711188

(往下滑看下PhongShader)调用ZBuffer算法进行填充

SetPoint读入三角形每个顶点的坐标、点法向量和纹理坐标

SortVertex对顶点排序

这里应用了PhongShader算法(ZBuffer类),法向量线性插值和纹理坐标线性插值算法,将纹理颜色作为材质的漫反射率和材质的环境反射率

LinearInterp纹理坐标线性插值计算,(LinearInterp)得到的当前点的归一化法向量(ptNormal),(LinearInterp) 通过对三角形跨度两端点的纹理坐标进行线性插值,得到当前点的纹理坐标

根据纹理地址使用使用GetTexture,定义纹理读取函数,使用纹理坐标读出纹素的颜色值

pMaterial->SetDiffuse(Texture.c);//纹理作为材质漫反射率
pMaterial->SetAmbient(Texture.c);//纹理作为材质环境反射率
CRGB Intensity = pLight->Illuminate(ViewPoint, CP3(x, y, CurrentDepth), ptNormal, pMaterial);
//用设置为纹理颜色的材质作为这一点的参数计算他的光强
image-20211216211153505

五、总结

通过本次课程设计,了解了微软是以从左上读入位图数据。而Ctexture类中从位图左下角开始读取,目的是为了保证位图正向绘制。

PhongShader算法中,利用了向量线性插值计算,通过对纹理坐标线性插值计算得到归一化法向量。通过对三角形跨度两端点的纹理坐标进行线性插值,得到当前点的纹理坐标。

Zbuffer算法中,利用了深度缓冲器,通过计算该点的深度值来判断是否绘制该像素点

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-01-04 13:26:12  更:2022-01-04 13:26:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 1:28:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码