iphone-OpenGL ES 教程-07--西蒙

 OpenGL ES 07 – 对物体进行独立的转换









起初,我想下一章介绍光照的,但是我又想到别的。我们依然有很多基本对象及转换没有完成,特 别是我们在不同的坐标系中对不同的物体进行转换。

记得我们如何在整个屏幕内使用 glTranslatef() 和 glRotatef ()?如果我想作一些不同的事情,我就会使用一个很方便的函数: glLoadIdentity().但是 glLoadIdentity()是“代价昂贵”在屏幕渲染技术中,所以现在,我会采用更有效的方法。

要作到这点,我将介绍一个新的对 象。我们将增加一个金字塔,并且使用 glTranslatef()来移动它,使用 glRotatef()来旋转它。和立方体独立开,并且不使用 glLoadIdentity() 来重置矩阵。

在我们的世界里增加一个金字塔
这不是仅仅增加一个三角形,让我们坚持使用3d物 体,我们会增加一个金字塔(想像一下Giza平原上的大石头)

有件事,我在以前的教程中避免去说明的,就是我们创建的立方体事一个复杂对 象,即物体是由一个以上的原点组成的。从技术上来说,我们的矩形也是一个复杂物体,但我们只使用了单一的OpenGL的函数去绘制它,我们可以将其看为一 个简单物体,我避免说明复杂物体是因为我不想让我们的工作看起来是一个“艰苦的工作”

现在你已经完成了一个复杂物体了。恭喜你!并且,现 在是时候建立第二个了。

金字塔不是难事。它是由一个矩形以及会聚到这个矩形上面的中心点的4个三角形组成的。一旦你将物体可以分解为你所 认知的简单图元,任何复杂的物体都不是问题。唯一的变化是你物体中图元的数目而已。

好,开启Xcode并且开启上个教程的工程。这次不要 删除任何东西,我们增加代码来构成一个平行线。

