Cocos2d-x之3D

Cocos2d-x是一個二維(2D)遊戲引擎。但在Cocos2d-x版本3開始,3D特性被加了進來並進行了改進。3D遊戲市場非常巨大, Cocos2d-x正在添加3D開發所需的所有功能 。

3D技術

  • Mesh(網格): 頂點,用於構造要渲染的形狀和紋理。
  • Model( 模型 ): 可以渲染的對象 ,它是風格的集合。在Cocos2d-x中就是Sprite3D對象。
  • Texture( 紋理 ):三維模型的所有曲面和頂點都可以映射到紋理。大多數情況, 每個模型有多個紋理 , 在紋理圖集中展開。
  • Camera(照相機): 因爲三維世界不是平面的,你需要設置一個相機來觀察它。 使用不同的相機參數可以得到不同的場景。
  • Light(燈光): 應用燈光使場景看起來逼真。 爲了使一個物體看起來真實,顏色應該根據光線變化。 當你面對光明時,它是光明的,相反的是黑暗的。 照亮物體意味着根據光線計算物體的顏色。

Sprite3D對象

就像2D遊戲,3D遊戲也有Sprite對象。Sprite對象是任何遊戲的核心基礎。Sprite3D和Sprite唯一不同的是Sprite3D對象有3個軸,可以定位在x、y和z軸上。 Sprite3D在很多方面都像普通的Sprite一樣工作。 加載並展示Sprite3D對象:

auto sprite = Sprite3D::create("boss.c3b"); //c3b file, created with the FBX-converter
sprite->setScale(5.f); //sets the object scale in float
sprite->setPosition(Vec2(200,200)); //sets sprite position
scene->addChild(sprite,1); //adds sprite to scene, z-index: 1

.c3b3D文件可以用FBX-converter工具來創建

現在,讓我們來循環旋轉模型。爲此,我們將創建一個操作並運行它:

//rotate around the X axis
auto rotation = RotateBy::create(15, Vec3(0, 360, 0));
//our sprite object runs the action
sprite->runAction(RepeatForever::create(rotation));

要在Sprite或Sprite3D上設置定位點,請使用:

sprite->setAnchorPoint(Point(0.0f,0.0f));

將三維模型附加到Sprite3D對象

請記住,三維模型是網格的集合。可以將三維模型附加到其他三維模型以創建豐富的效果。如添加武器到主角上,爲了做到這一點,你需要找到武器將被添加的附着點。可以調用getAttachNode(attachment_point_name)函數來找到。 然後我們用addChild()將新模型作爲子模型添加到附件點 。

auto sp = Sprite3D::create("axe.c3b");
sprite->getAttachNode("Bip001 R Hand")->addChild(sp);

交換三維模型

當做3D開發時,你可能要對你的模型做些動態改變 。這種改變可能是換裝、 視覺線索 、通知用戶關於模型的狀態變化。 如果三維模型由網格組成,則可以使用getMeshByIndex()getMeshByName()訪問網格數據。 . 使用這些功能,可以實現像用武器或衣服交換角色一樣的效果。

auto sprite = Sprite3D::create("ReskinGirl.c3b");

// display the first coat
auto girlTop0 = sprite->getMeshByName("Girl_UpperBody01");
girlTop0->setVisible(true);

auto girlTop1 = sprite->getMeshByName("Girl_UpperBody02");
girlTop1->setVisible(false);

// swap to the second coat
girlTop0->setVisible(false);
girlTop1->setVisible(true);

Animation動畫

創建Animate3D對象,使用動畫。

// the animation is contained in the .c3b file
auto animation = Animation3D::create("orc.c3b");

// creates the Action with Animation object
auto animate = Animate3D::create(animation);

// runs the animation
sprite->runAction(RepeatForever::create(animate));

多動畫
同時運行多個動畫。

auto animation = Animation3D::create(fileName);

auto runAnimate = Animate3D::create(animation, 0, 2);
sprite->runAction(runAnimate);

auto attackAnimate = Animate3D::create(animation, 3, 5);
sprite->runAction(attackAnimate);

上面這個例子有兩個動畫。第一個立刻執行,持續2秒,第二個在3秒後執行,持續5秒。

Animation speed動畫速度
向前的動畫速度是一個正整數,負速度則是相反的方向。

Animation blending動畫混合
當使用多個動畫時, 在每個動畫之間自動應用混合。混合的目的是在效果之間創建平滑過渡。 給定兩個動畫A和B,動畫A的最後幾幀和動畫B的前幾幀重疊以使動畫中的更改看起來自然。 默認的過渡時間是0.1秒。可以使用Animate3D::setTransitionTime修改過渡時間。
Cocos2d-x只支持關鍵幀之間的線性插值。 這將填充曲線中的間隙以確保路徑平滑。如果在模型製作中使用其他插值方法,我們的內置工具fbx-conv將生成額外的關鍵幀以進行補償。

Camera照相機

3D世界不是平的,我們需要使用照相機觀察它。因此Camera對象在3D開發中是非常重要的。Camera對象繼承Node,因此支持大多數相同的動作對象。有兩類Camera對象: perspective camera (透視照相機)和orthographic camera(正交相機)。

  • 透視照相機用於觀看具有近到遠效果的對象 。透視照相機的效果:在近處的物體會比較大,在遠處的物體會比較小。
  • 正交攝影機可以將其理解爲將三維世界轉換爲二維表示。使用正交攝影機可以看到,無論對象距攝影機對象有多遠,它們的大小都是相同的。遊戲中的小地圖通常使用正交攝影機渲染。另一個例子是自上而下的視圖,可能是在地牢風格的遊戲中。

每一個Scene自動地創建一個默認的camera, 基於Director對象的投影特性 。如果你要更多的camera,可以用如下代碼創建:

auto s = Director::getInstance()->getWinSize();
auto camera = Camera::createPerspective(60, (GLfloat)s.width/s.height, 1, 1000);

// set parameters for camera
camera->setPosition3D(Vec3(0, 100, 100));
camera->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0));

addChild(camera); //add camera to the scene

創建正交照相機
默認的Camera是透視照相機,如果要創建正交照相機,可以用如下代碼來創建:

Camera::createOrthographic();

如:

auto s = Director::getInstance()->getWinSize();
auto camera = Camera::createOrthographic(s.width, s.height, 1, 1000);

從相機中隱藏對象
有時,我們並不想在照相機裏顯示所有對象。從一個照相機裏隱藏一個對象其實也很簡單,通過在Node(節點)上調用setCameraMask(CameraFlag)和在Camera上調用setCameraFlag(CameraFlag)即可:

//Camera
camera->setCameraFlag(CameraFlag::USER1);

//Node
node->setCameraMask(CameraFlag::USER1);

Cubemap Texture立方紋理

立方體貼圖紋理是六個單獨的正方形紋理的集合,這些紋理放置在虛擬立方體的面上。它們通常用於在對象上顯示無限遠的反射,類似於“天空盒”在背景中顯示遙遠景物的方式。展開的立方體映射的外觀 :
在這裏插入圖片描述
Cocos2d-x創建立方紋理的方法:

// create a textureCube object with six texture assets
auto textureCube = TextureCube::create("skybox/left.jpg",  "skybox/right.jpg", "skybox/top.jpg", "skybox/bottom.jpg", "skybox/front.jpg", "skybox/back.jpg");

// set cube map texture parameters
Texture2D::TexParams tRepeatParams;
tRepeatParams.magFilter = GL_NEAREST;
tRepeatParams.minFilter = GL_NEAREST;
tRepeatParams.wrapS = GL_MIRRORED_REPEAT;
tRepeatParams.wrapT = GL_MIRRORED_REPEAT;
textureCube->setTexParameters(tRepeatParams);

// create and set our custom shader
auto shader = GLProgram::createWithFilenames("cube_map.vert", "cube_map.frag");
auto _state = GLProgramState::create(shader);

// bind cube map texture to uniform
state->setUniformTexture("u_cubeTex", textureCube);

Skybox

Skybox是你整個場景的包裝,它顯示了你的幾何之外的世界。你可以用一個天空盒來模擬無限的天空、山脈和其他現象。 創建Skybox:

// create a Skybox object
auto box = Skybox::create();

// set textureCube for Skybox
box->setTexture(_textureCube);

// attached to scene
_scene->addChild(box);

Light光線

光線對於營造遊戲的氣氛和氛圍非常重要。目前支持4種照明技術。

  • Ambient Light(環繞光): AmbientLight對象將均勻地爲場景中的所有對象應用燈光。效果就像辦公室裏的照明。燈光在頭頂,當你看到辦公室周圍的物體時,你會看到它們在同一個燈光下。
auto light = AmbientLight::create (Color3B::RED);
addChild(light);
  • Directional Light(平行光、定向光): 平行光通常用於模擬光源,如陽光。當使用方向光時,請記住它的密度是相同的,不管你和它的關係是什麼。當你直視太陽時,它是一種強烈的光,即使你朝任何方向移動幾步 。
