12919浏览
查看: 12919|回复: 6

[项目] Arduino + OLED12864 做一个简单的3d线框渲染引擎

[复制链接]
提到3d渲染引擎,大家都会有一种遥不可及的感觉,对吧??

其实呢,没那么复杂。比如,我们用Arduino就能完成3d引擎的两个步骤之一: vertex操作。(另一个是pixel/fragment操作)

只要空间数学功底好,3d其实就是对顶点的旋转,连接构成面,然后贴图而已。当然贴图用arduino基本上是不可能的。。。

我们的做法是什么呢?先给出3d物体的各个顶点坐标,指定顶点的连接顺序,然后将3d坐标正交投影到2d的屏幕上来,按连接顺序连线

什么?不知道正交投影?呵呵,这个词看似高端,其实就是这么回事啦:丢掉z坐标,只保留x,y,就能跟屏幕坐标系对应了



为了抛砖引玉,我们来最简单的一个场景:旋转一个立方体

我们对立方体进行旋转,获得旋转后的坐标,然后丢掉z,保留x和y,之后,按照规定好的连接顺序,将屏幕上的点连接起来
就这么简单!如果有兴趣,大家可以自己yy一个物体,把顶点替换掉代码里的,规定好连接顺序(第几个点和第几个点相连),就可以显示自制物体啦~~




  1. //作者:微风森林
  2. #define OLED_DC 9
  3. #define OLED_CS 12
  4. #define OLED_RESET 10
  5. #include <SSD1306.h>
  6. #include <SPI.h>
  7. #include "MyObj.h"
  8. SSD1306 oled(OLED_DC, OLED_RESET);
  9. static MyVertex mp[] ={{ -16, -16,-16}, \
  10.                       {16, -16, -16}, \
  11.                       {16, 16, -16}, \
  12.                       {-16, 16, -16}, \
  13.                       {-16, -16,16}, \
  14.                       {16, -16, 16}, \
  15.                       {16, 16, 16}, \
  16.                       {-16, 16, 16}
  17. };
  18. static MyEdge me[] ={{0, 1}, \
  19.                     {1, 2}, \
  20.                     {2, 3}, \
  21.                     {3, 0}, \
  22.                     {4, 5}, \
  23.                     {5, 6}, \
  24.                     {6, 7}, \
  25.                     {7, 4}, \
  26.                     {0, 4}, \
  27.                     {1, 5}, \
  28.                     {2, 6}, \
  29.                     {3, 7}
  30. };
  31. MyObject  obj = {8, 12, mp, me,{1,0,0,0},{0,0,0}};
  32. void setup()   {
  33.   Serial.begin(9600);
  34.   SPI.begin();
  35.   oled.ssd1306_init(SSD1306_SWITCHCAPVCC);
  36.   oled.display(); // show splashscreen
  37.   delay(1000);
  38.   oled.ssd1306_command(SSD1306_INVERTDISPLAY);  //反色显示,注释掉则为黑底白图
  39.   oled.clear();   // clears the screen and buffer
  40.   moveObject(obj,64,32,0);
  41.   renderObject(obj);
  42.   oled.display();
  43. }
  44. static float qdelta[4]={0.999847695f,0,0.0174524f,0};
  45. static float qview[4]={0.99144486f,0.13052619f,0,0};
  46. static float qtemp[4];
  47. void loop()
  48. {
  49.   qproduct(qdelta,obj.quat,qtemp);
  50.   rotateObject(obj,qtemp);
  51.   oled.clear();   // clears the screen and buffer
  52.   renderObject(obj);
  53.   oled.display();
  54.   delay(10);
  55. }
  56. void moveObject(MyObject &mo, float x, float y, float z) {
  57.   mo.offset[0]=x;
  58.   mo.offset[1]=y;
  59.   mo.offset[2]=z;
  60. }
  61. void rotateObject(MyObject &mo, float* q) {
  62.   mo.quat[0]=q[0];
  63.   mo.quat[1]=q[1];
  64.   mo.quat[2]=q[2];
  65.   mo.quat[3]=q[3];
  66. }
  67. void renderObject(MyObject &mo) {
  68.   MyVertex* mv=new MyVertex[mo.numv];
  69.   qproduct(qview,mo.quat,qtemp);
  70.   qnormalized(qtemp);
  71.   for (int i = 0; i < mo.numv; i++) {
  72.     iqRot(qtemp,mo.v[i].location,mv[i].location);
  73.     mv[i].location[0] += mo.offset[0];
  74.     mv[i].location[1] += mo.offset[1];
  75.     mv[i].location[2] += mo.offset[2];
  76.   }
  77.   for (int i = 0; i < mo.nume; i++) {
  78.     int p1 = mo.e[i].connection[0];
  79.     int p2 = mo.e[i].connection[1];
  80.     oled.drawline(mv[p1].location[0], mv[p1].location[1], mv[p2].location[0], mv[p2].location[1], WHITE);
  81.   }
  82.   delete mv;
  83. }
  84. float iqRot(float q[],int v[],int result[]){
  85.   float prod[4];
  86.   prod[0] =  - q[1] * v[0] - q[2] * v[1] - q[3] * v[2];
  87.   prod[1] = q[0] * v[0] + q[2] * v[2] - q[3] * v[1];
  88.   prod[2] = q[0] * v[1] - q[1] * v[2] + q[3] * v[0];
  89.   prod[3] = q[0] * v[2] + q[1] * v[1] - q[2] * v[0];
  90.   result[0] = -prod[0] * q[1] + prod[1] * q[0] - prod[2] * q[3] + prod[3] * q[2];
  91.   result[1] = -prod[0] * q[2] + prod[1] * q[3] + prod[2] * q[0] - prod[3] * q[1];
  92.   result[2] = -prod[0] * q[3] - prod[1] * q[2] + prod[2] * q[1] + prod[3] * q[0];
  93. }
  94. void qproduct(const float* p, const float* q, float* qr) {
  95.   qr[0] = p[0] * q[0] - p[1] * q[1] - p[2] * q[2] - p[3] * q[3];
  96.   qr[1] = p[0] * q[1] + p[1] * q[0] + p[2] * q[3] - p[3] * q[2];
  97.   qr[2] = p[0] * q[2] - p[1] * q[3] + p[2] * q[0] + p[3] * q[1];
  98.   qr[3] = p[0] * q[3] + p[1] * q[2] - p[2] * q[1] + p[3] * q[0];
  99. }
  100. void qnormalized(float* q) {
  101.   float invnorm;
  102.   invnorm = fastinvsqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
  103.   if (invnorm < 100000000) {
  104.     q[0] *= invnorm;
  105.     q[1] *= invnorm;
  106.     q[2] *= invnorm;
  107.     q[3] *= invnorm;
  108.   } else {
  109.     q[0] = 1;
  110.     q[1] = 0;
  111.     q[2] = 0;
  112.     q[3] = 0;
  113.   }
  114. }
  115. float fastinvsqrt(float x) {
  116.   float halfx = 0.5f * x;
  117.   float y = x;
  118.   long i = *(long*)&y;
  119.   i = 0x5f3759df - (i>>1);
  120.   y = *(float*)&i;
  121.   y = y * (1.5f - (halfx * y * y));
  122.   return y;
  123. }
