VTK:绘制等高线的C/C++用法

65 篇文章 ¥59.90 ¥99.00
本文介绍了如何在C/C++环境中利用VTK库读取数据文件并绘制等高线,通过详细步骤及源代码展示了数据可视化的实现过程,包括使用vtkImageData加载数据、设置等值线参数、创建映射器和演员,以及设置渲染器和交互器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

等高线(Contour)是一种常用的数据可视化技术,用于展示二维或三维数据的等值线。在C/C++中,我们可以使用Visualization Toolkit(VTK)库来实现等高线的绘制。本文将详细介绍如何在C/C++中使用VTK库来绘制等高线,并提供相应的源代码示例。

首先,我们需要安装和配置VTK库。确保已经正确安装了VTK,并将其包含在项目中。

接下来,我们将展示如何使用VTK库来读取数据文件并绘制等高线。假设我们有一个二维数据文件,其中包含一些高程数据。我们将使用VTK中的vtkImageData类来加载和处理数据。

#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#
/*========================================================================= Program: Visualization Toolkit Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include <vtkActor.h> #include <vtkCamera.h> #include <vtkInteractorStyleTrackballCamera.h> #include <vtkJPEGReader.h> #include <vtkLight.h> #include <vtkNew.h> #include <vtkOpenGLPolyDataMapper.h> #include <vtkPolyDataNormals.h> #include <vtkProperty.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkRenderer.h> #include <vtkSkybox.h> #include <vtkTexture.h> #include <vtkVersion.h> #include <vtkXMLPolyDataReader.h> #if VTK_VERSION_NUMBER >= 89000000000ULL #define USE_SHADER_PROPERTIES 1 #include <vtkShaderProperty.h> #endif //---------------------------------------------------------------------------- int main(int argc, char* argv[]) { if (argc < 3) { std::cout << "Usage: " << argv[0] << " file.vtp wintersun.jpg" << std::endl; return EXIT_SUCCESS; } vtkNew<vtkRenderer> renderer; renderer->SetBackground(0.0, 0.0, 0.0); vtkNew<vtkRenderWindow> renderWindow; renderWindow->SetSize(640, 480); renderWindow->AddRenderer(renderer); vtkNew<vtkRenderWindowInteractor> interactor; interactor->SetRenderWindow(renderWindow); vtkNew<vtkLight> light; light->SetLightTypeToSceneLight(); light->SetPosition(1.0, 7.0, 1.0); renderer->AddLight(light); vtkNew<vtkXMLPolyDataReader> reader; reader->SetFileName(argv[1]); vtkNew<vtkPolyDataNormals> norms; norms->SetInputConnection(reader->GetOutputPort()); vtkNew<vtkTexture> texture; texture->InterpolateOn(); // mipmapping works on many systems but is not // core 3.2 for cube maps. VTK will silently // ignore it if it is not supported. We commented it // out here to make valid images easier // texture->MipmapOn(); vtkNew<vtkJPEGReader> imgReader; imgReader->SetFileName(argv[2]); texture->SetInputConnection(imgReader->GetOutputPort()); vtkNew<vtkOpenGLPolyDataMapper> mapper; mapper->SetInputConnection(norms->GetOutputPort()); vtkNew<vtkActor> actor; actor->SetPosition(0, 0, 0); actor->SetScale(6.0, 6.0, 6.0); actor->GetProperty()->SetSpecular(0.8); actor->GetProperty()->SetSpecularPower(20); actor->GetProperty()->SetDiffuse(0.1); actor->GetProperty()->SetAmbient(0.1); actor->GetProperty()->SetDiffuseColor(1.0, 0.0, 0.4); actor->GetProperty()->SetAmbientColor(0.4, 0.0, 1.0); renderer->AddActor(actor); actor->SetTexture(texture); actor->SetMapper(mapper); #if USE_SHADER_PROPERTIES vtkShaderProperty* sp = actor->GetShaderProperty(); sp->AddVertexShaderReplacement( "//VTK::PositionVC::Dec", // replace true, // before the standard replacements "//VTK::PositionVC::Dec\n" // we still want the default "out vec3 TexCoords;\n", false // only do it once ); #else mapper->AddShaderReplacement( vtkShader::Vertex, "//VTK::PositionVC::Dec", // replace true, // before the standard replacements "//VTK::PositionVC::Dec\n" // we still want the default "out vec3 TexCoords;\n", false // only do it once ); #endif #if USE_SHADER_PROPERTIES sp->AddVertexShaderReplacement( "//VTK::PositionVC::Impl", // replace true, // before the standard replacements "//VTK::PositionVC::Impl\n" // we still want the default "vec3 camPos = -MCVCMatrix[3].xyz * mat3(MCVCMatrix);\n" "TexCoords.xyz = reflect(vertexMC.xyz - camPos, normalize(normalMC));\n", false // only do it once ); #else mapper->AddShaderReplacement( vtkShader::Vertex, "//VTK::PositionVC::Impl", // replace true, // before the standard replacements "//VTK::PositionVC::Impl\n" // we still want the default "vec3 camPos = -MCVCMatrix[3].xyz * mat3(MCVCMatrix);\n" "TexCoords.xyz = reflect(vertexMC.xyz - camPos, normalize(normalMC));\n", false // only do it once ); #endif #if USE_SHADER_PROPERTIES sp->AddFragmentShaderReplacement( "//VTK::Light::Dec", // replace true, // before the standard replacements "//VTK::Light::Dec\n" // we still want the default "in vec3 TexCoords;\n", false // only do it once ); #else mapper->AddShaderReplacement( vtkShader::Fragment, "//VTK::Light::Dec", // replace true, // before the standard replacements "//VTK::Light::Dec\n" // we still want the default "in vec3 TexCoords;\n", false // only do it once ); #endif #if USE_SHADER_PROPERTIES sp->AddFragmentShaderReplacement( "//VTK::Light::Impl", // replace true, // before the standard replacements "//VTK::Light::Impl\n" " float phix = length(vec2(TexCoords.x, TexCoords.z));\n" " vec3 skyColor = texture(actortexture, vec2(0.5*atan(TexCoords.z, " "TexCoords.x)/3.1415927 + 0.5, atan(TexCoords.y,phix)/3.1415927 + " "0.5)).xyz;\n" " gl_FragData[0] = vec4(ambientColor + diffuse + specular + " "specularColor*skyColor, opacity);\n", // we still want the default false // only do it once ); #else mapper->AddShaderReplacement( vtkShader::Fragment, "//VTK::Light::Impl", // replace true, // before the standard replacements "//VTK::Light::Impl\n" " float phix = length(vec2(TexCoords.x, TexCoords.z));\n" " vec3 skyColor = texture(actortexture, vec2(0.5*atan(TexCoords.z, " "TexCoords.x)/3.1415927 + 0.5, atan(TexCoords.y,phix)/3.1415927 + " "0.5)).xyz;\n" " gl_FragData[0] = vec4(ambientColor + diffuse + specular + " "specularColor*skyColor, opacity);\n", // we still want the default false // only do it once ); #endif vtkNew<vtkSkybox> world; world->SetProjectionToSphere(); world->SetTexture(texture); renderer->AddActor(world); renderer->GetActiveCamera()->SetPosition(0.0, 0.55, 2.0); renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.55, 0.0); renderer->GetActiveCamera()->SetViewAngle(60.0); renderer->ResetCamera(); renderer->GetActiveCamera()->Zoom(1.1); renderer->GetActiveCamera()->Azimuth(0); renderer->GetActiveCamera()->Elevation(5); renderer->ResetCameraClippingRange(); renderWindow->Render(); renderWindow->SetWindowName("SphereMap"); renderWindow->Render(); vtkNew<vtkInteractorStyleTrackballCamera> style; renderWindow->GetInteractor()->SetInteractorStyle(style); interactor->Start(); return EXIT_SUCCESS; } 详细解析下这个代码
最新发布
07-05
<think>这个代码是一个使用VTK(Visualization Toolkit)库的C++程序,用于创建一个3D渲染场景,其中包含一个从文件中读取的3D模型,并应用了环境贴图(天空盒)和反射效果。以下是对代码的详细解析: ### 1. 头文件和宏定义 ```cpp #include <vtkActor.h> #include <vtkCamera.h> #include <vtkInteractorStyleTrackballCamera.h> #include <vtkJPEGReader.h> #include <vtkLight.h> #include <vtkNew.h> #include <vtkOpenGLPolyDataMapper.h> #include <vtkPolyDataNormals.h> #include <vtkProperty.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkRenderer.h> #include <vtkSkybox.h> #include <vtkTexture.h> #include <vtkVersion.h> #include <vtkXMLPolyDataReader.h> #if VTK_VERSION_NUMBER >= 89000000000ULL #define USE_SHADER_PROPERTIES 1 #include <vtkShaderProperty.h> #endif ``` - 包含了VTK的各种头文件,用于创建渲染管道、读取文件、设置光照等。 - 宏`USE_SHADER_PROPERTIES`用于检查VTK版本是否大于等于8.9(版本号比较方式:8.9.0 -> 89000000000),如果是则使用`vtkShaderProperty`来管理着色器替换,否则使用`vtkOpenGLPolyDataMapper`的着色器替换方法。 ### 2. 主函数 ```cpp int main(int argc, char* argv[]) { if (argc < 3) { std::cout << "Usage: " << argv[0] << " file.vtp wintersun.jpg" << std::endl; return EXIT_SUCCESS; } ``` - 检查命令行参数,需要两个参数:一个.vtp文件(包含3D模型)和一个.jpg文件(用于纹理贴图)。 ### 3. 创建渲染窗口和交互器 ```cpp vtkNew<vtkRenderer> renderer; renderer->SetBackground(0.0, 0.0, 0.0); vtkNew<vtkRenderWindow> renderWindow; renderWindow->SetSize(640, 480); renderWindow->AddRenderer(renderer); vtkNew<vtkRenderWindowInteractor> interactor; interactor->SetRenderWindow(renderWindow); ``` - 创建渲染器(`vtkRenderer`),设置背景色为黑色。 - 创建渲染窗口(`vtkRenderWindow`),设置大小为640x480,并添加渲染器。 - 创建交互器(`vtkRenderWindowInteractor`),用于处理用户交互。 ### 4. 添加光源 ```cpp vtkNew<vtkLight> light; light->SetLightTypeToSceneLight(); light->SetPosition(1.0, 7.0, 1.0); renderer->AddLight(light); ``` - 创建一个光源(`vtkLight`),设置为场景光源,位置在(1,7,1),并添加到渲染器。 ### 5. 读取3D模型和纹理 ```cpp vtkNew<vtkXMLPolyDataReader> reader; reader->SetFileName(argv[1]); vtkNew<vtkPolyDataNormals> norms; norms->SetInputConnection(reader->GetOutputPort()); vtkNew<vtkTexture> texture; texture->InterpolateOn(); vtkNew<vtkJPEGReader> imgReader; imgReader->SetFileName(argv[2]); texture->SetInputConnection(imgReader->GetOutputPort()); ``` - 使用`vtkXMLPolyDataReader`读取.vtp文件(包含3D模型)。 - 使用`vtkPolyDataNormals`计算法线(用于光照和反射效果)。 - 使用`vtkJPEGReader`读取纹理图片,并设置给`vtkTexture`,开启纹理插值。 ### 6. 创建映射器(Mapper)和演员(Actor) ```cpp vtkNew<vtkOpenGLPolyDataMapper> mapper; mapper->SetInputConnection(norms->GetOutputPort()); vtkNew<vtkActor> actor; actor->SetPosition(0, 0, 0); actor->SetScale(6.0, 6.0, 6.0); actor->GetProperty()->SetSpecular(0.8); actor->GetProperty()->SetSpecularPower(20); actor->GetProperty()->SetDiffuse(0.1); actor->GetProperty()->SetAmbient(0.1); actor->GetProperty()->SetDiffuseColor(1.0, 0.0, 0.4); actor->GetProperty()->SetAmbientColor(0.4, 0.0, 1.0); renderer->AddActor(actor); actor->SetTexture(texture); actor->SetMapper(mapper); ``` - 创建`vtkOpenGLPolyDataMapper`(支持OpenGL着色器替换),输入为计算法线后的数据。 - 创建`vtkActor`,设置位置、缩放比例、材质属性(高光、漫反射、环境光等颜色和系数),并添加纹理和映射器。 ### 7. 着色器替换(关键部分) 这部分代码修改了默认的顶点着色器和片段着色器,以实现反射环境贴图的效果。根据VTK版本不同,使用两种方式(`USE_SHADER_PROPERTIES`宏控制)进行着色器替换。 #### 顶点着色器替换: ```cpp // 替换顶点着色器的声明部分,添加输出变量TexCoords #if USE_SHADER_PROPERTIES vtkShaderProperty* sp = actor->GetShaderProperty(); sp->AddVertexShaderReplacement( "//VTK::PositionVC::Dec", // replace true, // before the standard replacements "//VTK::PositionVC::Dec\n" // we still want the default "out vec3 TexCoords;\n", false // only do it once ); #else // ... 类似,使用mapper->AddShaderReplacement #endif ``` - 在顶点着色器的声明部分(`//VTK::PositionVC::Dec`标记处)添加一个输出变量`TexCoords`(用于传递纹理坐标到片段着色器)。 ```cpp // 替换顶点着色器的实现部分,计算反射向量 #if USE_SHADER_PROPERTIES sp->AddVertexShaderReplacement( "//VTK::PositionVC::Impl", // replace true, // before the standard replacements "//VTK::PositionVC::Impl\n" // we still want the default "vec3 camPos = -MCVCMatrix[3].xyz * mat3(MCVCMatrix);\n" "TexCoords.xyz = reflect(vertexMC.xyz - camPos, normalize(normalMC));\n", false // only do it once ); #else // ... 类似 #endif ``` - 在顶点着色器的实现部分(`//VTK::PositionVC::Impl`标记处)添加计算反射向量的代码: - `camPos`:计算相机在世界坐标系中的位置。 - `TexCoords.xyz`:计算从相机位置到顶点的反射向量(使用入射方向和法线)。 #### 片段着色器替换: ```cpp // 在片段着色器的声明部分添加输入变量TexCoords #if USE_SHADER_PROPERTIES sp->AddFragmentShaderReplacement( "//VTK::Light::Dec", // replace true, // before the standard replacements "//VTK::Light::Dec\n" // we still want the default "in vec3 TexCoords;\n", false // only do it once ); #else // ... 类似 #endif ``` - 在片段着色器的声明部分(`//VTK::Light::Dec`标记处)添加输入变量`TexCoords`(从顶点着色器传递而来)。 ```cpp // 替换片段着色器的光照实现部分,使用反射向量采样环境贴图 #if USE_SHADER_PROPERTIES sp->AddFragmentShaderReplacement( "//VTK::Light::Impl", // replace true, // before the standard replacements "//VTK::Light::Impl\n" " float phix = length(vec2(TexCoords.x, TexCoords.z));\n" " vec3 skyColor = texture(actortexture, vec2(0.5*atan(TexCoords.z, " "TexCoords.x)/3.1415927 + 0.5, atan(TexCoords.y,phix)/3.1415927 + " "0.5)).xyz;\n" " gl_FragData[0] = vec4(ambientColor + diffuse + specular + " "specularColor*skyColor, opacity);\n", // we still want the default false // only do it once ); #else // ... 类似 #endif ``` - 在片段着色器的光照实现部分(`//VTK::Light::Impl`标记处)添加代码: - 将反射向量(`TexCoords`)转换为球面坐标(经度和纬度),用于采样环境贴图(`actortexture`)。 - 将采样的环境贴图颜色(`skyColor`)与原有的光照颜色(环境光、漫反射、高光)结合,并乘以高光颜色(`specularColor`)后输出到`gl_FragData[0]`。 ### 8. 添加天空盒 ```cpp vtkNew<vtkSkybox> world; world->SetProjectionToSphere(); world->SetTexture(texture); renderer->AddActor(world); ``` - 创建天空盒(`vtkSkybox`),设置为球面投影,并使用相同的纹理贴图。 ### 9. 设置相机 ```cpp renderer->GetActiveCamera()->SetPosition(0.0, 0.55, 2.0); renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.55, 0.0); renderer->GetActiveCamera()->SetViewAngle(60.0); renderer->ResetCamera(); renderer->GetActiveCamera()->Zoom(1.1); renderer->GetActiveCamera()->Azimuth(0); renderer->GetActiveCamera()->Elevation(5); renderer->ResetCameraClippingRange(); ``` - 设置相机位置、焦点、视角,并进行一些调整(缩放、方位角、仰角)和重置裁剪范围。 ### 10. 渲染和交互 ```cpp renderWindow->Render(); renderWindow->SetWindowName("SphereMap"); renderWindow->Render(); vtkNew<vtkInteractorStyleTrackballCamera> style; renderWindow->GetInteractor()->SetInteractorStyle(style); interactor->Start(); ``` - 渲染窗口,设置窗口标题,并启动交互器(使用`vtkInteractorStyleTrackballCamera`交互风格)。 ### 总结 这个程序展示了如何使用VTK创建一个具有环境贴图和反射效果的3D场景。关键点在于通过着色器替换修改了默认的着色器,实现了反射环境贴图的计算。同时,使用天空盒作为背景,增强了场景的真实感。 ### 注意 - 着色器替换部分依赖于VTK版本,高版本(>=8.9)使用`vtkShaderProperty`,低版本使用`vtkOpenGLPolyDataMapper`的`AddShaderReplacement`方法。 - 反射效果是通过在顶点着色器中计算反射向量,然后在片段着色器中用这个向量采样环境贴图实现的。 §§相关问题§§: 1. VTK中如何读取和显示3D模型文件? 2. 在VTK中,如何设置光源和材质属性? 3. VTK的着色器替换机制是什么?如何使用? 4. 如何创建一个天空盒(Skybox)并设置其纹理?</think>### 详细代码解析 这段代码是使用VTK(Visualization Toolkit)库实现的3D环境映射渲染程序,主要功能是加载3D模型并应用球形环境贴图反射效果。以下是关键部分的详细解析: #### 1. 初始化与参数检查 ```cpp if (argc < 3) { std::cout << "Usage: " << argv[0] << " file.vtp wintersun.jpg" << std::endl; return EXIT_SUCCESS; } ``` - **作用**:检查命令行参数,确保提供了3D模型文件(.vtp)和纹理文件(.jpg) - **原因**:程序需要这两个文件才能正确渲染场景 #### 2. 渲染环境设置 ```cpp vtkNew<vtkRenderer> renderer; vtkNew<vtkRenderWindow> renderWindow; vtkNew<vtkRenderWindowInteractor> interactor; ``` - **作用**:创建渲染管线核心组件 - **组件说明**: - `vtkRenderer`:管理场景渲染 - `vtkRenderWindow`:显示渲染结果的窗口 - `vtkRenderWindowInteractor`:处理用户交互 #### 3. 光照设置 ```cpp vtkNew<vtkLight> light; light->SetLightTypeToSceneLight(); light->SetPosition(1.0, 7.0, 1.0); renderer->AddLight(light); ``` - **作用**:创建场景光源并设置位置 - **关键参数**: - `SetPosition(1.0, 7.0, 1.0)`:光源位于(1,7,1)坐标 - `SetLightTypeToSceneLight()`:设置为场景全局光源 #### 4. 模型加载与处理 ```cpp vtkNew<vtkXMLPolyDataReader> reader; reader->SetFileName(argv[1]); vtkNew<vtkPolyDataNormals> norms; norms->SetInputConnection(reader->GetOutputPort()); ``` - **作用**: - 读取VTK多边形数据文件(.vtp) - 计算模型法线(用于光照计算) - **原因**:法线数据是光照反射效果的必要前提 #### 5. 纹理处理 ```cpp vtkNew<vtkTexture> texture; texture->InterpolateOn(); vtkNew<vtkJPEGReader> imgReader; imgReader->SetFileName(argv[2]); texture->SetInputConnection(imgReader->GetOutputPort()); ``` - **作用**: - 加载JPEG纹理图像 - 启用纹理插值(平滑纹理) - **注意**:`texture->MipmapOn()`被注释掉,因立方体贴图的Mipmapping在OpenGL 3.2中不是核心功能 #### 6. 模型渲染设置 ```cpp vtkNew<vtkOpenGLPolyDataMapper> mapper; mapper->SetInputConnection(norms->GetOutputPort()); vtkNew<vtkActor> actor; actor->SetMapper(mapper); actor->SetTexture(texture); actor->SetScale(6.0, 6.0, 6.0); ``` - **关键属性**: - `SetScale(6.0, 6.0, 6.0)`:将模型放大6倍 - 材质属性设置(高光、漫反射等): ```cpp actor->GetProperty()->SetSpecular(0.8); actor->GetProperty()->SetSpecularPower(20); actor->GetProperty()->SetDiffuse(0.1); ``` #### 7. 环境映射核心 - 着色器替换(关键部分) ```cpp // 顶点着色器替换 - 传递纹理坐标 sp->AddVertexShaderReplacement( "//VTK::PositionVC::Dec", true, "//VTK::PositionVC::Dec\n" "out vec3 TexCoords;\n", false ); // 顶点着色器计算反射向量 sp->AddVertexShaderReplacement( "//VTK::PositionVC::Impl", true, "//VTK::PositionVC::Impl\n" "vec3 camPos = -MCVCMatrix[3].xyz * mat3(MCVCMatrix);\n" "TexCoords.xyz = reflect(vertexMC.xyz - camPos, normalize(normalMC));\n", false ); // 片段着色器采样环境贴图 sp->AddFragmentShaderReplacement( "//VTK::Light::Impl", true, "//VTK::Light::Impl\n" " float phix = length(vec2(TexCoords.x, TexCoords.z));\n" " vec3 skyColor = texture(actortexture, vec2(0.5*atan(TexCoords.z, TexCoords.x)/3.1415927 + 0.5, " "atan(TexCoords.y,phix)/3.1415927 + 0.5)).xyz;\n" " gl_FragData[0] = vec4(ambientColor + diffuse + specular + specularColor*skyColor, opacity);\n", false ); ``` - **实现原理**: 1. **顶点着色器**:计算相机位置和反射向量 2. **片段着色器**:将反射向量转换为球面坐标采样纹理 3. **最终颜色**:结合光照计算结果和环境贴图颜色 - **数学计算**: - `reflect()`:计算入射光的反射方向 - `atan()`:将3D反射向量转换为2D球面坐标 #### 8. 天空盒设置 ```cpp vtkNew<vtkSkybox> world; world->SetProjectionToSphere(); world->SetTexture(texture); renderer->AddActor(world); ``` - **作用**:创建球形背景,使用相同的环境贴图 - **投影方式**:`SetProjectionToSphere()`启用球形投影 #### 9. 相机设置 ```cpp renderer->GetActiveCamera()->SetPosition(0.0, 0.55, 2.0); renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.55, 0.0); renderer->ResetCamera(); renderer->GetActiveCamera()->Zoom(1.1); ``` - **参数说明**: - `SetPosition()`:相机位置 - `SetFocalPoint()`:相机焦点 - `Zoom(1.1)`:放大场景10% #### 10. 交互设置 ```cpp vtkNew<vtkInteractorStyleTrackballCamera> style; renderWindow->GetInteractor()->SetInteractorStyle(style); interactor->Start(); ``` - **交互类型**:`vtkInteractorStyleTrackballCamera`提供鼠标控制场景旋转和缩放 ### 总结 此程序实现了高级的**动态环境映射**效果,核心特点包括: 1. 使用着色器替换技术修改VTK默认渲染管线 2. 在顶点着色器中计算反射向量 3. 在片段着色器中将3D反射向量转换为2D球面坐标采样纹理 4. 结合光照模型和环境贴图产生金属反射效果 5. 使用相同的纹理创建天空盒背景增强沉浸感
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值