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

前:

  最近几天一个很虎比的教程吸引了我的视线,原作者使用c# / JavaScript逐步实现了一个基本的3D软引擎。

我不懂上面提到的语言,所以,准备用我熟悉的C++和Win32实现重造这个轮子。:)

注意:

  • 这不是一篇关于DirectX / OpenGL (GPU)的文章,本系列文章将实现一个软件(CPU)驱动的“DirectX”,很有趣吧,啊哈。
  • 本文假设读者有一定的计算机图形学的基础,使用OpenGL / DirectX 写过程序。
  • 本文假设读者有一定的Win32基础(不是MFC),最起码能写出一个空白窗口的程序。
  • 有人可能会问,现在的计算机都有显卡,问什么还要写软引擎呢?的确,对于实际的应用来说,这东西确实没啥用,写这个东西只是出于好玩,另外,它也能帮助你真正的理解3D流水线,当你再去学DirectX和OpenGL的时候,也会变得更加简单。

正文:

  本文将实现下面这个小玩意(仅仅使用Win32中的SetPixel()函数):

源码下载:链接: http://pan.baidu.com/s/1kTidLYn 密码: 5qul

(注:源码中使用了一点C++11的特性,请使用支持C++11的编译器编译)

1.创建窗口.

要绘制东西,首先要有一个窗口,为此我们设计一个BasicGame类:

BasicGame.h

 1 #ifndef _BASIC_GAME_
 2 #define _BASIC_GAME_
 3
 4 #include <windows.h>
 5 #include <string>
 6
 7 class BasicGame
 8 {
 9 public:
10     virtual bool init(){return true;}
11     virtual void render(){}
12     virtual void quit(){}
13     virtual void update(float deltaTime){};
14
15     BasicGame();
16     virtual ~BasicGame(){}
17
18     bool create(HINSTANCE instance, int cmdShow);
19
20     std::string getCaption() {return caption_;}
21     int    getWidth() {return width_;}
22     int getHeight() {return height_;}
23     HWND getHwnd(){return hwnd_;}
24
25     void setCaption(std::string caption){caption_ = caption;}
26     void setWidth(int width) {width_ = width;}
27     void setHeight(int height) {height_ = height;}
28
29 private:
30     WORD registerClass(HINSTANCE instance);
31     bool windowInit(HINSTANCE instance, int cmdShow);
32
33     static LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
34
35 protected:
36     std::string caption_;
37     int height_;
38     int width_;
39     HWND hwnd_;
40 };
41
42 #endif //_BASIC_GAME_

BasicGame.cpp

 1 #include "BasicGame.h"
 2
 3 BasicGame::BasicGame()
 4 {
 5     width_ = 800;
 6     height_ = 600;
 7 }
 8
 9 bool BasicGame::create(HINSTANCE instance, int cmdShow)
10 {
11     registerClass(instance);
12
13     if(!windowInit(instance, cmdShow))
14     {
15         return false;
16     }
17
18     return true;
19 }
20
21 WORD BasicGame::registerClass(HINSTANCE instance)
22 {
23     WNDCLASSEX wcex;
24
25     wcex.cbSize = sizeof(WNDCLASSEX);
26
27     wcex.style            = CS_HREDRAW | CS_VREDRAW;
28     wcex.lpfnWndProc    = (WNDPROC)BasicGame::WndProc;
29     wcex.cbClsExtra        = 0;
30     wcex.cbWndExtra        = 0;
31     wcex.hInstance        = instance;
32     wcex.hIcon            = NULL;
33     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
34     wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
35     wcex.lpszMenuName    = NULL;
36     wcex.lpszClassName    = "BasicGame";
37     wcex.hIconSm        = NULL;
38
39     return RegisterClassEx(&wcex);
40 }
41
42 bool BasicGame::windowInit(HINSTANCE instance, int cmdShow)
43 {
44     hwnd_ = CreateWindow(
45         "BasicGame",
46         caption_.c_str(),
47         WS_OVERLAPPEDWINDOW,
48         CW_USEDEFAULT,
49         0,
50         CW_USEDEFAULT,
51         0,
52         NULL,
53         NULL,
54         instance,
55         NULL);
56
57     if (!hwnd_)
58         return false;
59
60     MoveWindow(hwnd_,0,0,width_,height_,true);
61     ShowWindow(hwnd_, cmdShow);
62     UpdateWindow(hwnd_);
63
64     return true;
65 }
66
67 LRESULT CALLBACK  BasicGame::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
68 {
69     switch (msg)
70     {
71     case WM_DESTROY:
72         PostQuitMessage(0);
73         break;
74     }
75     return DefWindowProc(hWnd, msg, wParam, lParam);
76 }

BasicGame简单的对窗口创建进行了封装,其中

1 virtual bool init(){return true;}
2 virtual void render(){}
3 virtual void quit(){}
4 virtual void update(float deltaTime){};

是为了后面实现游戏循环预留的接口,我们要创建窗口,只需要继承BasicGame就可以了。

2.游戏循环。

利用上面的类,我们便可以这样写WinMain()函数:

 1 #include "BasicGame.h"
 2 #include <memory>
 3
 4 int WINAPI WinMain(HINSTANCE hInstance,
 5                    HINSTANCE hPrevInstance,
 6                    LPSTR lpCmdLine,
 7                    int nCmdShow)
 8 {
 9     std::unique_ptr<BasicGame> game(new BasicGame);
10
11     game->setCaption("Hello,World");
12     game->setWidth(800);
13     game->setHeight(600);
14
15     if(!game->create(hInstance, nCmdShow))
16         return -1;
17
18     game->init();
19
20     float tNow = 0.f;
21     float tPre = static_cast<float>(GetTickCount()) / 1000;
22
23     float timeSinceLastUpdate = 0.f;
24
25     float timePerPrame = 1.f / 60.f;
26
27     MSG msg;
28     for(;;)
29     {
30         if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
31         {
32             if(msg.message == WM_QUIT) break;
33             TranslateMessage(&msg);
34             DispatchMessage(&msg);
35         }
36         else
37         {
38             tNow = static_cast<float>(GetTickCount()) / 1000;
39
40             timeSinceLastUpdate += (tNow - tPre);
41             while(timeSinceLastUpdate > timePerPrame)
42             {
43                 timeSinceLastUpdate -= timePerPrame;
44                 game->update(timePerPrame);
45             }
46             tPre = tNow;
47             game->render();
48         }
49     }
50     game->quit();
51
52     return static_cast<int>(msg.wParam);
53 };
  • 如果我们在一个继承自BasicGame的新类NewGame,只需要将
1 std::unique_ptr<BasicGame> game(new BasicGame);

换成

1 std::unique_ptr<BasicGame> game(new NewGame);

然后再NewGame类中实现init(),render(),quit(),update(float)四个函数即可。

  • 重点在于20-50行到底做了什么? 如果你做过游戏,那么你一定不会陌生:-)

GetTickCount()函数返回系统启动到现在经过的时间(毫秒),能存储的最大值约为49.71天,超过这个期限,就会归零,我们认为是无穷大即可。

我们用TimeSinceLastUpdate变量表示自从上一次执行update函数所经过的时间。

每次执行update函数,我们便减去一个TimePerFrame(每帧所耗费的时间)。

上面的示例中,TimePerFrame = 1.0f / 60.0f ,则update()函数每秒将被执行60次,无论你的电脑性能如何。这个特性对于视频游戏来说是非常非常重要的。

3.数学基础。

本文不是讨论3D数学的,所以直接使用了开源的数学库(GLM),关于这个库的用法请看这篇博文

4.Camera & Mesh。

现在可以开始我们的软引擎的编码了。:)

首先,我们需要定义Camera和Mesh两个结构体,其中Mesh用来表示3D空间中的一个物体。

代码如下:

 1 #define GLM_FORCE_RADIANS
 2 #include <glm/glm.hpp>
 3 #include <glm/gtc/matrix_transform.hpp>
 4 #include <string>
 5
 6 namespace SoftEngine
 7 {
 8
 9     struct Camera
10     {
11         glm::vec3 position_;
12         glm::vec3 target_;
13     };
14
15     struct Mesh
16     {
17         std::string name_;
18         glm::vec3 position_;
19         float rotation_;
20         glm::vec4 *vertices_;
21         int verticesCount_;
22
23         Mesh(std::string name, int verticesCount)
24         {
25             verticesCount_ = verticesCount;
26             vertices_ = new glm::vec4 [verticesCount_];
27
28             name_ = name;
29         }
30         ~Mesh()
31         {
32             delete vertices_;
33         }
34     };
35 }

举例来说,如果你用Mesh来描述一个立方体:

通常只需要这样做:

 1 SoftEngine::Mesh mesh("Cube", 8);
 2
 3 mesh.vertices_[0] = glm::vec4(-1.f, 1.f, 1.f, 1.f);
 4 mesh.vertices_[1] = glm::vec4( 1.f, 1.f, 1.f, 1.f);
 5 mesh.vertices_[2] = glm::vec4(-1.f,-1.f, 1.f, 1.f);
 6 mesh.vertices_[3] = glm::vec4( 1.f,-1.f, 1.f, 1.f);
 7 mesh.vertices_[4] = glm::vec4(-1.f, 1.f,-1.f, 1.f);
 8 mesh.vertices_[5] = glm::vec4( 1.f, 1.f,-1.f, 1.f);
 9 mesh.vertices_[6] = glm::vec4( 1.f,-1.f,-1.f, 1.f);
10 mesh.vertices_[7] = glm::vec4(-1.f,-1.f,-1.f, 1.f);

5.Device.

有了Mesh,如何显示它呢?

我们知道,屏幕是二维的,而Mesh中的点是3维的,所以,如何把3维世界中的点画到二维的世界中是关键所在。

我们创建Device类,完成这项工作。

由第三部分提到的那篇博文,我们知道,最重要的部分在于下面的等式:

1 auto transformMatrix = projectionMatrix * viewMatrix * worldMatrix;

我们的Device类如下所示:

 1 namespace SoftEngine
 2 {
 3     class Device
 4     {
 5     public:
 6
 7         Device(HWND hWnd);
 8
 9         ~Device();
10
11         void present();
12
13         void render(Camera camera, Mesh *meshes, int length);
14
15     private:
16
17         glm::vec2 TransformCoordinates(glm::vec4 vector, glm::mat4 transformation);
18
19         void putPixel(int x, int y, COLORREF color);
20
21         void drawPoint(glm::vec2 point);
22
23         glm::vec2 project(glm::vec4 coord, glm::mat4 transMat);
24     private:
25
26         byte *backBuffer_;
27         HWND hWnd_;
28         HDC hDc_;
29         HDC mDc_;
30         float width_;
31         float height_;
32         HBITMAP bmp_;
33
34     };
35 }

类的定义:

 1 #include "SoftEngine.h"
 2 #include <cmath>
 3
 4 namespace SoftEngine
 5 {
 6
 7     Device::Device(HWND hWnd)
 8     {
 9         hWnd_ = hWnd;
10
11         hDc_ = GetDC(hWnd_);
12         mDc_ = ::CreateCompatibleDC(hDc_);
13
14         RECT rect = {0, 0, 0, 0};
15         GetClientRect(hWnd_, &rect);
16         width_ = static_cast<float>(rect.right - rect.left);
17         height_ = static_cast<float>(rect.bottom - rect.top);
18
19         DeleteObject(bmp_);
20         bmp_ = ::CreateCompatibleBitmap(hDc_, width_, height_);
21         ::SelectObject(mDc_, bmp_);
22     }
23
24     Device::~Device()
25     {
26         DeleteObject(hDc_);
27         DeleteObject(mDc_);
28     }
29
30     void Device::present()
31     {
32         BitBlt(hDc_, 0, 0, width_, height_, mDc_, 0, 0, SRCCOPY);
33         DeleteObject(bmp_);
34         bmp_ = ::CreateCompatibleBitmap(hDc_, width_, height_);
35         ::SelectObject(mDc_, bmp_);
36     }
37
38     void Device::putPixel(int x, int y, COLORREF color)
39     {
40         SetPixel(mDc_, x, y, color);
41     }
42
43     glm::vec2 Device::TransformCoordinates(glm::vec4 vector, glm::mat4 transformation)
44     {
45         auto x = (vector.x * transformation[0][0]) + (vector.y * transformation[1][0]) + (vector.z * transformation[2][0]) + transformation[3][0];
46         auto y = (vector.x * transformation[0][1]) + (vector.y * transformation[1][1]) + (vector.z * transformation[2][1]) + transformation[3][1];
47         //    auto z = (vector.x * transformation[0][2]) + (vector.y * transformation[1][2]) + (vector.z * transformation[2][2]) + transformation[3][2];
48         auto w = (vector.x * transformation[0][3]) + (vector.y * transformation[1][3]) + (vector.z * transformation[2][3]) + transformation[3][3];
49         return glm::vec2(x/ w, y / w);
50     }
51
52     glm::vec2 Device::project(glm::vec4 coord, glm::mat4 transMat)
53     {
54         glm::vec2 point = TransformCoordinates(coord, transMat);
55
56         auto x =   point.x * width_  + width_  / 2.0f;
57         auto y = - point.y * height_ + height_ / 2.0f;
58
59         return glm::vec2(x, y);
60     }
61
62     void Device::drawPoint(glm::vec2 point)
63     {
64         //Clipping.
65         if(point.x >= 0 && point.y >= 0 && point.x < width_ && point.y < height_)
66         {
67             putPixel(point.x, point.y, RGB(255, 255, 0));
68         }
69     }
70
71     void Device::render(Camera camera, Mesh *meshes, int length)
72     {
73         auto viewMatrix = glm::lookAt(camera.position_, camera.target_,
74             glm::vec3(0.0f, 1.0f, 0.0f));
75
76         auto temp = width_ / height_;
77         auto projectionMatrix = glm::perspective(45.0f, temp, 0.01f, 10.0f);
78
79         for(int i = 0; i < length; i++)
80         {
81             auto worldMatrix = glm::rotate(glm::mat4(1.0f), meshes[i].rotation_, meshes[i].position_);
82
83             auto transformMatrix = projectionMatrix * viewMatrix * worldMatrix;
84
85             auto &curMesh = meshes[i];
86
87             for(int j = 0; j < curMesh.verticesCount_; j++)
88             {
89                 auto point = project(curMesh.vertices_[j], transformMatrix);
90                 drawPoint(point);
91             }
92         }
93     }
94 }