复制代码

这是程序主体文件。SCL连13,SDA连11,RST连10,D/C连9。如果大家的oled还有使能脚CE,请连着GND或者VCC,都试一下看哪个可以点亮屏幕

我们还有个自定义的结构体定义,请存为MyObj.h并放入代码同一目录中



  1. typedef struct Vertex{
  2.   int location[3];
  3. }MyVertex;
  4. typedef struct Edge{
  5.   char connection[2];
  6. }MyEdge;
  7. typedef struct Object{
  8.   int numv;
  9.   int nume;
  10.   MyVertex* v;
  11.   MyEdge* e;
  12.   float quat[4];
  13.   float offset[3];
  14. }MyObject;
复制代码

第三,我使用的OLED库文件叫做SSD1306,请将库文件解压放入arduino的library目录中


下载附件SSD1306.rar



大功已告成!可以看代码很简单,对顶点的旋转用到了四元数,这基本上是各种3d引擎的基本运算了。如果这个旋转四元数用imu融合的四元数会怎样?大家自己去试吧~~

PS:我这边代码会让oled屏幕有几个亮点,初步分析是数组越界访问了oled的framebuffer,懒得去仔细分析了,如果大家分析出是哪里有问题请麻烦告知一声

本程序是最简单的正交投影的情况。如果需要实现正常的视锥体投影,需要计算一个投影矩阵。由于是抛砖引玉我这里就不去做的,感兴趣可以翻翻资料,以后有空我也会讲讲




dsweiliang  初级技神

发表于 2016-2-1 08:44:04

炫酷
回复

使用道具 举报

Jason_G  高级技师

发表于 2016-2-1 09:35:08

回头研究研究,好玩!
回复

使用道具 举报

hnyzcj  版主

发表于 2016-2-1 12:21:27

学习了,正好手里有,我们也会用起来
回复

使用道具 举报

大连林海  初级技神
 楼主|

发表于 2016-2-1 16:12:33


高手 太多了
回复

使用道具 举报

大连林海  初级技神
 楼主|

发表于 2016-2-1 16:12:44

Jason_G 发表于 2016-2-1 09:35
回头研究研究,好玩!

缺东西 不然 就跟着做了
回复

使用道具 举报

大连林海  初级技神
 楼主|

发表于 2016-2-1 16:12:57

hnyzcj 发表于 2016-2-1 12:21
学习了,正好手里有,我们也会用起来

陈老师 真富有哦
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail