本节简单介绍下如何使用Ogre 2.3加载模型,并给模型贴上纹理材质。
一. 安装ogre 2.3
主要有两种安装方法:
- 简单安装方法,使用scripts for Ogre 2.3 脚本,按照官网给出的步骤安装即可。需要注意的是脚本解压后的 *.bat 文件需要修改下 CMAKE 路径,否则很容易提示找到不对 CMAKE。下载地址为: https://github.com/OGRECave/ogre-next/releases/download/bin-releases/build_ogre_scripts-v2-3.7z
- 复杂安装方法,下载源码,编译安装。这部分官网都有介绍。
二. 创建新项目
由于官方源码Sample项目中的例子过于繁杂,我们不需要那么多功能,这里仅把所需要的功能代码拿过来,省略了一些负责的部分。
2.1 资源初始化
我们参考官网给的源码例子,在源码文件中GraphicsSystem.cpp
中有个方法
void GraphicsSystem::initialize( const Ogre::String &windowTitle )
该方法用于资源初始化,但是该方法中首先是SDL2写的弹出框,我们这里并不需要,这部分不要。主要使用的是该方法中的326行,这几个方法:
setupResources();
loadResources();
chooseSceneManager();
createCamera();
mWorkspace = setupCompositor();
2.2 设置资源
加载resource2.cfg中的配置资源,这里设置为两部分,一个是.cfg中配置的材质,另一个是项目中额外用的材质,初始化的时候都加载进项目中。
void setupCfgResources(void)
{
// Load resource paths from config file
Ogre::ConfigFile cf;
cf.load( AndroidSystems::openFile( mResourcePath + "resources2.cfg" ) );
// Go through all sections & settings in the file
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
Ogre::String secName, typeName, archName;
while( seci.hasMoreElements() )
{
secName = seci.peekNextKey();
Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
if( secName != "Hlms" )
{
Ogre::ConfigFile::SettingsMultiMap::iterator i;
for (i = settings->begin(); i != settings->end(); ++i)
{
typeName = i->first;
archName = i->second;
//这里可能需要修改下
//Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
//archName, typeName, secName);
addResourceLocation( archName, typeName, secName );
}
}
}
}
如果需要用到额外的材质,需要另外加载
void setupExtraResources(void)
{
Ogre::ConfigFile cf;
cf.load("resources2.cfg");
Ogre::String dataFolder = cf.getSetting("DoNotUseAsResource", "Hlms", "");
if (dataFolder.empty())
dataFolder = "./";
else if (*(dataFolder.end() - 1) != '/')
dataFolder += "/";
const char* c_locations[2] =
{ //本项目另外用到的材质
"2.0/scripts/materials/PbsMaterials",
"2.0/scripts/materials/HLMSEditor"
};
for(size_t i = 0; i < 2; ++i)
{
Ogre::String dataFolderFull = dataFolder + c_locations[i];
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(dataFolderFull, getMediaReadArchiveType(), "General");
}
}
2.3 加载资源
void loadResources(void)
{
registerHlms();
// Initialise, parse scripts etc
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups( true );
}
加载HLMS材质
void registerHlms(void)
{
Ogre::ConfigFile cf;
Ogre::String mResourcePath = "";
cf.load((mResourcePath + "resources2.cfg"));
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
Ogre::String rootHlmsFolder = Ogre::macBundlePath() + '/' +
cf.getSetting("DoNotUseAsResource", "Hlms", "");
#else
Ogre::String rootHlmsFolder = mResourcePath + cf.getSetting("DoNotUseAsResource", "Hlms", "");
#endif
if (rootHlmsFolder.empty())
rootHlmsFolder = "./";
else if (*(rootHlmsFolder.end() - 1) != '/')
rootHlmsFolder += "/";
//At this point rootHlmsFolder should be a valid path to the Hlms data folder
Ogre::HlmsUnlit* hlmsUnlit = 0;
Ogre::HlmsPbs* hlmsPbs = 0;
//For retrieval of the paths to the different folders needed
Ogre::String mainFolderPath;
Ogre::StringVector libraryFoldersPaths;
Ogre::StringVector::const_iterator libraryFolderPathIt;
Ogre::StringVector::const_iterator libraryFolderPathEn;
Ogre::ArchiveManager& archiveManager = Ogre::ArchiveManager::getSingleton();
const Ogre::String& archiveType = getMediaReadArchiveType();
{
//Create & Register HlmsUnlit
//Get the path to all the subdirectories used by HlmsUnlit
Ogre::HlmsUnlit::getDefaultPaths(mainFolderPath, libraryFoldersPaths);
Ogre::Archive* archiveUnlit = archiveManager.load(rootHlmsFolder + mainFolderPath,
archiveType, true);
Ogre::ArchiveVec archiveUnlitLibraryFolders;
libraryFolderPathIt = libraryFoldersPaths.begin();
libraryFolderPathEn = libraryFoldersPaths.end();
while (libraryFolderPathIt != libraryFolderPathEn)
{
Ogre::Archive* archiveLibrary =
archiveManager.load(rootHlmsFolder + *libraryFolderPathIt, archiveType, true);
archiveUnlitLibraryFolders.push_back(archiveLibrary);
++libraryFolderPathIt;
}
//Create and register the unlit Hlms
hlmsUnlit = OGRE_NEW Ogre::HlmsUnlit(archiveUnlit, &archiveUnlitLibraryFolders);
Ogre::Root::getSingleton().getHlmsManager()->registerHlms(hlmsUnlit);
}
{
//Create & Register HlmsPbs
//Do the same for HlmsPbs:
Ogre::HlmsPbs::getDefaultPaths(mainFolderPath, libraryFoldersPaths);
Ogre::Archive* archivePbs = archiveManager.load(rootHlmsFolder + mainFolderPath,
archiveType, true);
//Get the library archive(s)
Ogre::ArchiveVec archivePbsLibraryFolders;
libraryFolderPathIt = libraryFoldersPaths.begin();
libraryFolderPathEn = libraryFoldersPaths.end();
while (libraryFolderPathIt != libraryFolderPathEn)
{
Ogre::Archive* archiveLibrary =
archiveManager.load(rootHlmsFolder + *libraryFolderPathIt, archiveType, true);
archivePbsLibraryFolders.push_back(archiveLibrary);
++libraryFolderPathIt;
}
//Create and register
hlmsPbs = OGRE_NEW Ogre::HlmsPbs(archivePbs, &archivePbsLibraryFolders);
Ogre::Root::getSingleton().getHlmsManager()->registerHlms(hlmsPbs);
}
Ogre::RenderSystem* renderSystem = Ogre::Root::getSingletonPtr()->getRenderSystem();
if (renderSystem->getName() == "Direct3D11 Rendering Subsystem")
{
//Set lower limits 512kb instead of the default 4MB per Hlms in D3D 11.0
//and below to avoid saturating AMD's discard limit (8MB) or
//saturate the PCIE bus in some low end machines.
bool supportsNoOverwriteOnTextureBuffers;
renderSystem->getCustomAttribute("MapNoOverwriteOnDynamicBufferSRV",
&supportsNoOverwriteOnTextureBuffers);
if (!supportsNoOverwriteOnTextureBuffers)
{
hlmsPbs->setTextureBufferDefaultSize(512 * 1024);
hlmsUnlit->setTextureBufferDefaultSize(512 * 1024);
}
}
}
const char* getMediaReadArchiveType(void)
{
#if OGRE_PLATFORM != OGRE_PLATFORM_ANDROID
return "FileSystem";
#else
return "APKFileSystem";
#endif
}
2.4 创建场景
const size_t numThreads = 1u;
sceneManager = root->createSceneManager(ST_GENERIC, numThreads, "ExampleSMInstance");
2.5 创建摄像机
void createCamera(void)
{
// Create & setup camera
Ogre::Camera* camera = sceneManager->createCamera("Main Camera");
// Position it at 500 in Z direction
camera->setPosition(Ogre::Vector3(0, 5, 15));
// Look back along -Z
camera->lookAt(Ogre::Vector3(0, 0, 0));
camera->setNearClipDistance(0.2f);
camera->setFarClipDistance(1000.0f);
camera->setAutoAspectRatio(true);
}
2.6 创建基础组合器
// Setup a basic compositor with a blue clear colour
CompositorManager2* compositorManager = root->getCompositorManager2();
const String workspaceName("Demo Workspace");
const ColourValue backgroundColour(0.2f, 0.4f, 0.6f);
compositorManager->createBasicWorkspaceDef(workspaceName, backgroundColour, IdString());
compositorManager->addWorkspace(sceneManager, window->getTexture(), camera, workspaceName, true);
2.7 创建模型,并加载纹理贴图
void createScene01(void)
{
//-------------------------------------------------------------------------
// V2 mesh loading
Ogre::Item* item = sceneManager->createItem("Sphere1000.mesh",
Ogre::ResourceGroupManager::
AUTODETECT_RESOURCE_GROUP_NAME,
Ogre::SCENE_DYNAMIC);
item->setDatablock("Marble"); //这里为纹理材质,一定要加载PbsMaterials.material材质!!!
item->setVisibilityFlags(0x000000002);
Ogre::SceneNode* mSceneNode = sceneManager->getRootSceneNode(Ogre::SCENE_DYNAMIC)->
createChildSceneNode(Ogre::SCENE_DYNAMIC);
mSceneNode->scale(3.0, 3.0, 3.0);
mSceneNode->attachObject(item);
//光照 很重要!!!不写的话,纹理贴图无法显示出来
Ogre::Light* light = sceneManager->createLight();
Ogre::SceneNode* lightNode = ((Ogre::SceneNode*)sceneManager->getRootSceneNode()->createChild());
lightNode->attachObject(light);
light->setPowerScale(2.0f);
light->setType(Ogre::Light::LT_DIRECTIONAL);
light->setDirection(Ogre::Vector3(-1, -1, -1).normalisedCopy());
sceneManager->setAmbientLight((Ogre::ColourValue(0.3f, 0.5f, 0.7f) * 0.1f * 0.75f),
(Ogre::ColourValue(0.6f, 0.45f, 0.3f) * 0.065f * 0.75f),
((-light->getDirection()) + (Ogre::Vector3::UNIT_Y * 0.2f)));
}
2.8 完整代码如下:
main.cpp
#include "Hlms.h"
#include "OgreCamera.h"
#include "OgreRoot.h"
#include "OgreWindow.h"
#include "Compositor/OgreCompositorManager2.h"
#include "OgreWindowEventUtilities.h"
#include "OgreItem.h"
#include "OgreSceneManager.h"
//
#include "OgreHlmsUnlitDatablock.h"
#include "OgreHlmsPbsDatablock.h"
#include "OgreHlmsSamplerblock.h"
#include "OgreMesh.h"
#include "OgreMesh2.h"
#include "OgreMeshManager.h"
#include "OgreMeshManager2.h"
#include "OgreTextureFilters.h"
#include "OgreTextureGpuManager.h"
Ogre::Root* root = NULL;
Ogre::SceneManager* sceneManager = NULL;
Ogre::Camera* camera = NULL;
//加载资源
void setupResources(void);
void setupCfgResources(void);
void setupExtraResources(void);
void loadResources(void);
void createCamera(void);
void createScene01(void);
class MyWindowEventListener : public Ogre::WindowEventListener
{
bool mQuit;
public:
MyWindowEventListener() : mQuit(false) {}
virtual void windowClosed(Ogre::Window* rw) { mQuit = true; }
bool getQuit(void) const { return mQuit; }
};
int main(int argc, const char* argv[])
{
using namespace Ogre;
const String pluginsFolder = "./";
const String writeAccessFolder = "./";
#ifndef OGRE_STATIC_LIB
#if OGRE_DEBUG_MODE
const char* pluginsFile = "plugins_d.cfg";
#else
const char* pluginsFile = "plugins.cfg";
#endif
#endif
root = OGRE_NEW Root(pluginsFolder + pluginsFile,
writeAccessFolder + "ogre.cfg",
writeAccessFolder + "Ogre.log");
Ogre::RenderSystem* renderSystem = root->getRenderSystemByName("Direct3D11 Rendering Subsystem");
if (!(renderSystem))
{
printf("Render system not found!\n");
return -1;
}
// Initialize Root
root->setRenderSystem(renderSystem);
root->getRenderSystem()->setConfigOption("sRGB Gamma Conversion", "Yes");
root->getRenderSystem()->setConfigOption("Full Screen", "No");
Window* window = root->initialise(true, "Tutorial 00: Basic");
MyWindowEventListener myWindowEventListener;
WindowEventUtilities::addWindowEventListener(window, &myWindowEventListener);
//加载资源
setupResources();
//加载HLMS
loadResources();
// Create SceneManager
const size_t numThreads = 1u;
sceneManager = root->createSceneManager(ST_GENERIC, numThreads, "ExampleSMInstance");
//摄像机
createCamera();
// Setup a basic compositor with a blue clear colour
CompositorManager2* compositorManager = root->getCompositorManager2();
const String workspaceName("Demo Workspace");
const ColourValue backgroundColour(0.2f, 0.4f, 0.6f);
compositorManager->createBasicWorkspaceDef(workspaceName, backgroundColour, IdString());
compositorManager->addWorkspace(sceneManager, window->getTexture(), camera, workspaceName, true);
createScene01();
bool bQuit = false;
while (!bQuit)
{
WindowEventUtilities::messagePump();
bQuit = myWindowEventListener.getQuit() || !root->renderOneFrame();
}
WindowEventUtilities::removeWindowEventListener(window, &myWindowEventListener);
OGRE_DELETE root;
root = 0;
return 0;
}
void setupCfgResources(void)
{
// Load resource paths from config file
Ogre::ConfigFile cf;
cf.load("resources2.cfg");
// Go through all sections & settings in the file
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
Ogre::String secName, typeName, archName;
while (seci.hasMoreElements())
{
secName = seci.peekNextKey();
Ogre::ConfigFile::SettingsMultiMap* settings = seci.getNext();
if (secName != "Hlms")
{
Ogre::ConfigFile::SettingsMultiMap::iterator i;
for (i = settings->begin(); i != settings->end(); ++i)
{
typeName = i->first;
archName = i->second;
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
archName, typeName, secName);
}
}
}
}
void setupExtraResources(void)
{
Ogre::ConfigFile cf;
cf.load("resources2.cfg");
Ogre::String dataFolder = cf.getSetting("DoNotUseAsResource", "Hlms", "");
if (dataFolder.empty())
dataFolder = "./";
else if (*(dataFolder.end() - 1) != '/')
dataFolder += "/";
const char* c_locations[5] =
{
"2.0/scripts/materials/PbsMaterials",
"2.0/scripts/materials/HLMSEditor"
};
for(size_t i = 0; i < 2; ++i)
{
Ogre::String dataFolderFull = dataFolder + c_locations[i];
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(dataFolderFull, getMediaReadArchiveType(), "General");
}
}
void setupResources(void)
{
//1.加载resource2.cfg
setupCfgResources();
//2.加载额外需要的资源
setupExtraResources();
}
void loadResources(void)
{
registerHlms();
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(true);
}
void createCamera(void)
{
// Create & setup camera
camera = sceneManager->createCamera("Main Camera");
// Position it at 500 in Z direction
camera->setPosition(Ogre::Vector3(0, 5, 15));
// Look back along -Z
camera->lookAt(Ogre::Vector3(0, 0, 0));
camera->setNearClipDistance(0.2f);
camera->setFarClipDistance(1000.0f);
camera->setAutoAspectRatio(true);
}
void createScene01(void)
{
//-------------------------------------------------------------------------
/*Ogre::HlmsManager* hlmsManager = root->getHlmsManager();
assert(dynamic_cast<Ogre::HlmsPbs*>(hlmsManager->getHlms(Ogre::HLMS_PBS)));
Ogre::HlmsPbs* hlmsPbs = static_cast<Ogre::HlmsPbs*>(hlmsManager->getHlms(Ogre::HLMS_PBS));
Ogre::TextureGpuManager* textureMgr = root->getRenderSystem()->getTextureGpuManager();
Ogre::String datablockName = "Test" + Ogre::StringConverter::toString(0);
Ogre::HlmsPbsDatablock* datablock = static_cast<Ogre::HlmsPbsDatablock*>(
hlmsPbs->createDatablock(datablockName,
datablockName,
Ogre::HlmsMacroblock(),
Ogre::HlmsBlendblock(),
Ogre::HlmsParamVec()));
Ogre::TextureGpu* texture = textureMgr->createOrRetrieveTexture(
"SaintPetersBasilica.dds",
Ogre::GpuPageOutStrategy::Discard,
Ogre::TextureFlags::PrefersLoadingFromFileAsSRGB,
Ogre::TextureTypes::TypeCube,
Ogre::ResourceGroupManager::
AUTODETECT_RESOURCE_GROUP_NAME,
Ogre::TextureFilter::TypeGenerateDefaultMipmaps);
datablock->setTexture(Ogre::PBSM_REFLECTION, texture);
datablock->setDiffuse(Ogre::Vector3(1.0f, 1.0f, 1.0f));
datablock->setRoughness(std::max(0.02f, 3 / std::max(1.0f, (float)(8 - 1))));
datablock->setFresnel(Ogre::Vector3(3 / std::max(1.0f, (float)(8 - 1))), false);*/
// V2 mesh loading
Ogre::Item* item = sceneManager->createItem("Sphere1000.mesh",
Ogre::ResourceGroupManager::
AUTODETECT_RESOURCE_GROUP_NAME,
Ogre::SCENE_DYNAMIC);
item->setDatablock("Marble");
//item->setDatablock(datablock);
item->setVisibilityFlags(0x000000002);
Ogre::SceneNode* mSceneNode = sceneManager->getRootSceneNode(Ogre::SCENE_DYNAMIC)->
createChildSceneNode(Ogre::SCENE_DYNAMIC);
mSceneNode->scale(3.0, 3.0, 3.0);
mSceneNode->attachObject(item);
//光照
Ogre::Light* light = sceneManager->createLight();
Ogre::SceneNode* lightNode = ((Ogre::SceneNode*)sceneManager->getRootSceneNode()->createChild());
lightNode->attachObject(light);
light->setPowerScale(2.0f);
light->setType(Ogre::Light::LT_DIRECTIONAL);
light->setDirection(Ogre::Vector3(-1, -1, -1).normalisedCopy());
sceneManager->setAmbientLight((Ogre::ColourValue(0.3f, 0.5f, 0.7f) * 0.1f * 0.75f),
(Ogre::ColourValue(0.6f, 0.45f, 0.3f) * 0.065f * 0.75f),
((-light->getDirection()) + (Ogre::Vector3::UNIT_Y * 0.2f)));
}
Hlms.h
#pragma once
#include "OgreConfigFile.h"
#include <OgreArchiveManager.h>
#include "OgreHlmsManager.h"
#include <OgreHlmsPbs.h>
#include "OgreHlmsUnlit.h"
const char* getMediaReadArchiveType(void);
void registerHlms(void)
{
Ogre::ConfigFile cf;
Ogre::String mResourcePath = "";
cf.load((mResourcePath + "resources2.cfg"));
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
Ogre::String rootHlmsFolder = Ogre::macBundlePath() + '/' +
cf.getSetting("DoNotUseAsResource", "Hlms", "");
#else
Ogre::String rootHlmsFolder = mResourcePath + cf.getSetting("DoNotUseAsResource", "Hlms", "");
#endif
if (rootHlmsFolder.empty())
rootHlmsFolder = "./";
else if (*(rootHlmsFolder.end() - 1) != '/')
rootHlmsFolder += "/";
//At this point rootHlmsFolder should be a valid path to the Hlms data folder
Ogre::HlmsUnlit* hlmsUnlit = 0;
Ogre::HlmsPbs* hlmsPbs = 0;
//For retrieval of the paths to the different folders needed
Ogre::String mainFolderPath;
Ogre::StringVector libraryFoldersPaths;
Ogre::StringVector::const_iterator libraryFolderPathIt;
Ogre::StringVector::const_iterator libraryFolderPathEn;
Ogre::ArchiveManager& archiveManager = Ogre::ArchiveManager::getSingleton();
const Ogre::String& archiveType = getMediaReadArchiveType();
{
//Create & Register HlmsUnlit
//Get the path to all the subdirectories used by HlmsUnlit
Ogre::HlmsUnlit::getDefaultPaths(mainFolderPath, libraryFoldersPaths);
Ogre::Archive* archiveUnlit = archiveManager.load(rootHlmsFolder + mainFolderPath,
archiveType, true);
Ogre::ArchiveVec archiveUnlitLibraryFolders;
libraryFolderPathIt = libraryFoldersPaths.begin();
libraryFolderPathEn = libraryFoldersPaths.end();
while (libraryFolderPathIt != libraryFolderPathEn)
{
Ogre::Archive* archiveLibrary =
archiveManager.load(rootHlmsFolder + *libraryFolderPathIt, archiveType, true);
archiveUnlitLibraryFolders.push_back(archiveLibrary);
++libraryFolderPathIt;
}
//Create and register the unlit Hlms
hlmsUnlit = OGRE_NEW Ogre::HlmsUnlit(archiveUnlit, &archiveUnlitLibraryFolders);
Ogre::Root::getSingleton().getHlmsManager()->registerHlms(hlmsUnlit);
}
{
//Create & Register HlmsPbs
//Do the same for HlmsPbs:
Ogre::HlmsPbs::getDefaultPaths(mainFolderPath, libraryFoldersPaths);
Ogre::Archive* archivePbs = archiveManager.load(rootHlmsFolder + mainFolderPath,
archiveType, true);
//Get the library archive(s)
Ogre::ArchiveVec archivePbsLibraryFolders;
libraryFolderPathIt = libraryFoldersPaths.begin();
libraryFolderPathEn = libraryFoldersPaths.end();
while (libraryFolderPathIt != libraryFolderPathEn)
{
Ogre::Archive* archiveLibrary =
archiveManager.load(rootHlmsFolder + *libraryFolderPathIt, archiveType, true);
archivePbsLibraryFolders.push_back(archiveLibrary);
++libraryFolderPathIt;
}
//Create and register
hlmsPbs = OGRE_NEW Ogre::HlmsPbs(archivePbs, &archivePbsLibraryFolders);
Ogre::Root::getSingleton().getHlmsManager()->registerHlms(hlmsPbs);
}
Ogre::RenderSystem* renderSystem = Ogre::Root::getSingletonPtr()->getRenderSystem();
if (renderSystem->getName() == "Direct3D11 Rendering Subsystem")
{
//Set lower limits 512kb instead of the default 4MB per Hlms in D3D 11.0
//and below to avoid saturating AMD's discard limit (8MB) or
//saturate the PCIE bus in some low end machines.
bool supportsNoOverwriteOnTextureBuffers;
renderSystem->getCustomAttribute("MapNoOverwriteOnDynamicBufferSRV",
&supportsNoOverwriteOnTextureBuffers);
if (!supportsNoOverwriteOnTextureBuffers)
{
hlmsPbs->setTextureBufferDefaultSize(512 * 1024);
hlmsUnlit->setTextureBufferDefaultSize(512 * 1024);
}
}
}
const char* getMediaReadArchiveType(void)
{
#if OGRE_PLATFORM != OGRE_PLATFORM_ANDROID
return "FileSystem";
#else
return "APKFileSystem";
#endif
}
三. VS工程项目配置,重要!!!涉及到OGRE资源的加载
记住: .exe,.dll,.cfg,.pdb资源都在一个目录下。
-
设置工作目录
我的本地电脑
工作目录
$(OutDir)
D:\Project\vsproject\3DGIS\OgreNexTest\OgreDemoApp-master\build2019\Debug\
命令
$(TargetPath)
D:\Project\vsproject\3DGIS\OgreNexTest\OgreDemoApp-master\build2019\Debug\DemoApp.exe
-
将Ogre2.3的bin/Debug目录下的资源拷贝到
$(OutDir)
目录下。
-
材质资源复制,这里路径可以自己选择。只要resource2.cfg中配置正确即可。
-
C/C++ -> 附加包含目录
-
链接器 -> 输入 -> 附加依赖项
6. 到这里项目配置结束了,就可以运行代码看看效果啦