// 3D Cube by Jason Wright (C)opyright Pyrofer 2006
//
// This code is free, there is no charge for it nor should anybody else charge
// for the code, its distribution or derivitives of this code.
//
// You may use this code, modify and change and improve upon it on the following conditions,
//
// 1> You send me the modified source code and where possible make it public
// 2> I am credited for the original work in all derivitives of this code
// 3> You do not charge for this code or code derived from this code
// 4> You comment the code you change!
//
// Basically, use this to learn! I couldnt find anything this simple when I started so I hope
// that this helps others to learn. You should be able to change the LCD routines to drive almost
// any graphics device. The base resolution is 128x128 for this display. Simply set the X and Y offset
// to half the screen resolution for each axis.
// Then adjust the Z offset to make the object fit nicely in the screen.
#device PIC18F2520
#include "C:\Dev\PICC\PCW Projects\Dontronics LCD\gfxlcd.h"
#include "math.h" // include complex math routines
#ORG 0x1E00,0x1FFF{} // reserve rom spce to protect bootloader program
#fuses HS,NOWDT,NOPROTECT // set pic fuses
#use delay(clock=20000000) // set Clock speed for time calculations 20Mghz
#use rs232(baud=115200, xmit=PIN_C1, rcv=PIN_C0) // set up IO for LCD
/* 下面是定义了图像在LCD中的显示的中心位置,12843的可以改为64,32,30 */
#define OFFSETX 64 // offset for screen wont change unless
#define OFFSETY 64 // i use different screen! so its kinda fixed
#define OFFSETZ 30
void cube (void); // define the subroutines this one is the actual cube routine
void clearscreen (void); // clear the LCD screen
void lcdline (); // draw a line on the LCD
void initlcd (void); // initialise the LCD
void proginit (void); // setup other program stuff
void shutdown (void); // shutdown the LCD
// use const as they are in rom, saving ram
/* 以下部分定义了一个立方体 */
const signed int aa[8]={10,-10,-10,10, 10,-10,-10,10}; // x data for shape vertex
const signed int bb[8]={10,10,-10,-10, 10,10,-10,-10}; // y data for shape vertex
const signed int cc[8]={-10,-10,-10,-10, 10,10,10,10}; // z data for shape vertex
const int ff[12]={1,2,3,4, 5,6,7,8, 1,2,3,4}; // start vertex for lines
const int gg[12]={2,3,4,1, 6,7,8,5, 5,6,7,8}; // end vertex for lines
int sx,sy,ex,ey; // define global vars for calling graphics subroutines
void main() // begin main program
{
initlcd(); // set autobaud and clearscreen
proginit(); // call program inits
cube(); // call main cube drawing routine
delay_ms(9999); // wait for ages while we look at the pretty picture
clearscreen(); // clear the screen
shutdown(); // turn off LCD ready for power Down
while(1); // wait forever as program is done.
}
void cube() // routine to draw and calc 3d cube
{
int newx[8]; // translated screen x co-ordinates for vertex
int newy[8]; // translated screen y co-ordinates for vertex
int i,loop; // temp variable for loops
float xt,yt,zt,x,y,z,sinax,cosax,sinay,cosay,sinaz,cosaz,vertex; // lots of work variables
float xpos=0; // position for object in 3d space, in x
float ypos=0; // y
float zpos=0; // and z values
float rotx=0; // starting amount of x rotation
float roty=0; // starting amount of y rotation
float rotz=0; // starting amount of z rotation
for (loop=0; loop<=100; loop++) // rotate the cube 100 times
{
/* 修改下面的参数可以该面显示的位置 */
xpos=xpos+0.0; // move the object
ypos=ypos+0.0; // it would wander off screen
zpos=zpos+0.0; // really quick, so leave it centered
/* 下面参数定义每次旋转多少 */
rotx=rotx+0.5; // rotate the cube on X axis
roty=roty+0.5; // and on its y axis
rotz=rotz+0.0; // dont bother with z or it gets confusing
sinax=sin(rotx); // precalculate the sin and cos values
cosax=cos(rotx); // for the rotation as this saves a
sinay=sin(roty); // little time when running as we
cosay=cos(roty); // call sin and cos less often
sinaz=sin(rotz); // they are slow routines
cosaz=cos(rotz); // and we dont want slow!
for (i=0; i<8; i++) // translate 3d vertex position to 2d screen position
{
x=aa; // get x for vertex i
y=bb; // get y for vertex i
z=cc; // get z for vertex i
yt = y * cosax - z * sinax; // rotate around the x axis
zt = y * sinax + z * cosax; // using the Y and Z for the rotation
y = yt;
z = zt;
xt = x * cosay - z * sinay; // rotate around the Y axis
zt = x * sinay + z * cosay; // using X and Z
x = xt;
z = zt;
xt = X * cosaz - y * sinaz; // finaly rotate around the Z axis
yt = X * sinaz + y * cosaz; // using X and Y
x = xt;
y = yt;
x=x+xpos; // add the object position offset
y=y+ypos; // for both x and y
z=z+OFFSETZ-zpos; // as well as Z
newx=(x*64/z)+OFFSETX; // translate 3d to 2d coordinates for screen
newy=(y*64/z)+OFFSETY; // drawing so we can see the cube
}
/* 下面是清屏,实际效果闪烁比较厉害。可以改成数据写在缓存里后,直接在覆盖显示效果比较好 */
delay_ms(20); // delay for a while to allow looking at the cube
clearscreen(); // clear the screen to remove old cube
/* 为了提高显示速度,可以只清除上一帧显示到的区域 */
for (i=0; i<12; i++) // draw the lines that make up the object
{
vertex=ff-1; // temp = start vertex for this line
sx=newx[vertex]; // set line start x to vertex x position
sy=newy[vertex]; // set line start y to vertex y position
vertex=gg-1; // temp = end vertex for this line
ex=newx[vertex]; // set line end x to vertex[i+1] x position
ey=newy[vertex]; // set line end y to vertex[i+1] y position
lcdline(); // draw the line between these 2 vertex
}
}
}
void lcdline() // subroutine for drawing line
{
delay_ms(18); // wait for LCD to be ready just in case
putc(76); // send command for line
putc(sx); // send topleft X coord
putc(sy); // send topleft Y coord
putc(ex); // send bottomright x coord
putc(ey); // send bottomright y coord
putc(0); // send high byte of colour
putc(255); // send low byte of colour
}
void clearscreen() // subroutine to clear screen
{
delay_ms(20); // wait for LCD to be ready just in case
putc(69); // send clearscreen command
}
void initlcd() // routine to initialise LCD
{
delay_ms(5000); // wait for LCD to power up if it was off
putc(85); // send U for auto-baud
delay_ms(20); // wait for LCD to be ready
putc(89); // send command byte
putc(3); // with Startup command High byte
putc(1); // and low byte.
delay_ms(5000); // wait for LCD to power up if it was shutdown
clearscreen(); // clearscreen
}
void shutdown() // routine to shutdown the LCD for poweroff
{
delay_ms(20); // wait for LCD to be ready
putc(89); // send command byte
putc(3); // with Shutdown command High byte
putc(0); // and low byte.
}
void proginit(void)
{
setup_wdt(WDT_OFF); // turn off watchdog timer, low volt reset, internal clock and spi
setup_low_volt_detect(FALSE);
setup_oscillator(FALSE);
setup_spi(FALSE);
}作者: atommann 时间: 2008-4-18 16:19
好玩,涉及到计算机图形学里的变换操作。作者: macroxu 时间: 2008-4-18 17:22
/* 图片中立方体加文字的定义数据 */
const signed int aa[23]={8,-8,-8,8,8,-8,-8,8,8,8,8,8,8,0,4,-4,-8,-8,-8,-8,-8,-8,-8}; // x data for shape vertex
const signed int bb[23]={8,8,-8,-8,8,8,-8,-8,0,-4,4,-2,2,8,8,8,4,4,4,-4,-4,-4,0}; // y data for shape vertex
const signed int cc[23]={-8,-8,-8,-8,8,8,8,8,6,-6,-6,0,0,-6,6,6,-6,0,6,6,0,-6,0}; // z data for shape vertex
都是牛人,偶只有佩服的份。作者: 34480016 时间: 2008-4-19 19:24
厉害!收藏作者: armok 时间: 2008-4-19 19:25
提示: 作者被禁止或删除 内容自动屏蔽作者: greencamel 时间: 2008-4-19 20:05
唉,我什么时候才能到这种境界啊作者: xingzhang 时间: 2008-4-19 20:23
确实不错作者: kk20y 时间: 2008-4-19 21:41
顶...作者: macroxu 时间: 2008-4-19 21:48
哪位有彩色的LCD可以试一下效果应该会更好.我会把我的程序整理一下再传上来,有可能的话把3D旋转部分的算法重写一下,这个估计难度不小.毕竟人家的程序里讲了:
// This code is free, there is no charge for it nor should anybody else charge
// for the code, its distribution or derivitives of this code.
//
// You may use this code, modify and change and improve upon it on the following conditions,
//
// 1> You send me the modified source code and where possible make it public
// 2> I am credited for the original work in all derivitives of this code
// 3> You do not charge for this code or code derived from this code
// 4> You comment the code you change!作者: gwh1128 时间: 2008-4-20 00:08
确实有意思,有空我也试试,正好有个3100屏作者: wkman 时间: 2008-4-20 16:52
pic驱动的?作者: macroxu 时间: 2008-4-20 17:11
我做的是M128驱动的,参考程序是用PIC驱动的.作者: 525133174 时间: 2008-4-20 18:02
牛,应该很好玩,参考一下作者: vivalite 时间: 2008-4-20 18:16
呵呵 不错
我想起来还有一个用Javascript的,也能做参考:
http://www.wxs.ca/js3d/index.html作者: chenpeng 时间: 2008-4-21 18:33
牛人,记号先作者: bjj9217 时间: 2008-4-21 22:15
记号!作者: lionliu 时间: 2008-4-24 11:39
记号!作者: zc3909 时间: 2008-4-24 12:26
不错呀作者: xyang18 时间: 2008-4-24 12:47
虚心学习!!!作者: weiwgml 时间: 2008-4-24 13:14
有意思……作者: ljxh401 时间: 2008-4-24 17:57
转贴网上的一篇文章
OK,我们已经讲完了矢量的基本概念。我们开始两个矩阵的和。它与矢量相加非常相似,这里就不讨论了。设I是矩阵的一行,J是矩阵的一列,(i,j)是矩阵的一个元素。我们讨论与3D变换有关的重要的矩阵操作原理。两个矩阵相乘,而且M x N <> N x M。例如: A 4x4矩阵相乘公式如果 A=(aij)4x4, B=(bij)4x4, 那么 A x B= | S> a1jbj1 S> a1jbj2 S> a1jbj3 S> a1jbj4 | | | | S> a2jbj1 S> a2jbj2 S> a2jbj3 S> a2jbj4 | | | | S> a3jbj1 S> a3jbj2 S> a3jbj3 S> a3jbj4 | | | | S> a4jbj1 S> a4jbj2 S> a4jbj3 S> a4jbj4 |
其中 j=1,2,3,4
而且如果 AxB=(cik)4x4 那么我们可以在一行上写下: cik = S>4, j=1 aijbjk ( a1, a2, a3 ) x B = (Sum(aibi1) + b4,1, Sum(aibi2) + b4,2, Sum(aibi3) + b4,3 )
现在,我们可以试着把一些矩阵乘以单位阵来了解矩阵相乘的性质。我们把矩阵与矢量相乘结合在一起。下面有一个公式把3D矢量乘以一个4x4矩阵(得到另外一个三维矢量)如果B=(bij)4x4,那么: ( a1, a2, a3 ) x B = (S>aibi1 + b4,1, S>aibi2 + b4,2, S>aibi3 + b4,3 )>
这就是矢量和矩阵操作公式。从这里开始,代码与数学之间的联系开始清晰。
变换我们已经见过象这样的公式: t( tx, ty ): ( x, y ) ==> ( x + tx, y + ty )
这是在二维笛卡儿坐标系的平移等式。下面是缩放公式:
s( k ): ( x, y ) ==> ( kx, ky )
旋转等式: r( q ): ( x, y ) ==> ( x cos(q) - y sin(q), x sin(q) + y cos(q) ) 以上都是二维公式,在三维里公式的形式仍然很相近。平移公式: t( tx, ty, tz ): ( x, y, z ) ==> ( x + tx, y + ty, z + tz ) 缩放公式: s( k ): ( x, y, z ) ==> ( kx, ky, kz ) 旋转公式(围绕Z轴): r( q ): ( x, y, z ) ==> ( x cos(q) - y sin(q), x sin(q) + y cos(q), z ) 所以我们可以写出像在二维中同样的变换公式。我们通过乘以变换矩阵而得到新的矢量,新矢量将指向变换点。下面是所有三维变换矩阵:
如何建立透视如何建立对象的立体视觉,即显示器上的一些事物看起来离我们很近,而另外一些事物离我们很远。透视问题一直是困绕我们的一个问题。有许多方法被使用。我们使用的3D世界到2D屏幕的投影公式: P( f ):(x, y, z)==>( f*x / z + XOrigin, f*y / z + YOrigin )
Z-缓存 Z-缓存是把显示在屏幕上的每一个点的z坐标保持在一个巨大的数组中,并且当我们我们检查是否它靠近观察者或是否在观察者后面。我们仅仅在第一种情况下绘制它。所以我们不得不计算每一个点的z值。但是首先,我们定义全局树组和为他分配空间。(内存等于追至方向与水平方向的乘积): typedef long ZBUFTYPE; ZBUFTYPE *zbuffer; zbuffer=(ZBUFTYPE *)malloc(sizeof(ZBUFTYPE)*MEMORYSIZE); 我们使用长整形作为z-缓存类型,因为我们要使用定点数。我们必须记住设置每一个z坐标来尽可能得到更快的速度: int c; for(c=0; c
下面是数学公式。如何才能发现z坐标?我们仅仅已经定义的顶点,而不是多边形的每一个点。实际上,我们所需要做的是投影的反变换,投影公式是: u = f ?x / z