为了帮助你组成金字塔,我们需要分解它的各个组成部分,让我们开始:

    const GLfloat pyramidVertices[] = {
        // Our pyramid consists of 4 triangles and a square base.
        // We'll start with the square base
        -1.0, -1.0, 1.0,            // front left of base
        1.0, -1.0, 1.0,             // front right of base
        1.0, -1.0, -1.0,            // rear left of base
        -1.0, -1.0, -1.0,           // rear right of base

因此,我们创建了一个新的物体,和我们作的一样。这个矩形 是底座,所有的y坐标都是-1.0。

现在,我们可以创建金字塔的正面,这次我们创建的三角形而不是矩形。

        // Front face
        -1.0, -1.0, 1.0,            // bottom left of triangle
        1.0, -1.0, 1.0,             // bottom right
        0.0, 1.0, 0.0,              // top centre -- all triangle vertices
                                    //     will meet here

这是我们创建的唯一的真实三角形,这个三角形的角在矩形的前边,然后向后倾斜,上面的点在矩形的中心上面 (在x轴线上)

我们继续创建其他的三个三角形,使用相同的方法,下面是完整的顶点数组。

 
// Our new object definition code goes here
    const GLfloat pyramidVertices[] = {
        // Our pyramid consists of 4 triangles and a square base.
        // We'll start with the square base
        -1.0, -1.0, 1.0,            // front left of base
        1.0, -1.0, 1.0,             // front right of base
        1.0, -1.0, -1.0,            // rear left of base
        -1.0, -1.0, -1.0,           // rear right of base
 
        // Front face
        -1.0, -1.0, 1.0,            // bottom left of triangle
        1.0, -1.0, 1.0,             // bottom right
        0.0, 1.0, 0.0,              // top centre -- all triangle vertices
                                    //     will meet here
 
        // Rear face
        1.0, -1.0, -1.0,   // bottom right (when viewed through front face)
        -1.0, -1.0, -1.0,           // bottom left
        0.0, 1.0, 0.0,              // top centre
 
        // left face
        -1.0, -1.0, -1.0,           // bottom rear
        -1.0, -1.0, 1.0,            // bottom front
        0.0, 1.0, 0.0,              // top centre
 
        // right face
        1.0, -1.0, 1.0,             // bottom front
        1.0, -1.0, -1.0,            // bottom rear
        0.0, 1.0, 0.0               // top centre
    };
 
 

增加金字塔的定义到 drawView 方法里和 cubeVerticies[] 的定义在一起。

在我们继续前,我想先说明下金字塔的定义。

首先,这是第一个我们同时提 供了三角形及矩形的对象。这个金字塔是由一个矩形和4个三角形组成的。因为我们调用glDrawArrays()为每个单独的图元,但这不防碍我们每次使 用单独的定义来绘制不同的图元。那么,当我们绘制金字塔,一切都变的清楚了。

第2,再一次的提醒。我已指定所有的顶点是逆时针的。即使后 面看起来似乎是顺时针的,但是从OpenGL的角度看,它还是逆时针的。

其实,我想在我3d图形教学结束前,我们会一直讨论这个问题。

绘 制金字塔
现在低头去找立方体的代码。首先,删除glLoadIdentity() 这行,它已经不需要了。

在 rota+=0.5以后,我们增加绘制金字塔的代码。现在,我们要在不影响立方体的情况下,使用glTranslatef() 去移动金字塔,并且使用 glRotatef() 去旋转金字塔。为了做到这点,我们需要在不影响其他图元的情况下使用 glTranslatef() 和 glRotatef().

OpenGL 为我们使用这两个函数提供了快捷的方法。

    glPushMatrix();

    // Translation and drawing code goes here....

    glPopMatrix();

OpenGL 告诉我们如果要将我们的矩阵变换放如到堆栈中就要使用 glPushMatrix().我将我们的pyramidVertices[] 和 cubeVertices[] 目标 “推” 到 OpenGL’s 的堆栈中.

Note: 如果你不明白我说的堆栈是什么意思,那么你可能需要一本c或者object c 的工具书。

所以,我们的数据复制是安全的,我们可以象其 他OpenGL大师一样进行转换了。让我们开始绘制这个金字塔吧!

    glPushMatrix();
    {
        glTranslatef(-2.0, 0.0, -8.0);
        glRotatef(rota, 1.0, 0.0, 0.0);
        glVertexPointer(3, GL_FLOAT, 0, pyramidVertices);
        glEnableClientState(GL_VERTEX_ARRAY);

首 先要指出的是, glPushMatrix()后面的那个{.这个不是必要的,但是你在学习的时候最好加上它,{}内表示了被推入堆栈和弹出堆栈之间做的工作。

在 glPushMatrix() 后面的四行,我马上解释给你听. 所有我们所做的就是要求glTranslatef ( )把金字塔远离( 0 , 0 , 0 )的左侧和回8点到屏幕(进一步远离观众) 。那么我们的金字塔周围旋转的X轴,而不是所有三个轴计算的立方体的例子在过去的教程。最后,我们只要告诉OpenGL的有关数据,并使它能够被使用。

好 了,在我们做好坐标转换以后,我们开始绘制金字塔。

        // Draw the pyramid
        // Draw the base -- it's a square remember
        glColor4f(1.0, 0.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

前四个坐标表 示了我们金字塔的底部。我们将它的颜色设置为红色(也可以进行纹理映射),并且使用 GL_TRIANGLE_FAN 来绘制矩形。我们是从(pyramidVerticies[0~3]),使用4个的顶点。

Ok,我们完成矩形了,现在开始绘制第一个正面 的三角形。

        // Front Face
        glColor4f(0.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 4, 3);

除里改变颜色,我们改变绘制模 式为 GL_TRIANGLES的原因是显然的,我们已经从数组的第4个(pyramidVerticies[4~6])开始,延伸3个顶点来绘制。所以,你可 以看到,一个矩形和一个三角形可以很容易的共存于一个数据结构里。

下一步,我们继续绘制我们其他三个三角形:

        // Rear Face
        glColor4f(0.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 7, 3);
        
        // Right Face
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 10, 3);
        
        // Left Face
        glColor4f(1.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 13, 3);
    }
    glPopMatrix();

每次,我们只是改变颜色,并改变开始的偏移值。 OpenGL知道每个顶点是由三个坐标组成的,通过我们的 glVertexPointer() 函数。

最后,我们关闭},并且调用 glPopMatrix().

现在,在这点上我们可以绘制立方体。但是,请注意,你可以一遍又一遍的重复绘制,只需要将绘制函数放在 glPushMatrix() 和 glPopMatrix()中间.

绘制立方体-修订
这里是绘制立方体的完整代码。最大的改变 就是代码头尾的函数 glPushMatrix() 和 glPopMatrix()


 
    glPushMatrix();
    {
        glTranslatef(2.0, 0.0, -8.0);
        glRotatef(rota, 1.0, 1.0, 1.0);
        glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
        glEnableClientState(GL_VERTEX_ARRAY);
 
        // Draw the front face in Red
        glColor4f(1.0, 0.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
        // Draw the top face in green
        glColor4f(0.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
 
        // Draw the rear face in Blue
        glColor4f(0.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
 
        // Draw the bottom face
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
 
        // Draw the left face
        glColor4f(0.0, 1.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
 
        // Draw the right face
        glColor4f(1.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
    }
    glPopMatrix();
 

其他的变化只有 glTranslatef 的参数。
这 里是完整的drawView函数。

 
- (void)drawView {
 
    // Our new object definition code goes here
    const GLfloat pyramidVertices[] = {
        // Our pyramid consists of 4 triangles and a square base.
        // We'll start with the square base
        -1.0, -1.0, 1.0,            // front left of base
        1.0, -1.0, 1.0,             // front right of base
        1.0, -1.0, -1.0,            // rear left of base
        -1.0, -1.0, -1.0,           // rear right of base
 
        // Front face
        -1.0, -1.0, 1.0,            // bottom left of triangle
        1.0, -1.0, 1.0,             // bottom right
        0.0, 1.0, 0.0,              // top centre -- all triangle vertices
                                    //     will meet here
 
        // Rear face
        1.0, -1.0, -1.0,     // bottom right (when viewed through front face)
        -1.0, -1.0, -1.0,           // bottom left
        0.0, 1.0, 0.0,              // top centre
 
        // left face
        -1.0, -1.0, -1.0,           // bottom rear
        -1.0, -1.0, 1.0,            // bottom front
        0.0, 1.0, 0.0,              // top centre
 
        // right face
        1.0, -1.0, 1.0,             // bottom front
        1.0, -1.0, -1.0,            // bottom rear
        0.0, 1.0, 0.0               // top centre
    };
 
    const GLfloat cubeVertices[] = {
 
        // Define the front face
        -1.0, 1.0, 1.0,             // top left
        -1.0, -1.0, 1.0,            // bottom left
        1.0, -1.0, 1.0,             // bottom right
        1.0, 1.0, 1.0,              // top right
 
        // Top face
        -1.0, 1.0, -1.0,            // top left (at rear)
        -1.0, 1.0, 1.0,             // bottom left (at front)
        1.0, 1.0, 1.0,              // bottom right (at front)
        1.0, 1.0, -1.0,             // top right (at rear)
 
        // Rear face
        1.0, 1.0, -1.0,             // top right (when viewed from front)
        1.0, -1.0, -1.0,            // bottom right
        -1.0, -1.0, -1.0,           // bottom left
        -1.0, 1.0, -1.0,            // top left
 
        // bottom face
        -1.0, -1.0, 1.0,
        -1.0, -1.0, -1.0,
        1.0, -1.0, -1.0,
        1.0, -1.0, 1.0,
 
        // left face
        -1.0, 1.0, -1.0,
        -1.0, 1.0, 1.0,
        -1.0, -1.0, 1.0,
        -1.0, -1.0, -1.0,
 
        // right face
        1.0, 1.0, 1.0,
        1.0, 1.0, -1.0,
        1.0, -1.0, -1.0,
        1.0, -1.0, 1.0
    };
 
    const GLshort squareTextureCoords[] = {
        // Front face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
 
        // Top face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
 
        // Rear face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
 
        // Bottom face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
 
        // Left face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
 
        // Right face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
    };
 
    [EAGLContext setCurrentContext:context];    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    glMatrixMode(GL_MODELVIEW);
 
    glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
    // Our new drawing code goes here
    rota += 0.5;
 
    glPushMatrix();
    {
        glTranslatef(-2.0, 0.0, -8.0);
        glRotatef(rota, 1.0, 0.0, 0.0);
        glVertexPointer(3, GL_FLOAT, 0, pyramidVertices);
        glEnableClientState(GL_VERTEX_ARRAY);
 
        // Draw the pyramid
        // Draw the base -- it's a square remember
        glColor4f(1.0, 0.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
        // Front Face
        glColor4f(0.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 4, 3);
 
        // Rear Face
        glColor4f(0.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 7, 3);
 
        // Right Face
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 10, 3);
 
        // Left Face
        glColor4f(1.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLES, 13, 3);
    }
    glPopMatrix();
 
    glPushMatrix();
    {
        glTranslatef(2.0, 0.0, -8.0);
        glRotatef(rota, 1.0, 1.0, 1.0);
        glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
        glEnableClientState(GL_VERTEX_ARRAY);
 
        // Draw the front face in Red
        glColor4f(1.0, 0.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
        // Draw the top face in green
        glColor4f(0.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
 
        // Draw the rear face in Blue
        glColor4f(0.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
 
        // Draw the bottom face
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
 
        // Draw the left face
        glColor4f(0.0, 1.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
 
        // Draw the right face
        glColor4f(1.0, 0.0, 1.0, 1.0);
        glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
    }
    glPopMatrix();
 
 
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
 
    [self checkGLError:NO];
}
 
 


请注意,我们并没有为金字塔添加一个新的纹理坐标数 组。我们可以使用相同的坐标数组,因为它依然为这个范例工作。

改变好了以后,点击”Build an Go”




你所有需要主要的是,这两个物体正在独立的改变自己。金字塔只沿x轴旋转,而立方体沿3轴旋转。再次说明,颜色只是让你更容易的分辨每 个面。

采用3d概念:法线
记不记得我早些时候说过,要保证每个三角形都是逆时针的,即使有些背面的三角形从正面看起来象是顺时针 的?

恩,这是因为你要保证你每个面的数据都是“标准”的。简单的说,一个面的法线是一个假想的线从物体的面抽出。为了说明这点,请看下面 的图:




在 上面的图片里,这个三角形的法线就是一条从面出来的线。当前的三角形的法线表示,这个三角形的3个顶点是逆时针的。我会在介绍光照的那章里说明什么是法 线。

(在这里我真的要说说,我真的很佩服这教程的原作者,他居然可以将如此简单的代码说的如此的复杂,请注意,我没有一点的贬义,而是对 他非常的敬佩。明明每个教程只有几行代码,他确不怕麻烦的一次又一次的说明其中的概念,解释每一行代码的表面意思和内部的概念。这样细致的工作打个比方来 说,就是一个博士生在细心的教幼儿园小朋友唱歌。不是一般人可以做到的。比我们所阅读到的其他OpenGL书籍更加的认真,也更加的细致。这也是我继续翻 译下去的主要动力。)

智慧旅游解决方案利用云计算、物联网和移动互联网技术,通过便携终端设备,实现对旅游资源、经济、活动和旅游者信息的智能感知和发布。这种技术的应用旨在提升游客在旅游各个环节的体验,使他们能够轻松获取信息、规划行程、预订票务和安排食宿。智慧旅游平台为旅游管理部门、企业和游客提供服务,包括政策发布、行政管理、景区安全、游客流量统计分析、投诉反馈等。此外,平台还提供广告促销、库存信息、景点介绍、电子门票、社交互动等功能。 智慧旅游的建设规划得到了国家政策的支持,如《国家中长期科技发展规划纲要》和国务院的《关于加快发展旅游业的意见》,这些政策强调了旅游信息服务平台的建设和信息化服务的重要性。随着技术的成熟和政策环境的优化,智慧旅游的时机已经到来。 智慧旅游平台采用SaaS、PaaS和IaaS等云服务模式,提供简化的软件开发、测试和部署环境,实现资源的按需配置和快速部署。这些服务模式支持旅游企业、消费者和管理部门开发高性能、高可扩展的应用服务。平台还整合了旅游信息资源,提供了丰富的旅游产品创意平台和统一的旅游综合信息库。 智慧旅游融合应用面向游客和景区景点主管机构,提供无线城市门户、智能导游、智能门票及优惠券、景区综合安防、车辆及停车场管理等服务。这些应用通过物联网和云计算技术,实现了旅游服务的智能化、个性化和协同化,提高了旅游服务的自由度和信息共享的动态性。 智慧旅游的发展标志着旅游信息化建设的智能化和应用多样化趋势,多种技术和应用交叉渗透至旅游行业的各个方面,预示着全面的智慧旅游时代已经到来。智慧旅游不仅提升了游客的旅游体验,也为旅游管理和服务提供了高效的技术支持。
智慧旅游解决方案利用云计算、物联网和移动互联网技术,通过便携终端设备,实现对旅游资源、经济、活动和旅游者信息的智能感知和发布。这种技术的应用旨在提升游客在旅游各个环节的体验,使他们能够轻松获取信息、规划行程、预订票务和安排食宿。智慧旅游平台为旅游管理部门、企业和游客提供服务,包括政策发布、行政管理、景区安全、游客流量统计分析、投诉反馈等。此外,平台还提供广告促销、库存信息、景点介绍、电子门票、社交互动等功能。 智慧旅游的建设规划得到了国家政策的支持,如《国家中长期科技发展规划纲要》和国务院的《关于加快发展旅游业的意见》,这些政策强调了旅游信息服务平台的建设和信息化服务的重要性。随着技术的成熟和政策环境的优化,智慧旅游的时机已经到来。 智慧旅游平台采用SaaS、PaaS和IaaS等云服务模式,提供简化的软件开发、测试和部署环境,实现资源的按需配置和快速部署。这些服务模式支持旅游企业、消费者和管理部门开发高性能、高可扩展的应用服务。平台还整合了旅游信息资源,提供了丰富的旅游产品创意平台和统一的旅游综合信息库。 智慧旅游融合应用面向游客和景区景点主管机构,提供无线城市门户、智能导游、智能门票及优惠券、景区综合安防、车辆及停车场管理等服务。这些应用通过物联网和云计算技术,实现了旅游服务的智能化、个性化和协同化,提高了旅游服务的自由度和信息共享的动态性。 智慧旅游的发展标志着旅游信息化建设的智能化和应用多样化趋势,多种技术和应用交叉渗透至旅游行业的各个方面,预示着全面的智慧旅游时代已经到来。智慧旅游不仅提升了游客的旅游体验,也为旅游管理和服务提供了高效的技术支持。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值