auto light = DirectionLight::create(Vec3(-1.0f, -1.0f, 0.0f), Color3B::RED);
addChild(light);
  • Point Light點光源:點光源通常用於模擬燈泡、燈或火炬的效果。 點光源的方向是 從發光位置到點光源 。離光源近則亮,遠則暗。
auto light = PointLight::create(Vec3(0.0f, 0.0f, 0.0f), Color3B::RED, 10000.0f);
addChild(light);
  • Spot Light聚光源:聚光源通常用於模擬手電筒。:
auto spotLight = SpotLight::create(Vec3(-1.0f, -1.0f, 0.0f), Vec3(0.0f, 0.0f, 0.0f),
Color3B::RED, 0.0, 0.5, 10000.0f) ;
addChild(spotLight);

遮光

在節點上使用照明遮罩僅對其應用特定的光源。例如,如果場景中有多個燈光,則節點只能由其中一個燈光而不是全部三個燈光照亮。可以使用setLightFlag(LightFlag)控制燈光影響哪些節點對象。需要注意的是,所有光源都是在一個過程中渲染的。由於移動平臺性能問題,不建議使用多個光源。默認最大值爲1。如果要打開多個光源,必須在info.plist中定義以下鍵:

<key> cocos2d.x.3d.max_dir_light_in_shader </key>
<integer> 1 </integer>
<key> cocos2d.x.3d.max_point_light_in_shader </key>
<integer> 1 </integer>
<key> cocos2d.x.3d.max_spot_light_in_shader </key>
<integer> 1 </integer>

Terrain地形

紋理用於表示高度圖。最多可以使用4種紋理來混合地形、草地、道路等的細節。

HeightMap高度圖

高度圖對象是地形圖的核心。與普通圖像不同,高度圖表示頂點的高度。它決定了地形的幾何形狀。

DetailMap詳細圖

DetailMap對象是確定地形細節的紋理列表,最多可以使用四個紋理。

AlphaMap字母表

AlphaMap對象是一個圖像,其數據是細節貼圖的混合權重。混合結果是最終地形的外觀。

LOD(Level Of Detail)政策

地形使用一種稱爲細節層次(Level Of Detail,LOD)的優化技術。這是一種渲染技術,當對象與攝影機的距離增加時,它可以減少對其進行渲染的垂直(或三角形)數量。用戶可以通過調用Terrain::setLODDistance(float lod1, float lod2, float lod3) 方法來設置到相機的距離。
相鄰的地形對象塊,其LOD不同,可能會產生裂紋僞影。地形提供了兩個功能來避免它們:

Terrain::CrackFixedType::SKIRT
Terrain::CrackFixedType::INCREASE_LOWER

Terrain::CrackFixedType::SKIRT: 將在塊的每個邊上生成四個類似裙子的網格。
Terrain::CrackFixedType::INCREASE_LOWER: 將動態調整每個塊索引以無縫連接它們。

創建Terrain地形
以下代碼片段創建一個玩家並將其放置在地形圖上:

player->setScale(0.08);
player->setPositionY(terrain->getHeight(player->getPositionX(),player->getPositionZ()));

創建所有DetailMap對象,最多4個。我們需要將DetailMap對象傳遞給Terrain::DetailMap結構體:

Terrain::DetailMap r("dirt.dds");
Terrain::DetailMap g("grass.dds");
Terrain::DetailMap b("road.dds");
Terrain::DetailMap a("greenSkin.jpg");

用DetailMap對象創建TerrainData變量,並指定高度圖文件路徑和字母圖文件路徑:

Terrain::TerrainData data("chapter9/heightmap16.jpg","TerrainTest/alphamap.png", r, g, b, a);

傳遞TerrainData對象給Terrain::create,最後一個參數決定LOD政策:

_terrain = Terrain::create(data, Terrain::CrackFixedType::SKIRT);

如果你設置Terrain對象照相機遮罩(camera mask)並添加到一個節點Node或一個場景Scene。這時要當心,Terrain被添加到節點或場景後,就不能再用transform(translate,scale)。另外,如果在addChild方法之後做以上操作,可能會導致一個錯誤。

使用方法Terrain::getHeight(float x, float z, Vec3 * normal= nullptr)可以獲得指定位置的高度。當你想把一個Sprite3D對象或任意節點放在地形圖上時,這個方法特別有用。

射線地形交會試驗將會計算指定位置的交點。
Terrain::CrackFixedType::SKIRT會產生四個 每一塊邊緣都有裙狀網格 。
Terrain::CrackFixedType::INCREASE_LOWER將會動態調整每個塊索引以無縫連接它們。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章