为了显示物体,我们创建自己的Game类:

 1 #ifndef _GAME_
 2 #define _GAME_
 3
 4 #include "BasicGame.h"
 5 #include "SoftEngine.h"
 6 using namespace SoftEngine;
 7
 8 class Game : public BasicGame
 9 {
10 public:
11     Game();
12     virtual bool init();
13     virtual void render();
14     virtual void quit();
15     virtual void update(float deltaTime);
16
17 private:
18     Device device_;
19     Mesh mesh_;
20     Camera camera_;
21 };
22
23 #endif //_GAME_

类的定义:

 1 #include "Game.h"
 2
 3 Game::Game()
 4     :device_()
 5     ,mesh_("Cube", 8)
 6     ,camera_()
 7 {}
 8
 9 bool Game::init()
10 {
11     device_.init(getHwnd());
12
13     mesh_.vertices_[0] = glm::vec4(-1.f, 1.f, 1.f, 1.f);
14     mesh_.vertices_[1] = glm::vec4( 1.f, 1.f, 1.f, 1.f);
15     mesh_.vertices_[2] = glm::vec4(-1.f,-1.f, 1.f, 1.f);
16     mesh_.vertices_[3] = glm::vec4( 1.f,-1.f, 1.f, 1.f);
17     mesh_.vertices_[4] = glm::vec4(-1.f, 1.f,-1.f, 1.f);
18     mesh_.vertices_[5] = glm::vec4( 1.f, 1.f,-1.f, 1.f);
19     mesh_.vertices_[6] = glm::vec4( 1.f,-1.f,-1.f, 1.f);
20     mesh_.vertices_[7] = glm::vec4(-1.f,-1.f,-1.f, 1.f);
21
22     mesh_.position_ = glm::vec3(0.5f, 1.0f, 0.0f);
23
24     mesh_.rotation_ = 0.f;
25
26     camera_.position_ =  glm::vec3(0, 0, 10.f);
27     camera_.target_ = glm::vec3(0.f);
28
29     return true;
30 }
31
32 void Game::render()
33 {
34     device_.render(camera_, &mesh_, 1);
35
36     device_.present();
37 }
38
39 void Game::quit()
40 {
41
42 }
43
44 void Game::update(float deltaTime)
45 {
46     mesh_.rotation_ += 0.4f * deltaTime;
47     if(mesh_.rotation_ > 360.f)
48         mesh_.rotation_ = 0.f;
49 }

好了,现在运行程序,可以看到立方体的8个顶点在屏幕中央开心的旋转 X)

我们现在要做的是在8个点之间连上线,使其看起来更舒服一些。

那么怎么画线呢?请看这里

我们编写画线函数:

 1 void Device::drawBresenhamLine(glm::vec2 point0, glm::vec2 point1)
 2 {
 3     int x0 = (int)point0.x;
 4     int y0 = (int)point0.y;
 5     int x1 = (int)point1.x;
 6     int y1 = (int)point1.y;
 7
 8     auto dx = abs(x1 - x0);
 9     auto dy = abs(y1 - y0);
10     auto sx = (x0 < x1) ? 1 : -1;
11     auto sy = (y0 < y1) ? 1 : -1;
12     auto err = dx - dy;
13
14     while (true)
15     {
16         drawPoint(glm::vec2(x0, y0));
17
18         if ((x0 == x1) && (y0 == y1)) break;
19         auto e2 = 2 * err;
20         if (e2 > -dy) { err -= dy; x0 += sx; }
21         if (e2 < dx) { err += dx; y0 += sy; }
22     }
23 }

有3D基础的人都知道,3D里面最基本的元素就是三角形,如果能画三角形,我们就能画任何物体。

我们称一个三角形为一个“Face”,下面编写我们的Face类:

 1 struct Face
 2 {
 3     int a_;
 4     int b_;
 5     int c_;
 6
 7     void set(int a, int b, int c)
 8     {
 9         a_ = a;
10         b_ = b;
11         c_ = c;
12     }
13 };

改写Mesh类:

 1 struct Mesh
 2 {
 3     std::string name_;
 4     glm::vec3 position_;
 5     float rotation_;
 6     glm::vec4 *vertices_;
 7     int verticesCount_;
 8     Face *faces_;
 9     int facesCount_;
10
11     Mesh(std::string name, int verticesCount, int facesCount)
12     {
13         verticesCount_ = verticesCount;
14         vertices_ = new glm::vec4 [verticesCount_];
15         facesCount_ = facesCount;
16         faces_ = new Face [facesCount_];
17
18         name_ = name;
19     }
20     ~Mesh()
21     {
22         delete vertices_;
23         delete faces_;
24     }
25 };

现在,要显示一个四边形,我们只需要如下代码:

1 mesh_.vertices_[0] = glm::vec4(-1.f, 1.f, 1.f, 1.f);
2 mesh_.vertices_[1] = glm::vec4( 1.f, 1.f, 1.f, 1.f);
3 mesh_.vertices_[2] = glm::vec4(-1.f,-1.f, 1.f, 1.f);
4 mesh_.vertices_[3] = glm::vec4( 1.f,-1.f, 1.f, 1.f);
5
6 mesh_.faces_[0 ].set(0, 1, 2);
7 mesh_.faces_[1 ].set(1, 2, 3);

下面我们为此编写相应的代码:

 1 auto &curMesh = meshes[i];
 2
 3 for(int j = 0; j < curMesh.facesCount_; j++)
 4 {
 5     auto &face = curMesh.faces_[j];
 6
 7     auto vertexA = curMesh.vertices_[face.a_];
 8     auto vertexB = curMesh.vertices_[face.b_];
 9     auto vertexC = curMesh.vertices_[face.c_];
10
11     auto pixelA = project(vertexA, transformMatrix);
12     auto pixelB = project(vertexB, transformMatrix);
13     auto pixelC = project(vertexC, transformMatrix);
14
15     drawBresenhamLine(pixelA, pixelB);
16     drawBresenhamLine(pixelB, pixelC);
17     drawBresenhamLine(pixelC, pixelA);
18 }

现在我们只需要定义立方体的8个顶点和12个面,立方体就能正确地显示了:

 1 mesh_.vertices_[0] = glm::vec4(-1.f, 1.f, 1.f, 1.f);
 2 mesh_.vertices_[1] = glm::vec4( 1.f, 1.f, 1.f, 1.f);
 3 mesh_.vertices_[2] = glm::vec4(-1.f,-1.f, 1.f, 1.f);
 4 mesh_.vertices_[3] = glm::vec4( 1.f,-1.f, 1.f, 1.f);
 5 mesh_.vertices_[4] = glm::vec4(-1.f, 1.f,-1.f, 1.f);
 6 mesh_.vertices_[5] = glm::vec4( 1.f, 1.f,-1.f, 1.f);
 7 mesh_.vertices_[6] = glm::vec4( 1.f,-1.f,-1.f, 1.f);
 8 mesh_.vertices_[7] = glm::vec4(-1.f,-1.f,-1.f, 1.f);
 9
10 mesh_.faces_[0 ].set(0, 1, 2);
11 mesh_.faces_[1 ].set(1, 2, 3);
12 mesh_.faces_[2 ].set(1, 3, 6);
13 mesh_.faces_[3 ].set(1, 5, 6);
14 mesh_.faces_[4 ].set(0, 1, 4);
15 mesh_.faces_[5 ].set(1, 4, 5);
16
17 mesh_.faces_[6 ].set(2, 3, 7);
18 mesh_.faces_[7 ].set(3, 6, 7);
19 mesh_.faces_[8 ].set(0, 2, 7);
20 mesh_.faces_[9 ].set(0, 4, 7);
21 mesh_.faces_[10].set(4, 5, 6);
22 mesh_.faces_[11].set(4, 6, 7);

