浅谈osgViewer::StatsHandler、osg::Stats类的用法

osg解析系列-[osg::osgViewer] (视景器) 1.osgViewer::View中默认有一个主相机,附属相机可以有0~N个。附属相机存在时,主相机负责视图控制,附属相机群负责渲染;附属相机不存在时,主相机即负责视图控制又负责渲染。 阅读详情

目录

1. 前言

2. osgViewer::StatsHandler

2.1. 功能与用法说明

2.2. 主要接口说明

3. osg::Stats

4. 附录


1. 前言

       osg为视景器的使用和调试提供了丰富的辅助组件,它们主要是以osg::ViewerBase的成员变量或交互事件处理器(osgGA::GUIEventHandler)的形式出现。osgViewer::StatsHandler、osg::Stats类就是其中的两个经常用到的辅助组件。

2. osgViewer::StatsHandler

2.1. 功能与用法说明

      osgViewer::StatsHandler类的头文件位于osg源码目录下的:

include\osgViewer\ViewerEventHandlers

文件; osgViewer::StatsHandler类的cpp文件位于osg源码目录下的:

src\osgViewer\StatsHandler.cpp

        该类的功能为向视景器osgViewer::Viewer(单视图)或osgViewer::CompositeViewer(多视图)输出屏幕统计信息。如下代码:

#include<osgViewer/Viewer>
#include<osgDB/readFile>
#include<osgViewer/ViewerEventHandlers>

int main(int argc, char* argv[])
{
    osg::ArgumentParser arguments(&argc, argv);
    osg::setNotifyLevel(osg::INFO);
    osgViewer::Viewer viewer(arguments);
    auto pNode = osgDB::readNodeFile(R"(E:\osg\OpenSceneGraph-Data\cow.osg)");
    if (nullptr == pNode)
    {
        OSG_WARN << "file not exist!\r\n";
        return 1;
    }

    auto pStatsEventHandler = new osgViewer::StatsHandler; // 构造一视景器统计事件处理器
    viewer.addEventHandler(pStatsEventHandler);  // 向视景器增加统计事件处理器
    viewer.setSceneData(pNode);
    return viewer.run();
}

为视景器对象viewer增加了一个pStatsEventHandler的统计事件处理器对象。一旦程序运行起来,默认情况下,第1次按小写s键,则只统计帧率,如下:

图1 

第2次按小写s键时,保持第1次按下s键的信息外,还统计和视景器相关的信息,如下:

图2 

当3次按下小写s键时, 保持第1、2次按下s键的信息外,还所有相机和场景相关的信息,如下:

图3 

 当4次按下小写s键时, 保持第1、2、3次按下s键的信息外,还统计所有视景器和场景相关的信息,如下:

图4

       当5次按下小写s键时,则清除图1到图4的所有输出信息,程序保证按下s的次数不会超过5,当达到5时,自动回到0,然后每次按下小写s键,自动加1,递增到5。按下大写S时,则将统计信息输出到标准控制台窗口。一个综合统计图如下所示:

图5

上述统计的信息主要信息说明如下:

参    数   名       参   数   含   义   备                                          注
Frame Rate 当前帧率在debug模式下为DEBUG Frame Rate
ThreadingModel线程模式ViewerBase::ThreadingModel枚举类型
Event一帧内交互事件耗费的时间单位:秒
Update一帧内场景更新耗费的时间单位:秒
Cull一帧内场景裁剪耗费的时间单位:秒
Draw渲染一帧耗费的时间单位:秒
GPU渲染一帧时,GPU耗费的时间单位:秒
Drawable场景内独立可绘制体总数
Geode场景内独立可绘叶节点总数
Vertices场景内独立顶点总数
Primitives场景内独立可图元数

                                                                                     表1 

因为统计的参数很多,其它参数含义请参见StatsHandler.cpp类源码。

2.2. 主要接口说明

osgViewer::StatEventHandler类主要接口说明如下:

