DirectX11--实现一个3D魔方(3)

前言

(2019/1/9 09:23)上一章我们主要讲述了魔方的旋转,这个旋转真是有毒啊,搞完这个部分搭键鼠操作不到半天应该就可以搭完了吧...

(2019/1/9 21:25)啊,真香


有人发这张图片问我写魔方的目的是不是这个。。。噗

现在光是键鼠相关的代码也搭了400行左右。。其中键盘相关的调用真的是毫无技术可言,重点实现基本上都被鼠标给耽搁了。

章节
实现一个3D魔方(1)
实现一个3D魔方(2)
实现一个3D魔方(3)

Github项目--魔方

对了,在此之前你可以去了解一下我这里所使用的摄像机、碰撞检测、鼠标拾取相关模块的实现:

章节
10 摄像机类
18 使用DirectXCollision库进行碰撞检测
21 鼠标拾取

最后日常安利一波本人正在编写的DX11教程。

DirectX11 With Windows SDK完整目录

欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。

我就简单提一下键盘的逻辑

键盘操作使用的是DXTK经过修改的Keyboard库。

因为之前说过,Rubik::RotateX函数在响应了来自键盘的输入后,就会进入自动旋转模式,此时的键盘输入将不会响应。因此整个函数实现起来就是这么暴力:

void GameApp::KeyInput()
{
    Keyboard::State keyState = mKeyboard->GetState();
    mKeyboardTracker.Update(keyState);

    //
    // 整个魔方旋转
    //

    // 公式x
    if (mKeyboardTracker.IsKeyPressed(Keyboard::Up))
    {
        mRubik.RotateX(3, XM_PIDIV2);
        return;
    }

    // ...

    //
    // 双层旋转
    //

    // 公式r
    if (keyState.IsKeyDown(Keyboard::LeftControl) && mKeyboardTracker.IsKeyPressed(Keyboard::I))
    {
        mRubik.RotateX(-2, XM_PIDIV2);
        return;
    }

    // ...

    //
    // 单层旋转
    //

    // 公式R
    if (mKeyboardTracker.IsKeyPressed(Keyboard::I))
    {
        mRubik.RotateX(2, XM_PIDIV2);
        return;
    }

    // ...
}

我列个表格来描述键盘的36种操作,就当做说明书来看吧:

键位 对应公式 描述
Up x 整个魔方按x轴顺时针旋转
Down x‘ 整个魔方按x轴逆时针旋转
Left y 整个魔方按y轴顺时针旋转
Right y‘ 整个魔方按y轴逆时针旋转
Pg Up z‘ 整个魔方按z轴逆时针旋转
Pg Down z 整个魔方按z轴顺时针旋转
-------- ---- ------------------------
LCtrl+I r 右面两层按x轴顺时针旋转
LCtrl+K r‘ 右面两层按x轴逆时针旋转
LCtrl+J u 顶面两层按y轴顺时针旋转
LCtrl+L u‘ 顶面两层按y轴逆时针旋转
LCtrl+U f‘ 正面两层按z轴逆时针旋转
LCtrl+O f 正面两层按z轴顺时针旋转
-------- ---- ------------------------
LCtrl+W l‘ 左面两层按x轴逆时针旋转
LCtrl+S l 左面两层按x轴顺时针旋转
LCtrl+A d‘ 底面两层按y轴逆时针旋转
LCtrl+D d 底面两层按y轴顺时针旋转
LCtrl+Q b 背面两层按z轴顺时针旋转
LCtrl+E b‘ 背面两层按z轴逆时针旋转
-------- ---- ------------------------
I R 右面两层按x轴顺时针旋转
K R‘ 右面两层按x轴逆时针旋转
J U 顶面两层按y轴顺时针旋转
L U‘ 顶面两层按y轴逆时针旋转
U F‘ 正面两层按z轴逆时针旋转
O F 正面两层按z轴顺时针旋转
-------- ---- ------------------------
T M 右面两层按x轴顺时针旋转
G M‘ 右面两层按x轴逆时针旋转
F E 顶面两层按y轴顺时针旋转
H E‘ 顶面两层按y轴逆时针旋转
R S‘ 正面两层按z轴逆时针旋转
Y S 正面两层按z轴顺时针旋转
-------- ---- ------------------------
W L‘ 右面两层按x轴顺时针旋转
S L 右面两层按x轴逆时针旋转
A D‘ 顶面两层按y轴顺时针旋转
D D 顶面两层按y轴逆时针旋转
Q B 正面两层按z轴逆时针旋转
E B‘ 正面两层按z轴顺时针旋转

鼠标逻辑相关的实现

鼠标相关的实现难度远比键盘复杂多了,我主要分三个部分来讲:

  1. 立方体的拾取与判断拾取到的立方体表面
  2. 根据拖动方向判断旋转轴
  3. 鼠标在不同的操作阶段对应的处理

在此之前,我先讲讲在这个项目加的一点点私货

鼠标的轻微抖动效果

首先来看效果

这个效果的实现比较简单,现在我使用的是第三人称摄像机。现规定以游戏窗口中心为0偏移点,那么偏离中心做左右移动会产生绕中心以Y轴旋转,而做上下移动产生绕中心以X轴旋转。

相关代码的实现如下:

void GameApp::MouseInput(float dt)
{
    Mouse::State mouseState = mMouse->GetState();
    // ...

    // 获取子类
    auto cam3rd = dynamic_cast<ThirdPersonCamera*>(mCamera.get());

    // ******************
    // 第三人称摄像机的操作
    //

    // 绕物体旋转,添加轻微抖动
    cam3rd->SetRotationX(XM_PIDIV2 * 0.6f + (mouseState.y - mClientHeight / 2) *  0.0001f);
    cam3rd->SetRotationY(-XM_PIDIV4 + (mouseState.x - mClientWidth / 2) * 0.0001f);
    cam3rd->Approach(-mouseState.scrollWheelValue / 120 * 1.0f);

    // 更新观察矩阵
    mCamera->UpdateViewMatrix();
    mBasicEffect.SetViewMatrix(mCamera->GetViewXM());

    // 重置滚轮值
    mMouse->ResetScrollWheelValue();

    // ...
}

立方体的拾取与判断拾取到的立方体表面

现在要先判断鼠标点击拾取到哪个立方体,考虑到我们能拾取到的立方体都是可以看到的,这也说明它们的深度值肯定是最小的。因此,我们的Rubik::HitCube函数实现如下:

DirectX::XMINT3 Rubik::HitCube(Ray ray, float * pDist) const
{
    BoundingOrientedBox box(XMFLOAT3(), XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f));
    BoundingOrientedBox transformedBox;
    XMINT3 res = XMINT3(-1, -1, -1);
    float dist, minDist = FLT_MAX;

    // 优先拾取暴露在外的立方体(同时也是距离摄像机最近的)
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 3; ++j)
        {
            for (int k = 0; k < 3; ++k)
            {
                box.Transform(transformedBox, mCubes[i][j][k].GetWorldMatrix());
                if (ray.Hit(transformedBox, &dist) && dist < minDist)
                {
                    minDist = dist;
                    res = XMINT3(i, j, k);
                }
            }
        }
    }
    if (pDist)
        *pDist = (minDist == FLT_MAX ? 0.0f : minDist);

    return res;
}

上面的函数会遍历所有的立方体,找出深度最小且拾取到的立方体的索引值,通过pDist可以返回射线起始点到目标立方体表面的最小距离。这个信息非常有用,稍后我们会提到。

对了,如果没有拾取到立方体呢?我们可以利用屏幕空白的地方,在拖动这些地方的时候会带动整个魔方的旋转。

根据拖动方向判断旋转轴

首先给出魔方旋转轴的枚举:

enum RubikRotationAxis {
    RubikRotationAxis_X,    // 绕X轴旋转
    RubikRotationAxis_Y,    // 绕Y轴旋转
    RubikRotationAxis_Z,    // 绕Z轴旋转
};

现在让我们再看一眼魔方:

界面中可以看到魔方的面有+X面,+Y面和-Z面。

在我们拾取到立方体后,我们还要根据这两个信息来确定旋转轴:

  1. 当前具体是拾取到立方体的哪个面
  2. 当前鼠标的拖动方向

这又是一个十分细的问题。其中-X面和-Z面在屏幕上是对称关系,代码实现可以做镜像处理,但是+Y面的操作跟其它两个面又有一些差别。

鼠标落在立方体的-Z面

现在我们只讨论拾取到立方体索引[2][2][0]的情况,鼠标落在了该立方体白色的表面上。我们只是知道鼠标拾取到当前立方体上,那怎么做才能知道它现在拾取的是其中的-Z面呢?