现在我们的程序看起来象下面这样:

很神奇对吗?我们只用了一个画点的函数,就画出了这么好玩的东西,Awesome!

ksco

2014.6.24

转载请注明出处。

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

时间: 2024-10-12 08:38:00

使用Windows GDI 做一个3D”软引擎“-Part1的相关文章

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

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

用css3做一个3D立方体

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

3D软引擎之深度排序

花了不少时间去实现了这个功能,大多问题都出现在低级错误,看来以后要提醒十二分精神!错误的原因是: <span style="color: rgb(255, 255, 255); font-family: Arial; font-size: 14px; line-height: 22px;"> </span>void CTriangle2DUtils::DrawSolidGeneralClipZOrder( D3DXVECTOR3 p0, D3DXVECTOR3

一个人独立开发 3D 游戏引擎可能吗?

作者:孙志超链接:https://www.zhihu.com/question/24733255/answer/42000966来源:知乎著作权归作者所有,转载请联系作者获得授权. 当然可以,但难道有个引擎,就可以做出真正商业化的游戏么?而且国产游戏大部分是网游啊. 几年前的老文--<一个人的服务器端>(只是为了说明游戏开发难度,不是针对题主问题.) 技术准备 能够做这个MMO的触发点是通过某些途径得到了某个大公司使用的一款3D引擎,其他的都是白手起家.当时大家还不知道有"分布式服务

Top 10:HTML5、JavaScript 3D游戏引擎和框架

由于很多人都在用JavaScript.HTML5和WebGL技术创建基于浏览器的3D游戏,所有JavaScript 3D游戏引擎是一个人们主题.基于浏览器的游戏最棒的地方是平台独立,它们能在iOS.Android.Windows或其他任何平台上运行. 有很多的JavaScript能够用于创建基于浏览器.使用HTML5和WebGL的3D游戏.然后,选择一个合适的游戏引擎是一个不小的挑战,它有时能帮你完成项目或突破项目瓶颈. 为了让你的选择变的容易,我们已经通过分析大多数JavaScript 3D游

HTML5、JavaScript 3D游戏引擎和框架

由于很多人都在用JavaScript.HTML5和WebGL技术创建基于浏览器的3D游戏,所有JavaScript 3D游戏引擎是一个人们主题.基于浏览器的游戏最棒的地方是平台独立,它们能在iOS.Android.Windows或其他任何平台上运行. 有很多的JavaScript能够用于创建基于浏览器.使用HTML5和WebGL的3D游戏.然后,选择一个合适的游戏引擎是一个不小的挑战,它有时能帮你完成项目或突破项目瓶颈. 为了让你的选择变的容易,我们已经通过分析大多数JavaScript 3D游

做一个WINDOWS下破解WIFI。不需要Linux抓包!

搬家了,没网了.没有WIFI了! 想破解,不过没有Linux环境,不能抓包!破解! 于是自己动手开工! 在windows 下直接破解.貌似国内 还没看到.如果有了,那么请各位童鞋 提醒一下.赶急 要使用啊!! 最终: 不过有点问题,如果路由器 启用混淆模式,那么如何拿到真实的握手协议呢? 做一个WINDOWS下破解WIFI.不需要Linux抓包!

【Bugly干货分享】一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎

Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言 好吧,说是“粒子引擎”还是大言不惭而标题党了,离真正的粒子引擎还有点远.废话少说,先看[demo],扫描后点击屏幕有惊喜哦… 本文将教会你做一个简单的canvas粒子制造器(下称引擎). 世界观 这个简单的引擎里需要有三种元素:世界(World).发射器(Launcher).粒子(Grain).总得来说就是:发射器存在于世界之中,

【转载】从零实现3D图像引擎:(2)画2D直线不简单

原文:从零实现3D图像引擎:(2)画2D直线不简单 1. 数学分析 1) 画直线的问题 本来我以为画直线会很容易,随便拿个直线公式,遍历X求Y画出来不就完了么,但事实并非如此.以2D直线为例,因为3D直线也只是多引入了个Z坐标而已.关键的问题:我们在数学中所学的直线是基于实数域的,而在计算机屏幕上,所画的直线是基于正整数域的,可以想象这么一个情形,在直线的某一点X=1,Y=0.01时,在屏幕上如何画呢?下图对比了实数域的直线,与基于正整数域的直线: 为什么直线在正整数域是不连续的呢,还记得斜率的