void setKeyEventTogglesOnScreenStats(int key);
int getKeyEventTogglesOnScreenStats() const; 

上述接口功能为:设置或获取统计信息输出到屏幕的按键,默认为小写的s,可以设置为其它键,参数key是osgGA::GUIEventAdapter::KeySymbol枚举,即键盘虚拟码。如下将默认的小写s键改为数字0键作为统计信息的输出按下,此时按下0才能输出统计信息,s不再其作用:

auto pStatsEventHandler = new osgViewer::StatsHandler; // 构造一视景器统计事件处理器
pStatsEventHandler ->setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_0);

 下面接口:

void setKeyEventPrintsOutStats(int key);
int getKeyEventPrintsOutStats() const ;

功能为:设置或获取统计信息输出到控制台窗口的按键,同上面的类似,不再赘述。

void reset();

上面接口功能为:重置统计信息,此时界面上不会有任何统计信息。注意和按下默认的s键或设定的统计信息键累计5次不输出统计信息的不同:重置是将用于输出统计信息的相机的图形设备上下文对象及相机孩子对象都清空,这样会释放内存,而后者则不会。

3. osg::Stats

         视景器osgViewer::Viewer(单视图)或osgViewer::CompositeViewer(多视图)对象(为了便于后文描述,暂称为viewer)可以通过setViewerStats函数设置统计类对象。如下代码设置osg::Stats对象到视景器:

osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
auto pStats = new osg::Stats("test"); // 构造一个统计类对象,参数为统计类对象名称,最好根据业务需求取名
pStats->collectStats("scene", true);  // 统计场景数据。如果设置为false,则场景有关的数据不统计
viewer->setViewerStats(pStats); // 将统计类对象设置到视景器。


...... // 其它代码

viewer->run();

在执行视景器的run函数时会进入到ViewerBase类的renderingTraversals函数,在该函数中统计了相关信息,如下:

void ViewerBase::renderingTraversals()
{
    ...... // 其它代码略

    // 如果为视景器设置了统计对象且需要统计场景类的各种属性
    if (getViewerStats() && getViewerStats()->collectStats("scene"))
    {

        Views views;
        getViews(views);
        for(Views::iterator vitr = views.begin();
            vitr != views.end();
            ++vitr)
        {
            View* view = *vitr;
            osg::Stats* stats = view->getStats();
            osg::Node* sceneRoot = view->getSceneData();
            if (sceneRoot && stats)
            {
                osgUtil::StatsVisitor statsVisitor;
                sceneRoot->accept(statsVisitor);
                statsVisitor.totalUpStats();

                unsigned int unique_primitives = 0;
                osgUtil::Statistics::PrimitiveCountMap::iterator pcmitr;
                for(pcmitr = statsVisitor._uniqueStats.GetPrimitivesBegin();
                    pcmitr != statsVisitor._uniqueStats.GetPrimitivesEnd();
                    ++pcmitr)
                {
                    unique_primitives += pcmitr->second;
                }

                stats->setAttribute(frameNumber, "Number of unique StateSet", static_cast<double>(statsVisitor._statesetSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Group", static_cast<double>(statsVisitor._groupSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Transform", static_cast<double>(statsVisitor._transformSet.size()));
                stats->setAttribute(frameNumber, "Number of unique LOD", static_cast<double>(statsVisitor._lodSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Switch", static_cast<double>(statsVisitor._switchSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Geode", static_cast<double>(statsVisitor._geodeSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Drawable", static_cast<double>(statsVisitor._drawableSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Geometry", static_cast<double>(statsVisitor._geometrySet.size()));
                stats->setAttribute(frameNumber, "Number of unique Vertices", static_cast<double>(statsVisitor._uniqueStats._vertexCount));
                stats->setAttribute(frameNumber, "Number of unique Primitives", static_cast<double>(unique_primitives));

                unsigned int instanced_primitives = 0;
                for(pcmitr = statsVisitor._instancedStats.GetPrimitivesBegin();
                    pcmitr != statsVisitor._instancedStats.GetPrimitivesEnd();
                    ++pcmitr)
                {
                    instanced_primitives += pcmitr->second;
                }

                stats->setAttribute(frameNumber, "Number of instanced Stateset", static_cast<double>(statsVisitor._numInstancedStateSet));
                stats->setAttribute(frameNumber, "Number of instanced Group", static_cast<double>(statsVisitor._numInstancedGroup));
                stats->setAttribute(frameNumber, "Number of instanced Transform", static_cast<double>(statsVisitor._numInstancedTransform));
                stats->setAttribute(frameNumber, "Number of instanced LOD", static_cast<double>(statsVisitor._numInstancedLOD));
                stats->setAttribute(frameNumber, "Number of instanced Switch", static_cast<double>(statsVisitor._numInstancedSwitch));
                stats->setAttribute(frameNumber, "Number of instanced Geode", static_cast<double>(statsVisitor._numInstancedGeode));
                stats->setAttribute(frameNumber, "Number of instanced Drawable", static_cast<double>(statsVisitor._numInstancedDrawable));
                stats->setAttribute(frameNumber, "Number of instanced Geometry", static_cast<double>(statsVisitor._numInstancedGeometry));
                stats->setAttribute(frameNumber, "Number of instanced Vertices", static_cast<double>(statsVisitor._instancedStats._vertexCount));
                stats->setAttribute(frameNumber, "Number of instanced Primitives", static_cast<double>(instanced_primitives));
           }
        }
    }
 
...... // 其它代码略

其中以"Number of"开头的是被统计的属性名称,可以看成是对前文表1和后文表2的补充。

      获取视景器的osg::Stats对象如下:

viewer.getViewerStats();

然后使用osg::Stats对象的如下接口设置或获取指定帧索引下指定属性名的属性值:

bool setAttribute(unsigned int frameNumber, const std::string& attributeName, double value);
inline bool getAttribute(unsigned int frameNumber, const std::string& attributeName, double& value) const;

如下为获取第100帧时的帧速率:

double frameRate;
viewer.getViewerStats()->getAttribute(100, "Frame rate", frameRate);

这两个函数第2个参数表示的主要属性名如下表所示:

          属性名含                      义
Frame rate帧率
Event traversal time taken一帧内交互事件耗费的时间(单位:秒)
Update traversal time taken更新场景一帧耗费时间(单位:秒)
Cull traversal time taken一帧内场景裁剪耗费的时间(单位:秒)
GPU draw time taken渲染一帧时,GPU耗费的时间(单位:秒)
Draw traversal time taken场景内独立可绘制体总数
Number of unique Geode场景内独立的叶节点总数
Reference time系统运行事件(单位:秒)
Number of unique Geometry场景内独立几何体总数
Number of unique Vertices场景内独立顶点总数
Number of unique Primitives场景内独立图元总数

表2 

因为统计的参数很多,其它参数含义请参见StatsHandler.cpp类源码。其实osgViewer::StatEventHandler类内部就是通过osg::Stats类来实现统计信息的。

4. 附录

更多关于osg::Stats类用法请参考:osg::Stats类用法及该类源码剖析。

osg其它事件处理器用法参见如下博文:

OSG图形环境、视图与相机、FBO和视景器——Part1(官方教程后补) osg学习 阅读详情

相关推荐

osg状态信息统计的实现原理

osgViewer::Viewer首先添加osgView::StatsHandler负责统计信息的处理 osgView::StatsHandle中的handle函数具体负责s键的响应和切换。 osgViewer/StatsHandler.cpp bool StatsHandler::handle(const osgGA::GUIEventAdapter&amp; ea, osgGA::G...

hankern的专栏 1738

3D进阶之OSG:最基本的显示osgviewer

项目全部源码我还在整理,所以请暂时不要问源码的问题。 原来的培训教程在这里, OSG新版教学视频(2021)— 内容篇_哔哩哔哩_bilibili 但因为我使用VS2019, 不再使用VS2010这么老的版本,所以目前还在修改整理阶段。 这是我们实现的第一个程序序,很简单,全部源码如下, #include <CommonLib/Common/Common.h> #include <osgGA/StateSetManipulator> #include <osgVie

tanmx219的博客 5912

从Hello World到旋转地球:我的第一个OSG 3D程序踩坑实录(VS2022 + CMake)

本文详细记录了在VS2022和CMake环境下搭建第一个OSG 3D程序的全过程,从环境配置、CMake项目搭建到实现旋转地球模型的完整步骤。文章重点解决了OSG库链接、纹理加载、性能优化等常见问题,并提供了实用的调试技巧和跨平台注意事项,帮助开发者快速掌握OSG 3D图形编程。

weixin_30493401的博客 412

一、osgViewer应用基础(上)

1.打开VS,选择【新建】-【visual C++】-【win32】-【win32控制台应用程序】。 2.在解决资源方案管理器中, 右击【源文件】-【添加】-【新建项】 -【C++文件.cpp】 3. 在helloworld项目中的main.cpp文件中添加,此时运行会不成功,因为缺乏头文件,可以采用【附1的方法】成功运行,也可以采用下文所采取的新建头文件,添加依赖的方式。 #in...

qq_36119499的博客 2547

OSG —— 笔记1 - 指令调用模型

效果           关注 笔者 - jxd 微信公众号搜索 “码农总动员” 或 微信扫描下方二维码,了解更多你不知道的XX,O(∩_∩)O

2298

QT+OSG/osgEarth编译之八十五:osgViewer+Qt编译(一套代码、一套框架,跨平台编译,版本:OSG-3.6.5核心库osgViewer)

通过一套osgViewer代码和框架,实现osgViewer的跨平台编译。 在Qt环境下,集成osgViewer头文件、库文件,构建跨平台编译的pro文件。 通过构建的一套配置工程,基于Qt Creator IDE,完成跨平台的编译实践。 在Windows、Linux、MacOS等操作系统上进行测试,成功编译,形成的成果(头文件、库文件等)可在不同系统下调用或使用,从而更好地构建跨平台解决方案。 采用的是osg3.6.5版本。 读者可参考博客中的集成原理和pro文件,构建不同版本的osgViewe跨平台包。

LuckyHanMo的博客 961

osgEarth二维态势中时变对象高效绘制方法

在地理信息系统(GIS)与实时仿真领域,动态几何对象的毫秒级更新是二维态势可视化的核心挑战。其本质涉及地理坐标系投影、GPU顶点缓冲区管理及渲染管线调度等底层原理。技术价值在于突破传统静态Geometry重绘瓶颈,实现低延迟、高帧率、可扩展的动态目标渲染。典型应用场景包括军事指挥系统中的无人机轨迹跟踪、城市交通流实时监控以及应急救援资源动态调度。本文聚焦osgEarth框架下时变对象的轻量级绘制优化,深度融合osgEarth地理一致性保障与osg底层图形更新机制,重点解决位置跳变、朝向抖动与批量卡顿三大工程

weixin_33979203的博客 562

Android:Google三方库之Adjust集成详细步骤

Android为APP添加google adjust功能

学习就是我的个性 4476

osgViewer中的ScreenCaptureHandler、LODScaleHandler、HelpHandler事件处理器用法

osgViewer中的ScreenCaptureHandler、LODScaleHandler、HelpHandler事件处理器用法

danshiming的博客 1275

用python将dycom .dcm格式图像转换为nifti .nii

因为发现目前搜索到的多数中文教程并不能正确将FMRI图像正确转换,例如很多用simpleITk的seriesreader的,所以写了一下自己正确转换的方法。因为FMRI是一个4D数据,有时间轴,所以用seriesreader读写出来的图像把时间轴维度给降维了。当然你用SPM12工具的dycom import也是能正确转换的,但是我还是喜欢python这样的,代码直接简单。

qq_41103479的博客 3609

Android keymint(keymaster)一RKP

Android RKP

weixin_47139576的博客 1万+

osg的加载osgb数据(一)

osgArchive: 类似于zip,可将多个.osg模型打包成一个.osga的模型压缩包;单个的osgb文件我们大概了解了,osgb的data全文件呢?osgViewer: 四个程序当中用的最多的 OSG 程序,用于查看模型;osgVersion: 查看osg库版本;前提条件是假设大家已经完成了osg的编译。osgConv: 模型格式转换与压缩;

原在路上的博客。 1451

双参数威布尔分布(Weibull分布)参数计算(根据故障数据,求双参数威布尔分布的参数详细步骤)

根据故障数据求威布尔分布参数步骤 根据众多文献研究,设备故障数据大多服从指数分布、威布尔分布或对数正态分布。其中威布尔分布较为常用,本文主要介绍威布尔分布的简单应用,并根据某设备故障数据为例简单介绍威布尔分布参数的计算方法。 参考文献11参考文献22 1.双参数威布尔分布的概率密度函数、概率分布函数、可靠度函数: 2.预处理故障数据 将故障数据按时间排序,假设设备分别在以下时间点失效:11.1 、23.5、58.4……则将此时间从小到大排序。并计算中位秩。中位秩的计算公式如下: 其中i表示第几个数据,n

biaochengxiaobai的博客 4万+

2、《OSG快速入门指导》详细笔记1

OSG安装目录是在编译源码时指定的,会将相关的结果向这个目录输出,方便直接获取,设置环境变量等。在bin目录中OSG提供了可以直接运行的执行程序,他们都有各自的功能。上面的代码中仅仅执行了三个步骤:1、创建了顶点、法线和颜色数据的数组。2、将osg::Geometry对象实例化,并将上述数组添加到对象中。同时还添加了一个osg::DrawArrays对象以指定数据绘制的方式。3、将osg::Geode场景图形节点实例化,并将几何信息添加到其中。1、向量与数组类。

qq_44918555的博客 4866

用Python实现一个简单的远程桌面服务端和客户端

本文介绍了一个基于Python的轻量级远程桌面解决方案,针对商业软件在嵌入式系统上的兼容性问题、性能问题和成本问题。系统采用客户端-服务端架构,包含两个通信通道:5000端口传输压缩视频流,5001端口传输控制事件。通过多线程设计、图像压缩(JPEG+zlib)和网络优化(禁用Nagle算法)等技术手段提高性能。服务端实现了高效的屏幕捕获(使用mss库)、图像处理流水线和事件处理逻辑。客户端采用PyQt5构建UI界面,包含视频接收线程以减少主线程负担。该方案具有跨平台、开源免费、轻量级(核心代码不到500行

Hi20240217的博客 1665

从编解码芯片看Tesla T4的隐藏实力:双NVDEC如何实现70路1080P解码?

本文深入剖析了英伟达Tesla T4显卡的隐藏实力,重点解读其独特的双NVDEC硬件解码器设计如何突破性能瓶颈。通过分析Turing架构、对比HEVC/H.264解码差异,并结合驱动与SDK优化,揭示了实现高达70路1080P视频流并发解码的原理与实战配置,为云游戏、直播转码等高并发场景提供高效解决方案。

weixin_29209801的博客 486

【OSG学习笔记】Day 3: OSG 实用工具

OpenSceneGraph(OSG)提供了一系列开箱即用的命令行工具,能极大提升 3D 场景开发与调试效率。本文将详细介绍 **osgViewer、osgVersion、osgArchive、osgConv** 这四个核心工具,涵盖功能、用法、实战案例,帮你快速掌握 OSG 生态的高效工作流。

u012739527的博客 791

【S32K3 MCAL配置】-3.4-CANFD配置-接收特定的“经典CAN/CANFD标准帧”和“经典CAN/CANFD扩展帧”(基于MCAL+FreeRTOS)

【S32K3 MCAL配置】-3.4-CANFD配置-接收特定的“经典CAN/CANFD标准帧”和“经典CAN/CANFD扩展帧”(基于MCAL+FreeRTOS)

「汽车电子助手」的博客 696

osg解析系列-需要将osg嵌入其他平台窗口时(MFC/QT/X),请使用osgViewer::GraphicsWindowEmbedded

SceneView已弃用,还保留它仅为了向后兼容。需要嵌入式渲染支持时,建议使用 osgViewer::Viewer 和 osgViewer::GraphicsWindowEmbedded组合使用,osgViewer::GraphicsWindowEmbedded提供了更大范围的功能和一致性的API。

qq_42531872的博客 857

【单智能体】AI 数据分析智能体案例讲解(附完整源码)

本案例将引导您构建一个基于 AI 的数据分析智能体,让用户能够通过自然语言查询来分析 CSV 和 Excel 文件中的数据,无需掌握 SQL 知识。该智能体使用 Agno Agent 框架集成 OpenAI GPT-4o 模型,并利用 DuckDB 提供高效的数据处理能力。

rengang66的博客 239

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

VS 2019 默认API 窗口

// Windows_API.cpp : 定义应用程序的入口点。 // #define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 // Windows 头文件 #include <windows.h> // C 运行时头文件 #include <stdlib.h> #include <mal...

TRW777 1416

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

OSG一个最简单的viewer

#include"pch.h"//main.cpp #include <osgViewer/Viewer> #include <osgDB/ReadFile> using namespace std; int main() { osg::ref_ptr<osgViewer::Viewer> viewer=new osgViewer::Viewer; osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("gl

qq_40433221的博客 744

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

1、osgViewer::Viewer,osgViewer::ViewerBase,osgGA::KeySwitchMatrixManipulator,osg::ArgumentParser的说明

一、osgViewer::Viewer类 Viewer() Viewer (osg::ArgumentParser &arguments) Viewer (const osgViewer::Viewer &viewer, const osg::CopyOp &copyop=osg::CopyOp::SHALLOW_COPY)说明:这三个函数是osgViewer::Viewer的构造函数。其中

sxj的专栏 2218

OSG教程:场景保存类Viewer

来源:第三维度     作者:FreeSouth[杨石兴]编著     选自《OSG程序设计教程》第二章第四节     osgViewer::Viewer     类描述: 为一个单独的场景[scene]保存一个单独的view.     类中方法:     1:Viewer()     2:Viewer (osg::ArgumentParser &arguments)

TimothyFly的专栏 5867

osg多线程_osgViewer:: Viewer::advance() osg多线程与智能指针

void ViewerBase::frame(doublesimulationTime){if (_done) return;//OSG_NOTICE<if(_firstFrame){viewerInit();if (!isRealized()){realize();}_firstFrame= false;}advance(simulationTime);eventTraversal();u...

weixin_29491259的博客 599

OSG3.4.0碰撞检测(下)

写在前面的话:笔者新手学习OpenSceneGraph3.4.0(OSG),并且C++的基础较差,OpenGL的基础也较差,都没有系统性科学性地学习过。由于毕业论文需要,所以突击学习,在网上各种论坛和各种博客里找不到太多有价值的参考代码,OSG本身的源码又看不懂。总之,想了很多办法,最近取得一点学习进展,现将osgViewer 碰撞检测知识点代码所学积累都公布出来,供小白使用。如果觉得有点用请留言...

Code与我 2138
上一篇: glBeginQuery/glEndQuery/glQueryCounter获取GPU执行指令耗时
下一篇: osg::Drawable类通过setDrawCallback函数设置回调函数的说明
荆楚闲人
博客等级 码龄20年 3万+粉丝 473原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值