Rubik::HitCube函数不仅返回了拾取到的立方体索引,还有射线击中立方体表面的最短距离。我们知道-Z面的所有顶点的z值在不产生旋转的情况下都会为-3,因此我们只需要将得到的 \(t\) 值带入射线方程 \(\mathbf{p}=\mathbf{e}+t\mathbf{d}\) 中,判断求得的 \(\mathbf{p}\) 其中的z分量是否为3,如果是,那说明当前鼠标拾取的是该立方体的-Z面。

接下来就是要讨论用鼠标拖动魔方会产生怎么样的旋转问题了。我们还需要确定当前的拖动会让哪一层魔方旋转(或者说绕什么轴旋转)。以下图为例:

上图的X轴和Y轴对应的是屏幕坐标系,坐标轴的原点为我鼠标刚点击时的落点,通过两条虚线,可以将鼠标的拖动方向划分为四个部分,对应魔方旋转的四种情况。其中屏幕坐标系的主+X(-X)拖动方向会使得魔方的+Y面做逆(顺)时针旋转,而屏幕坐标系的主+Y(-Y)拖动方向会使得魔方的+X面做逆(顺)时针旋转。

我们可以将这些情况进行简单归类,即当X方向的瞬时位移量比Y方向的大时,魔方的+Y面就会绕Y轴进行旋转,反之则是魔方的+X面绕X轴进行旋转。

这里先把GameApp中所有与鼠标操作相关的新增成员先列出来,后面我就不再重复:

//
// 鼠标操作控制
//

int mClickPosX, mClickPosY;                 // 初次点击时鼠标位置
float mSlideDelay;                          // 拖动延迟响应时间
float mCurrDelay;                           // 当前延迟时间
bool mDirectionLocked;                      // 方向锁
RubikRotationAxis mCurrRotationAxis;        // 当前鼠标拖动时的旋转轴
int mSlidePos;                              // 当前鼠标拖动的层数索引,3为整个魔方

mSlidePosmCurrRotationAxis用于保留判断旋转轴和层数的结果,以保证后续旋转的一致性。

核心判断方法如下:

// 判断当前主要是垂直操作还是水平操作
bool isVertical = abs(dx) < abs(dy);
// 当前鼠标操纵的是-Z面,根据操作类型决定旋转轴
if (pos.z == 0 && fabs((ray.origin.z + dist * ray.direction.z) - (-3.0f)) < 1e-5f)
{
    mSlidePos = isVertical ? pos.x : pos.y;
    mCurrRotationAxis = isVertical ? RubikRotationAxis_X : RubikRotationAxis_Y;
}

pos为鼠标拾取到的立方体索引。

鼠标落在立方体的+X面

现在我们拾取到了索引为[2][2][0]立方体的+X面,该表面所有顶点的x值在不旋转的情况下为3。当鼠标拖动时的X偏移量比Y的大时,会使得魔方的+Y面绕Y轴做旋转,反之则使得魔方的-X面绕X轴做旋转。

这部分的判断如下:

// 当前鼠标操纵的是+X面,根据操作类型决定旋转轴
if (pos.x == 2 && fabs((ray.origin.x + dist * ray.direction.x) - 3.0f) < 1e-5f)
{
    mSlidePos = isVertical ? pos.z : pos.y;
    mCurrRotationAxis = isVertical ? RubikRotationAxis_Z : RubikRotationAxis_Y;
}

鼠标落在立方体的+Y面

之前+X面和-Z面在屏幕中是对称的,处理过程基本上差不多。但是处理+Y面的情况又不一样了,先看下图:

现在的虚线按垂直和水平方向划分成四个拖动区域。当鼠标在屏幕坐标系拖动时,如果X的瞬时偏移量和Y的符号是一致的(划分虚线的右下区域和左上区域), 魔方的-Z面会绕Z轴旋转;如果异号(划分虚线的左下区域和右上区域),魔方的+X面会绕X轴旋转。

然后就是魔方+Y面的顶点在不产生旋转的情况下y值恒为3,因此这部分的判断逻辑如下:

// 当前鼠标操纵的是+Y面,要判断平移变化量dx和dy的符号来决定旋转方向
if (pos.y == 2 && fabs((ray.origin.y + dist * ray.direction.y) - 3.0f) < 1e-5f)
{
    // 判断异号
    bool diffSign = ((dx & 0x80000000) != (dy & 0x80000000));
    mSlidePos = diffSign ? pos.x : pos.z;
    mCurrRotationAxis = diffSign ? RubikRotationAxis_X : RubikRotationAxis_Z;
}

鼠标没有拾取到魔方

前面我们一直都是在讨论鼠标拾取到魔方的立方体产生了单层旋转的情况。现在我们还想让整个魔方进行旋转,可以依靠拖动游戏界面的空白区域来实现,按下图的方式划分成两片区域:

只要在魔方区域外拖动,且水平偏移量比垂直的大,就会产生绕Y轴的旋转。在窗口左(右)半部分产生了主垂直拖动则会绕X(Z)轴旋转。

整个拾取部分的判断如下:

// 找到当前鼠标点击的方块索引
Ray ray = Ray::ScreenToRay(*mCamera, (float)mouseState.x, (float)mouseState.y);
float dist;
XMINT3 pos = mRubik.HitCube(ray, &dist);

// 判断当前主要是垂直操作还是水平操作
bool isVertical = abs(dx) < abs(dy);
// 当前鼠标操纵的是-Z面,根据操作类型决定旋转轴
if (pos.z == 0 && fabs((ray.origin.z + dist * ray.direction.z) - (-3.0f)) < 1e-5f)
{
    mSlidePos = isVertical ? pos.x : pos.y;
    mCurrRotationAxis = isVertical ? RubikRotationAxis_X : RubikRotationAxis_Y;
}
// 当前鼠标操纵的是+X面,根据操作类型决定旋转轴
else if (pos.x == 2 && fabs((ray.origin.x + dist * ray.direction.x) - 3.0f) < 1e-5f)
{
    mSlidePos = isVertical ? pos.z : pos.y;
    mCurrRotationAxis = isVertical ? RubikRotationAxis_Z : RubikRotationAxis_Y;
}
// 当前鼠标操纵的是+Y面,要判断平移变化量dx和dy的符号来决定旋转方向
else if (pos.y == 2 && fabs((ray.origin.y + dist * ray.direction.y) - 3.0f) < 1e-5f)
{
    // 判断异号
    bool diffSign = ((dx & 0x80000000) != (dy & 0x80000000));
    mSlidePos = diffSign ? pos.x : pos.z;
    mCurrRotationAxis = diffSign ? RubikRotationAxis_X : RubikRotationAxis_Z;
}
// 当前鼠标操纵的是空白地区,则对整个魔方旋转
else
{
    mSlidePos = 3;
    // 水平操作是Y轴旋转
    if (!isVertical)
    {
        mCurrRotationAxis = RubikRotationAxis_Y;
    }
    // 屏幕左半部分的垂直操作是X轴旋转
    else if (mouseState.x < mClientWidth / 2)
    {
        mCurrRotationAxis = RubikRotationAxis_X;
    }
    // 屏幕右半部分的垂直操作是Z轴旋转
    else
    {
        mCurrRotationAxis = RubikRotationAxis_Z;
    }
}           

鼠标在不同的操作阶段对应的处理

鼠标拖动魔方旋转可以分为三个阶段:鼠标初次点击、鼠标产生拖动、鼠标刚释放。

确定拖动方向

在鼠标初次点击的时候不一定会产生偏移量,但我们必须要在这个时候判断鼠标是在做垂直拖动还是竖直拖动来确定当前的旋转轴,以限制魔方的旋转。

现在要考虑这样一个情况,我鼠标在初次点击魔方时可能会因为手抖或者鼠标不稳产生了一个以下方向为主的瞬时移动,然后程序判断我现在在做向下的拖动,但实际情况却是我需要向右方向拖动鼠标,程序却只允许我上下拖动。这就十分尴尬了。

由于鼠标的拖动过程相对程序的运行会比较缓慢,我们可以给程序加上一个延迟判断。比如说我现在可以根据鼠标初次点击后的0.05s内产生的累计垂直/水平偏移量来判断此时是水平拖动还是竖直拖动。

此外,一旦确定这段时间内产生了偏移值,必须要加上方向锁,防止后续又重新判断旋转方向。

这部分代码实现如下:

// 此时未确定旋转方向
if (!mDirectionLocked)
{
    // 此时未记录点击位置
    if (mClickPosX == -1 && mClickPosY == -1)
    {
        // 初次点击
        if (mMouseTracker.leftButton == Mouse::ButtonStateTracker::PRESSED)
        {
            // 记录点击位置
            mClickPosX = mouseState.x;
            mClickPosY = mouseState.y;
        }
    }

    // 仅当记录了点击位置才进行更新
    if (mClickPosX != -1 && mClickPosY != -1)
        mCurrDelay += dt;
    // 未到达滑动延迟时间则结束
    if (mCurrDelay < mSlideDelay)
        return;

    // 未产生运动则不上锁
    if (abs(dx) == abs(dy))
        return;

    // 开始上方向锁
    mDirectionLocked = true;
    // 更新累积的位移变化量
    dx = mouseState.x - mClickPosX;
    dy = mouseState.y - mClickPosY;

    // 找到当前鼠标点击的方块索引
    Ray ray = Ray::ScreenToRay(*mCamera, (float)mouseState.x, (float)mouseState.y);
    // ...剩余部分就是上面的代码
}

拖动时更新魔方状态

这部分实现就比较简单了。只要鼠标左键按下,且确认方向锁,就可以进行魔方的旋转。

如果是绕X轴的旋转,鼠标向右移动和向上移动都会产生顺时针旋转。
如果是绕Y轴的旋转,只有鼠标向左移动才会产生顺时针旋转。
如果是绕Z轴的旋转,鼠标向左移动和向上移动都会产生顺时针旋转。

这里的Rotate函数最后一个参数必须要传递true以告诉内部不要进行预旋转操作。

// 上了方向锁才能进行旋转
if (mDirectionLocked)
{
    // 进行旋转
    switch (mCurrRotationAxis)
    {
    case RubikRotationAxis_X: mRubik.RotateX(mSlidePos, (dx - dy) * 0.008f, true); break;
    case RubikRotationAxis_Y: mRubik.RotateY(mSlidePos, -dx * 0.008f, true); break;
    case RubikRotationAxis_Z: mRubik.RotateZ(mSlidePos, (-dx - dy) * 0.008f, true); break;
    }
}

拖动完成后的操作

完成拖动后,需要恢复方向锁和滑动延迟,并且鼠标刚释放时产生的偏移我们直接丢掉。现在Rotate函数仅用于发送进行预旋转的命令:

// 鼠标左键是否点击
if (mouseState.leftButton)
{
    // ...
}
// 鼠标刚释放
else if (mMouseTracker.leftButton == Mouse::ButtonStateTracker::RELEASED)
{
    // 释放方向锁
    mDirectionLocked = false;
    // 滑动延迟归零
    mCurrDelay = 0.0f;
    // 坐标移出屏幕
    mClickPosX = mClickPosY = -1;
    // 发送完成指令,进行预旋转
    switch (mCurrRotationAxis)
    {
    case RubikRotationAxis_X: mRubik.RotateX(mSlidePos, 0.0f); break;
    case RubikRotationAxis_Y: mRubik.RotateY(mSlidePos, 0.0f); break;
    case RubikRotationAxis_Z: mRubik.RotateZ(mSlidePos, 0.0f); break;
    }
}

最终鼠标拖动的效果如下:

键盘的效果如下:

至此魔方的一些核心实现就讲的差不多了。最后无非就是功能上的堆叠了。到现在写魔方的实现用了2天工时,博客也差不多2天。

这一章也写了快500行内容,比代码还多。

未完待续。。。

Github项目--魔方

欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。

原文地址:https://www.cnblogs.com/X-Jun/p/10247018.html

时间: 2024-11-08 22:08:09

DirectX11--实现一个3D魔方(3)的相关文章

3D魔方的思路与实现

思路:(要实现魔方六个面以及九个小块的样式并旋转展示.) 内容部分:魔方分为六个面,每个面有九个小块,使用一个div内嵌套六个div再嵌套九个div实现内容部分. 样式部分:最外层div设置属性 transform-style: preserve-3d;实现整体的3D效果. 内部六个面使用 transform: rotateX/Y/Z(0deg) translateZ(1px); 分别平移旋转不同的角度,与第一个面组成立方体. 设置每个面九个小块的大小和边框样式(圆角)以及背景颜色. 最后设置整

使用CSS3实现一个3D相册

CSS3系列我已经写过两篇文章,感兴趣的同学可以先看一下CSS3初体验之奇技淫巧,CSS3 3D立方体效果-transform也不过如此 第一篇主要列出了一些常用或经典的CSS3技巧和方法:第二篇是一个用CSS3实现的立方体实例,详细讲解了3D旋转和transform等属性. 本文再来利用CSS3属性来编写一个实例,话不多说,先直接看看效果.3D相册实例DEMO 因为前面已经讲解过一些属性的用法,此篇文章不再赘述,只记录这个实例的编码过程.项目代码已上传至github,项目代码github地址

用css3做一个3D立方体

首先看一下效果图 1.坐标系,要在脑海里先建立一个3D坐标系 如下图,看清楚x,y,z轴 2.html代码. <div class="container"> <!--包裹六个面的元素--> <div class="cube"> <!--立方体的六个面--> <div class="plane-front">前面</div> <div class="plane-

使用纯CSS3实现一个3D旋转的书本

有一些前沿的电商网站已经开始使用3D模型来展示商品并支持在线定制,而其中图书的展示是最为简单的一种, 无需复杂的建模过程,使用图片和CSS3的一些变换即可实现更好的展示效果,简洁而实用. 书本的3D模型是所有商品中最为简单的,因为其本质上就是一个立方体(cube),只是带有封面/封底和左侧封条. 所以要构造一个3D书本展示,问题就被分解为构造一个立方体+旋转+图片背景. 1. 构造一个立方体 要创建一个立方体,首先我们需要创建一个虚拟的三维视觉空间,这可以通过设置包容器元素的perspectiv

【小松教你手游开发】【系统模块开发】做一个3d旋转菜单

在unity做一个3d旋转菜单,像乱斗西游2的这种: 暂时有两种方法可以实现: 一.当做是2d界面,通过定义几个固定点的坐标.大小.透明度,还有每个点的panel depth大小,把数据存储下来,在手机滑动的过程中计算滑动划过的距离和这个panel大小的比值,乘以两个点之间的距离,获得坐标点移动的距离,通过改变x轴改变位置,同理改变大小和透明度. 这个方法我自己做2d游戏的时候实现过,做起来比较简单,没有什么可拓展性可言,并且会有很多限制,比如拖动过程中很难转变方向.要自己实现运动中的弹性(这里

使用Windows GDI 做一个3D”软引擎“-Part1

前: 最近几天一个很虎比的教程吸引了我的视线,原作者使用c# / JavaScript逐步实现了一个基本的3D软引擎. 我不懂上面提到的语言,所以,准备用我熟悉的C++和Win32实现重造这个轮子.:) 注意: 这不是一篇关于DirectX / OpenGL (GPU)的文章,本系列文章将实现一个软件(CPU)驱动的“DirectX”,很有趣吧,啊哈. 本文假设读者有一定的计算机图形学的基础,使用OpenGL / DirectX 写过程序. 本文假设读者有一定的Win32基础(不是MFC),最起

如何使用CSS3实现一个3D泡沫图形

要实现一个逼真的泡沫,涉及到比较复杂的光学/物理学知识. 我们这里先简化下问题,实现一个相对简单而足够实用的泡沫元素. 我们可以把基础的泡沫元素应用在很多场景中,比如水景.泡咖啡.啤酒甚至火焰特效中. 泡沫首先是一个圆形元素 1 2 3 4 5 .bubble {     width: 200px;     height: 200px;     border-radius: 50%; } 上面圆角边框半径等于50%,定义了一个圆形元素的样式. 添加3D阴影 泡沫应该是有色彩梯度的,这可以通过线性

3D魔方场景

3D魔方场景实例 效果: 源码: ----------------------------------------- <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title>3D魔方场景</title> <meta name="K

如何用webgl(three.js)搭建一个3D库房-第一课

今天我们来讨论一下如何使用当前流行的WebGL技术搭建一个库房并且实现实时有效交互 第一步.搭建一个3D库房首先你得知道库房长啥样,我们先来瞅瞅库房长啥样(这是我在网上找的一个库房图片,百度了"库房"一下,找不到合适的全景,我们也只能窥一斑思全豹了,就它了,特此声明:此图片归原作者所有 非本人所拍,拿来只是给读者做个案例) 下面是我用webgl做出来的3D效果图(当前展示没有货物时的整体模拟): 第二部.开干 1. 首先我们创建一个页面,引入各种需要的库文件 <script sr