0xAA55 发表于 2017-3-17 12:14:42

【算法】在文曲星平台上,用gvmaker实现3D场景渲染!(附:文曲星GVMaker开发环境下载,GVMaker模拟器下载,源码下载)

注:本文所指的“文曲星”是“文曲星A2008”机型。。




    文曲星不支持浮点数运算,而且运行也十分缓慢——其实不是文曲星慢,而是它上面运行的gvmaker虚拟机太慢了。所以我使用定点数来代替浮点数进行运算。

    所谓定点数,就是小数点位置被固定到指定位置的一种数据类型,比如我存储1.0,对于定点数,我把小数点固定在第10位,那么我实际存储的数值就是0x400(十进制1024)。这样的话,我的定点数的精度,最低也是1/1024。

    定点数的加减法和整数无异,直接+ - 就好。但乘除法的话,如果你用一个定点数去乘一个定点数,并且你直接用的是整数的乘法的处理方式的话,你需要把得到的积再除以定点数表示的1.0,你才能得到正确的结果。比如你的定点数1.0用整数方式存储是1024的话,为了得到2.0 * 2.0 = 4.0的效果,你实际做的整数乘法是2048 * 2048,得到的是4194304,你需要再除以1024(也就是你的定点数的1.0),你才能得到4096。同理除法也需要经过这样的处理,只不过除法是先让被除数乘上定点数1.0,再除以除数。比如1.0 / 2.0 = 0.5,用定点数做运算的话,就是1024 * 1024 / 2048 = 512。

    和浮点数做对比,定点数运算比较快,并且它的精度是可控的——因为浮点数的小数点位置是浮动的,它的精度与数值的大小有密切的关系,所以用定点数能保证精度的统一,而浮点数则不能。

    但定点数的缺点在于做乘除法的时候,会严重地损失精度,尤其是圆周率,32位浮点数能存储3.1415927,而定点数假设小数点在第10位,那么它只能用 3216 来表示圆周率(3216 = π×1024 取整)。而如果我把小数点放在第16位的位置上(1.0用65536表示)的话,我虽然能精确到1/65536,但我能存储的最大的数值也从之前的4294966272变成了65536,而且我每次做乘除法的时候,我都要用一个64位的整数来存储中间值,这会严重降低运算的效率(然而x86平台还好)。

    有关3D渲染的算法,其实就是很简单的矩阵变换和投影,其它并没有什么玄妙。顺带一提,很多人以为用了DirectX或者OpenGL才能算真3D,但其实3D渲染的“真伪”只取决于你用的算法,和你使用的媒介无关。事实上就算你用的是正交投影,某种程度上也算是“真3D”的。我之前就写过一篇帖子,讲如何用VB自带的Line进行3D画面的绘制的。

    文曲星运行用户程序,用的是一款叫“gvmaker”的开发环境,和它的启动器。这里放出下载:
gvmaker开发环境:
文曲星端的gvmaker启动器:

源码:(320x240,64K色)
#define FIXED 1024
#define FIXED_SHIFT 10
#define MSTACK_SIZE 16 // 矩阵栈的大小

#define VP_WIDTH 320
#define VP_HEIGHT 240

#define VP_CENTERX 160
#define VP_CENTERY 120

#define VP_RIGHT 319
#define VP_BOTTOM 239

// 触屏判断
struct point
{
        int x;
        int y;
};
struct point pt;
struct point pt_old;

//向量
struct vec3
{
        long x,y,z;
};

struct vec4
{
        long x,y,z,w;
};

// 矩阵
struct mat4
{
        struct vec4 x,y,z,w;
};

// 矩阵栈
struct mat4 m_stack;
int m_stacktop; // 栈顶
struct mat4 m; // 临时矩阵(gvmaker不能把结构体变量作为局部变量定义)
struct mat4 m_modelview; // 模型矩阵
struct mat4 m_projection; // 投影矩阵

struct vec3 eye_pos; // 眼睛位置
long eye_yaw, eye_pitch; // 眼睛角度
struct vec3 eye_xaxis; // 眼睛右方向
struct vec3 eye_yaxis; // 眼睛上方向
struct vec3 eye_zaxis; // 眼睛前方向

long time_global; // 全局时间
long time_delta; // 帧时间

void mat_init();
void mat_push();
void mat_pop();
void mat_mult();
void mat_transpose();
void mat_load_identity();
void mat_load_RotXAxis(long deg);
void mat_load_RotYAxis(long deg);
void mat_load_RotZAxis(long deg);
void mat_load_RotAxis(long ax, long ay, long az, long deg);
void mat_load_EulerRotation(long yaw, long pitch, long roll);
void mat_load_view_lh(long x, long y, long z, long yaw, long pitch, long roll);
void mat_load_perspective_lh(long fovy, long aspect, long zn, long zf);
void mat_load_modelview();
void mat_load_projection();
void mat_set_modelview();
void mat_set_projection();
void mat_print(int x, int y);

long tan(long deg)
{
        return (sin(deg) << FIXED_SHIFT) / cos(deg);
}

long cot(long deg)
{
        return (cos(deg) << FIXED_SHIFT) / sin(deg);
}

// 初始化矩阵栈
void mat_init()
{
        m_stacktop = 0;
        mat_load_identity();
}

// 压入新矩阵
void mat_push()
{
        m_stacktop = (m_stacktop + 1) % MSTACK_SIZE;
}

// 压入栈顶
void mat_pushtop()
{
        int prev;
        prev = m_stacktop;
        m_stacktop = (m_stacktop + 1) % MSTACK_SIZE;
        memcpy(&m_stack.x.x, &m_stack.x.x, 64);
}

// 弹出矩阵
void mat_pop()
{
        m_stacktop = m_stacktop - 1;
        if(m_stacktop < 0)
                m_stacktop = m_stacktop + MSTACK_SIZE;
}

// 将栈顶两个矩阵做矩阵乘法
void mat_mult()
{
        long ptr2;
        ptr2 = m_stacktop;
        mat_pop();

        m.x.x = (m_stack.x.x * m_stack.x.x >> FIXED_SHIFT) + (m_stack.x.y * m_stack.y.x >> FIXED_SHIFT) + (m_stack.x.z * m_stack.z.x >> FIXED_SHIFT) + (m_stack.x.w * m_stack.w.x >> FIXED_SHIFT);
        m.x.y = (m_stack.x.x * m_stack.x.y >> FIXED_SHIFT) + (m_stack.x.y * m_stack.y.y >> FIXED_SHIFT) + (m_stack.x.z * m_stack.z.y >> FIXED_SHIFT) + (m_stack.x.w * m_stack.w.y >> FIXED_SHIFT);
        m.x.z = (m_stack.x.x * m_stack.x.z >> FIXED_SHIFT) + (m_stack.x.y * m_stack.y.z >> FIXED_SHIFT) + (m_stack.x.z * m_stack.z.z >> FIXED_SHIFT) + (m_stack.x.w * m_stack.w.z >> FIXED_SHIFT);
        m.x.w = (m_stack.x.x * m_stack.x.w >> FIXED_SHIFT) + (m_stack.x.y * m_stack.y.w >> FIXED_SHIFT) + (m_stack.x.z * m_stack.z.w >> FIXED_SHIFT) + (m_stack.x.w * m_stack.w.w >> FIXED_SHIFT);

        m.y.x = (m_stack.y.x * m_stack.x.x >> FIXED_SHIFT) + (m_stack.y.y * m_stack.y.x >> FIXED_SHIFT) + (m_stack.y.z * m_stack.z.x >> FIXED_SHIFT) + (m_stack.y.w * m_stack.w.x >> FIXED_SHIFT);
        m.y.y = (m_stack.y.x * m_stack.x.y >> FIXED_SHIFT) + (m_stack.y.y * m_stack.y.y >> FIXED_SHIFT) + (m_stack.y.z * m_stack.z.y >> FIXED_SHIFT) + (m_stack.y.w * m_stack.w.y >> FIXED_SHIFT);
        m.y.z = (m_stack.y.x * m_stack.x.z >> FIXED_SHIFT) + (m_stack.y.y * m_stack.y.z >> FIXED_SHIFT) + (m_stack.y.z * m_stack.z.z >> FIXED_SHIFT) + (m_stack.y.w * m_stack.w.z >> FIXED_SHIFT);
        m.y.w = (m_stack.y.x * m_stack.x.w >> FIXED_SHIFT) + (m_stack.y.y * m_stack.y.w >> FIXED_SHIFT) + (m_stack.y.z * m_stack.z.w >> FIXED_SHIFT) + (m_stack.y.w * m_stack.w.w >> FIXED_SHIFT);

        m.z.x = (m_stack.z.x * m_stack.x.x >> FIXED_SHIFT) + (m_stack.z.y * m_stack.y.x >> FIXED_SHIFT) + (m_stack.z.z * m_stack.z.x >> FIXED_SHIFT) + (m_stack.z.w * m_stack.w.x >> FIXED_SHIFT);
        m.z.y = (m_stack.z.x * m_stack.x.y >> FIXED_SHIFT) + (m_stack.z.y * m_stack.y.y >> FIXED_SHIFT) + (m_stack.z.z * m_stack.z.y >> FIXED_SHIFT) + (m_stack.z.w * m_stack.w.y >> FIXED_SHIFT);
        m.z.z = (m_stack.z.x * m_stack.x.z >> FIXED_SHIFT) + (m_stack.z.y * m_stack.y.z >> FIXED_SHIFT) + (m_stack.z.z * m_stack.z.z >> FIXED_SHIFT) + (m_stack.z.w * m_stack.w.z >> FIXED_SHIFT);
        m.z.w = (m_stack.z.x * m_stack.x.w >> FIXED_SHIFT) + (m_stack.z.y * m_stack.y.w >> FIXED_SHIFT) + (m_stack.z.z * m_stack.z.w >> FIXED_SHIFT) + (m_stack.z.w * m_stack.w.w >> FIXED_SHIFT);

        m.w.x = (m_stack.w.x * m_stack.x.x >> FIXED_SHIFT) + (m_stack.w.y * m_stack.y.x >> FIXED_SHIFT) + (m_stack.w.z * m_stack.z.x >> FIXED_SHIFT) + (m_stack.w.w * m_stack.w.x >> FIXED_SHIFT);
        m.w.y = (m_stack.w.x * m_stack.x.y >> FIXED_SHIFT) + (m_stack.w.y * m_stack.y.y >> FIXED_SHIFT) + (m_stack.w.z * m_stack.z.y >> FIXED_SHIFT) + (m_stack.w.w * m_stack.w.y >> FIXED_SHIFT);
        m.w.z = (m_stack.w.x * m_stack.x.z >> FIXED_SHIFT) + (m_stack.w.y * m_stack.y.z >> FIXED_SHIFT) + (m_stack.w.z * m_stack.z.z >> FIXED_SHIFT) + (m_stack.w.w * m_stack.w.z >> FIXED_SHIFT);
        m.w.w = (m_stack.w.x * m_stack.x.w >> FIXED_SHIFT) + (m_stack.w.y * m_stack.y.w >> FIXED_SHIFT) + (m_stack.w.z * m_stack.z.w >> FIXED_SHIFT) + (m_stack.w.w * m_stack.w.w >> FIXED_SHIFT);
       
        memcpy(&m_stack.x.x, &m.x.x, 64);
}

// 矩阵转置
void mat_transpose()
{
        long t;
       
        t = m_stack.x.y;
        m_stack.x.y = m_stack.y.x;
        m_stack.y.x = t;
       
        t = m_stack.x.z;
        m_stack.x.z = m_stack.z.x;
        m_stack.z.x = t;
       
        t = m_stack.x.w;
        m_stack.x.w = m_stack.w.x;
        m_stack.w.x = t;
       
        t = m_stack.y.z;
        m_stack.y.z = m_stack.z.y;
        m_stack.z.y = t;
       
        t = m_stack.y.w;
        m_stack.y.w = m_stack.w.y;
        m_stack.w.y = t;
       
        t = m_stack.z.w;
        m_stack.z.w = m_stack.w.z;
        m_stack.w.z = t;
}

// 载入单位矩阵
void mat_load_identity()
{
        m_stack.x.x=FIXED;m_stack.x.y=0;m_stack.x.z=0;m_stack.x.w=0;
        m_stack.y.y=FIXED;m_stack.y.x=0;m_stack.y.z=0;m_stack.y.w=0;
        m_stack.z.z=FIXED;m_stack.z.x=0;m_stack.z.y=0;m_stack.z.w=0;
        m_stack.w.w=FIXED;m_stack.w.x=0;m_stack.w.y=0;m_stack.w.z=0;
}

// 载入平移矩阵
void mat_load_translate(long x, long y, long z)
{
        m_stack.x.x=FIXED;m_stack.x.y=0;m_stack.x.z=0;m_stack.x.w=0;
        m_stack.y.y=FIXED;m_stack.y.x=0;m_stack.y.z=0;m_stack.y.w=0;
        m_stack.z.z=FIXED;m_stack.z.x=0;m_stack.z.y=0;m_stack.z.w=0;
        m_stack.w.w=FIXED;m_stack.w.x=x;m_stack.w.y=y;m_stack.w.z=z;
}

// 载入绕X轴旋转矩阵
void mat_load_RotXAxis(long deg)
{
        m_stack.x.x = FIXED;
        m_stack.x.y = 0;
        m_stack.x.z = 0;
        m_stack.x.w = 0;
       
        m_stack.y.x = 0;
        m_stack.y.y = cos(deg);
        m_stack.y.z =-sin(deg);
        m_stack.y.w = 0;
       
        m_stack.z.x = 0;
        m_stack.z.y = sin(deg);
        m_stack.z.z = cos(deg);
        m_stack.z.w = 0;
       
        m_stack.w.x = 0;
        m_stack.w.y = 0;
        m_stack.w.z = 0;
        m_stack.w.w = FIXED;
}

// 载入绕Y轴旋转矩阵
void mat_load_RotYAxis(long deg)
{
        m_stack.x.x = cos(deg);
        m_stack.x.y = 0;
        m_stack.x.z = sin(deg);
        m_stack.x.w = 0;
       
        m_stack.y.x = 0;
        m_stack.y.y = FIXED;
        m_stack.y.z = 0;
        m_stack.y.w = 0;
       
        m_stack.z.x =-sin(deg);
        m_stack.z.y = 0;
        m_stack.z.z = cos(deg);
        m_stack.z.w = 0;
       
        m_stack.w.x = 0;
        m_stack.w.y = 0;
        m_stack.w.z = 0;
        m_stack.w.w = FIXED;
}

// 载入绕Z轴旋转矩阵
void mat_load_RotZAxis(long deg)
{
        m_stack.x.x = cos(deg);
        m_stack.x.y = sin(deg);
        m_stack.x.z = 0;
        m_stack.x.w = 0;
       
        m_stack.y.x =-sin(deg);
        m_stack.y.y = cos(deg);
        m_stack.y.z = 0;
        m_stack.y.w = 0;
       
        m_stack.z.x = 0;
        m_stack.z.y = 0;
        m_stack.z.z = FIXED;
        m_stack.z.w = 0;
       
        m_stack.w.x = 0;
        m_stack.w.y = 0;
        m_stack.w.z = 0;
        m_stack.w.w = FIXED;
}

// 载入绕轴旋转矩阵
void mat_load_RotAxis(long ax, long ay, long az, long deg)
{
        m_stack.x.x = (((FIXED - cos(deg)) * ax >> FIXED_SHIFT) * ax >> FIXED_SHIFT) + cos(deg);
        m_stack.x.y = (((FIXED - cos(deg)) * ax >> FIXED_SHIFT) * ay >> FIXED_SHIFT) - sin(deg) * az >> FIXED_SHIFT;
        m_stack.x.z = (((FIXED - cos(deg)) * ax >> FIXED_SHIFT) * az >> FIXED_SHIFT) + sin(deg) * ay >> FIXED_SHIFT;
        m_stack.x.w = 0;
                                  
        m_stack.y.x = (((FIXED - cos(deg)) * ay >> FIXED_SHIFT) * ax >> FIXED_SHIFT) + sin(deg) * az >> FIXED_SHIFT;
        m_stack.y.y = (((FIXED - cos(deg)) * ay >> FIXED_SHIFT) * ay >> FIXED_SHIFT) + cos(deg);
        m_stack.y.z = (((FIXED - cos(deg)) * ay >> FIXED_SHIFT) * az >> FIXED_SHIFT) - sin(deg) * ax >> FIXED_SHIFT;
        m_stack.y.w = 0;
                                  
        m_stack.z.x = (((FIXED - cos(deg)) * az >> FIXED_SHIFT) * ax >> FIXED_SHIFT) - sin(deg) * ay >> FIXED_SHIFT;
        m_stack.z.y = (((FIXED - cos(deg)) * az >> FIXED_SHIFT) * ay >> FIXED_SHIFT) + sin(deg) * ax >> FIXED_SHIFT;
        m_stack.z.z = (((FIXED - cos(deg)) * az >> FIXED_SHIFT) * az >> FIXED_SHIFT) + cos(deg);
        m_stack.z.w = 0;
       
        m_stack.w.x = 0;
        m_stack.w.y = 0;
        m_stack.w.z = 0;
        m_stack.w.w = FIXED;
}

// 载入欧拉角旋转矩阵
void mat_load_EulerRotation(long yaw, long pitch, long roll)
{
        mat_load_RotZAxis(roll);
       
        mat_push();
        mat_load_RotXAxis(pitch);
       
        mat_mult();
       
        mat_push();
        mat_load_RotYAxis(yaw);
       
        mat_mult();
}

// 载入左手坐标系观察矩阵
void mat_load_view_lh(long x, long y, long z, long yaw, long pitch, long roll)
{
        long t;
        mat_load_EulerRotation(yaw, pitch, roll);
       
        eye_xaxis.x = m_stack.x.x;
        eye_xaxis.y = m_stack.x.y;
        eye_xaxis.z = m_stack.x.z;
        eye_yaxis.x = m_stack.y.x;
        eye_yaxis.y = m_stack.y.y;
        eye_yaxis.z = m_stack.y.z;
        eye_zaxis.x = m_stack.z.x;
        eye_zaxis.y = m_stack.z.y;
        eye_zaxis.z = m_stack.z.z;
       
        m_stack.w.x = -((eye_xaxis.x * x >> FIXED_SHIFT) + (eye_xaxis.y * y >> FIXED_SHIFT) + (eye_xaxis.z * z >> FIXED_SHIFT));
        m_stack.w.y = -((eye_yaxis.x * x >> FIXED_SHIFT) + (eye_yaxis.y * y >> FIXED_SHIFT) + (eye_yaxis.z * z >> FIXED_SHIFT));
        m_stack.w.z = -((eye_zaxis.x * x >> FIXED_SHIFT) + (eye_zaxis.y * y >> FIXED_SHIFT) + (eye_zaxis.z * z >> FIXED_SHIFT));
        m_stack.w.w = FIXED;
       
        t = m_stack.x.y;
        m_stack.x.y = m_stack.y.x;
        m_stack.y.x = t;
       
        t = m_stack.x.z;
        m_stack.x.z = m_stack.z.x;
        m_stack.z.x = t;
       
        t = m_stack.y.z;
        m_stack.y.z = m_stack.z.y;
        m_stack.z.y = t;
       
        m_stack.x.w = 0;
        m_stack.y.w = 0;
        m_stack.z.w = 0;
}

// 载入左手坐标系投影矩阵
void mat_load_perspective_lh(long fovy, long aspect, long zn, long zf)
{
        long xscale,yscale;
        long zdiff;
        zdiff = zf-zn;
        yscale = cot(fovy / 2);
        xscale = (yscale << FIXED_SHIFT) / aspect;
        m_stack.x.x = xscale; m_stack.x.y = 0; m_stack.x.z = 0; m_stack.x.w = 0;
        m_stack.y.x = 0; m_stack.y.y = yscale; m_stack.y.z = 0; m_stack.y.w = 0;
        m_stack.z.x = 0; m_stack.z.y = 0; m_stack.z.z = (zf<<FIXED_SHIFT)/zdiff; m_stack.z.w = FIXED;
        m_stack.w.x = 0; m_stack.w.y = 0; m_stack.w.z = -zn*zf/zdiff; m_stack.w.w = 0;
}

// 将模型矩阵复制到栈顶
void mat_load_modelview()
{
        memcpy(&m_stack.x.x, &m_modelview.x.x, 64);
}

// 将投影矩阵复制到栈顶
void mat_load_projection()
{
        memcpy(&m_stack.x.x, &m_projection.x.x, 64);
}

// 将栈顶的矩阵复制到模型矩阵
void mat_set_modelview()
{
        memcpy(&m_modelview.x.x, &m_stack.x.x, 64);
}

// 将栈顶的矩阵复制到投影矩阵
void mat_set_projection()
{
        memcpy(&m_projection.x.x, &m_stack.x.x, 64);
}

// 打印矩阵的值到屏幕上
void mat_print(int x, int y)
{
        char buf;
        sprintf(buf, "%d:",m_stacktop);
        TextOut(x,y,buf);y=y+GetFontSize();
        sprintf(buf, "%d,%d,%d,%d", m_stack.x.x,m_stack.x.y,m_stack.x.z,m_stack.x.w);
        TextOut(x,y,buf);y=y+GetFontSize();
        sprintf(buf, "%d,%d,%d,%d", m_stack.y.x,m_stack.y.y,m_stack.y.z,m_stack.y.w);
        TextOut(x,y,buf);y=y+GetFontSize();
        sprintf(buf, "%d,%d,%d,%d", m_stack.z.x,m_stack.z.y,m_stack.z.z,m_stack.z.w);
        TextOut(x,y,buf);y=y+GetFontSize();
        sprintf(buf, "%d,%d,%d,%d", m_stack.w.x,m_stack.w.y,m_stack.w.z,m_stack.w.w);
        TextOut(x,y,buf);
}

// 线性插值
long m_lerp(long v1, long v2, long s)
{
        return v1 + ((v2 - v1) * s >> FIXED_SHIFT);
}

// 画线
void g_draw_line(long x1, long y1, long x2, long y2)
{
        // gvmaker原版的画线函数不能处理坐标超出屏幕外的情况
        // 所以要做一个插值处理
        if(x1 < 0)
        {
                if(x2 < 0)
                        return; // 防止除零异常
                y1 = y1 + (y2 - y1) *-x1 / (x2 - x1);
                x1 = 0;
        }
        if(y1 < 0)
        {
                if(y2 < 0)
                        return;
                x1 = x1 + (x2 - x1) *-y1 / (y2 - y1);
                y1 = 0;
        }
        if(x2 < 0)
        {
                if(x1 <= 0)
                        return;
                y2 = y2 + (y1 - y2) *-x2 / (x1 - x2);
                x2 = 0;
        }
        if(y2 < 0)
        {
                if(y1 <= 0)
                        return;
                x2 = x2 + (x1 - x2) *-y2 / (y1 - y2);
                y2 = 0;
        }
        if(x1 > VP_RIGHT)
        {
                if(x2 > VP_RIGHT)
                        return;
                y1 = y1 + (y2 - y1) * (x1 - VP_RIGHT) / (x1 - x2);
                x1 = VP_RIGHT;
        }
        if(y1 > VP_BOTTOM)
        {
                if(y2 > VP_BOTTOM)
                        return;
                x1 = x1 + (x2 - x1) * (y1 - VP_BOTTOM) / (y1 - y2);
                y1 = VP_BOTTOM;
        }
        if(x2 > VP_RIGHT)
        {
                if(x1 >= VP_RIGHT)
                        return;
                y2 = y2 + (y1 - y2) * (x2 - VP_RIGHT) / (x2 - x1);
                x2 = VP_RIGHT;
        }
        if(y2 > VP_BOTTOM)
        {
                if(y1 >= VP_BOTTOM)
                        return;
                x2 = x2 + (x1 - x2) * (y2 - VP_BOTTOM) / (y2 - y1);
                y2 = VP_BOTTOM;
        }
        if(x1 == x2)
                VLine(x1, y1, y2);
        else if(y1 == y2)
                HLine(x1, x2, y1);
        else
                Line(x1,y1,x2,y2);
}

// 画3D线,坐标将经过矩阵变换和投影处理。
void g_draw_3d_line(long x1, long y1, long z1, long x2, long y2, long z2)
{
        long pxt, pyt, pzt, pwt; // 临时坐标
        long px1, py1, pz1, pw1; // 坐标1
        long px2, py2, pz2, pw2; // 坐标2
        long ls;
       
        pxt = (x1 * m_modelview.x.x >> FIXED_SHIFT) + (y1 * m_modelview.y.x >> FIXED_SHIFT) + (z1 * m_modelview.z.x >> FIXED_SHIFT) + m_modelview.w.x;
        pyt = (x1 * m_modelview.x.y >> FIXED_SHIFT) + (y1 * m_modelview.y.y >> FIXED_SHIFT) + (z1 * m_modelview.z.y >> FIXED_SHIFT) + m_modelview.w.y;
        pzt = (x1 * m_modelview.x.z >> FIXED_SHIFT) + (y1 * m_modelview.y.z >> FIXED_SHIFT) + (z1 * m_modelview.z.z >> FIXED_SHIFT) + m_modelview.w.z;
        pwt = (x1 * m_modelview.x.w >> FIXED_SHIFT) + (y1 * m_modelview.y.w >> FIXED_SHIFT) + (z1 * m_modelview.z.w >> FIXED_SHIFT) + m_modelview.w.w;
       
        px1 = (pxt * m_projection.x.x >> FIXED_SHIFT) + (pyt * m_projection.y.x >> FIXED_SHIFT) + (pzt * m_projection.z.x >> FIXED_SHIFT) + (pwt * m_projection.w.x >> FIXED_SHIFT);
        py1 = (pxt * m_projection.x.y >> FIXED_SHIFT) + (pyt * m_projection.y.y >> FIXED_SHIFT) + (pzt * m_projection.z.y >> FIXED_SHIFT) + (pwt * m_projection.w.y >> FIXED_SHIFT);
        pz1 = (pxt * m_projection.x.z >> FIXED_SHIFT) + (pyt * m_projection.y.z >> FIXED_SHIFT) + (pzt * m_projection.z.z >> FIXED_SHIFT) + (pwt * m_projection.w.z >> FIXED_SHIFT);
        pw1 = (pxt * m_projection.x.w >> FIXED_SHIFT) + (pyt * m_projection.y.w >> FIXED_SHIFT) + (pzt * m_projection.z.w >> FIXED_SHIFT) + (pwt * m_projection.w.w >> FIXED_SHIFT);
       
        pxt = (x2 * m_modelview.x.x >> FIXED_SHIFT) + (y2 * m_modelview.y.x >> FIXED_SHIFT) + (z2 * m_modelview.z.x >> FIXED_SHIFT) + m_modelview.w.x;
        pyt = (x2 * m_modelview.x.y >> FIXED_SHIFT) + (y2 * m_modelview.y.y >> FIXED_SHIFT) + (z2 * m_modelview.z.y >> FIXED_SHIFT) + m_modelview.w.y;
        pzt = (x2 * m_modelview.x.z >> FIXED_SHIFT) + (y2 * m_modelview.y.z >> FIXED_SHIFT) + (z2 * m_modelview.z.z >> FIXED_SHIFT) + m_modelview.w.z;
        pwt = (x2 * m_modelview.x.w >> FIXED_SHIFT) + (y2 * m_modelview.y.w >> FIXED_SHIFT) + (z2 * m_modelview.z.w >> FIXED_SHIFT) + m_modelview.w.w;
       
        px2 = (pxt * m_projection.x.x >> FIXED_SHIFT) + (pyt * m_projection.y.x >> FIXED_SHIFT) + (pzt * m_projection.z.x >> FIXED_SHIFT) + (pwt * m_projection.w.x >> FIXED_SHIFT);
        py2 = (pxt * m_projection.x.y >> FIXED_SHIFT) + (pyt * m_projection.y.y >> FIXED_SHIFT) + (pzt * m_projection.z.y >> FIXED_SHIFT) + (pwt * m_projection.w.y >> FIXED_SHIFT);
        pz2 = (pxt * m_projection.x.z >> FIXED_SHIFT) + (pyt * m_projection.y.z >> FIXED_SHIFT) + (pzt * m_projection.z.z >> FIXED_SHIFT) + (pwt * m_projection.w.z >> FIXED_SHIFT);
        pw2 = (pxt * m_projection.x.w >> FIXED_SHIFT) + (pyt * m_projection.y.w >> FIXED_SHIFT) + (pzt * m_projection.z.w >> FIXED_SHIFT) + (pwt * m_projection.w.w >> FIXED_SHIFT);
       
        // 处理近视距剪切
        if(pz1 < FIXED)
        {
                if(pz2 < FIXED)
                        return;
               
                if(pz1 == pz2)
                        return;
               
                ls = ((pz2 - FIXED) << FIXED_SHIFT) / (pz2 - pz1);
               
                px1 = m_lerp(px2, px1, ls);
                py1 = m_lerp(py2, py1, ls);
                pz1 = m_lerp(pz2, pz1, ls);
                pw1 = m_lerp(pw2, pw1, ls);
        }
       
        if(pz2 < FIXED)
        {
                if(pz1 < FIXED)
                        return;
               
                if(pz1 == pz2)
                        return;
               
                ls = ((pz1 - FIXED) << FIXED_SHIFT) / (pz1 - pz2);
               
                px2 = m_lerp(px1, px2, ls);
                py2 = m_lerp(py1, py2, ls);
                pz2 = m_lerp(pz1, pz2, ls);
                pw2 = m_lerp(pw1, pw2, ls);
        }
       
        // 投影
        px1 = px1 * VP_CENTERX / pw1 + VP_CENTERX;
        py1 = VP_CENTERY - py1 * VP_CENTERY / pw1;
        px2 = px2 * VP_CENTERX / pw2 + VP_CENTERX;
        py2 = VP_CENTERY - py2 * VP_CENTERY / pw2;
        g_draw_line(px1,py1,px2,py2);
}

// 用线条画长方体
void g_draw_cube(long dx, long dy, long dz)
{
        // Up
        g_draw_3d_line(-dx, dy, dz, dx, dy, dz);
        g_draw_3d_line( dx, dy, dz, dx, dy,-dz);
        g_draw_3d_line( dx, dy,-dz,-dx, dy,-dz);
        g_draw_3d_line(-dx, dy,-dz,-dx, dy, dz);
       
        // Down
        g_draw_3d_line(-dx,-dy, dz, dx,-dy, dz);
        g_draw_3d_line( dx,-dy, dz, dx,-dy,-dz);
        g_draw_3d_line( dx,-dy,-dz,-dx,-dy,-dz);
        g_draw_3d_line(-dx,-dy,-dz,-dx,-dy, dz);
       
        // Pillars
        g_draw_3d_line(-dx, dy, dz,-dx,-dy, dz);
        g_draw_3d_line( dx, dy, dz, dx,-dy, dz);
        g_draw_3d_line( dx, dy,-dz, dx,-dy,-dz);
        g_draw_3d_line(-dx, dy,-dz,-dx,-dy,-dz);
}

// 输出16进制数
void XDigitOut(int x, int y, long digit)
{
        int ch;
        int w;
        int shifts;
       
        w = GetFontSize() / 2;
       
        if(!digit)
        {
                TextOut(x, y, "00000000");
                return;
        }
       
        x=x+w*7;
        shifts=32;
        while(shifts)
        {
                ch=digit&0xF;
                if(ch>=10)
                        CharOut(x, y, ch-10+'A');
                else
                        CharOut(x, y, ch+'0');
                x=x-w;
                digit=digit>>4;
                shifts=shifts-4;
        }
}

void main()
{
        long i;
        int x,y;
       
        SetFontSize(12);
        SetLCDMode(0);
       
        eye_pos.x=0;
        eye_pos.y=3 << FIXED_SHIFT;
        eye_pos.z=-10 << FIXED_SHIFT;
       
        mat_init();
       
        time_global = GetTickCount();
       
        for(;;)
        {
                // 帧时间处理
                i = GetTickCount();
                time_delta = i - time_global;
                time_global = i;
               
                ClearScreen();
               
                // 设置投影矩阵
                mat_load_perspective_lh(60, (VP_WIDTH << FIXED_SHIFT) / VP_HEIGHT, FIXED, 1000 << FIXED_SHIFT);
                mat_set_projection();
               
                // 设置旋转立方体的矩阵
                mat_load_RotYAxis(time_global * 60 / 1000);
                mat_push();
                mat_load_translate(0,FIXED,0);
                mat_mult();
                mat_push();
                mat_load_view_lh(eye_pos.x, eye_pos.y, eye_pos.z, eye_yaw, eye_pitch, 0);
                mat_mult();
                mat_set_modelview();
               
                // 画立方体
                g_draw_cube(FIXED, FIXED, FIXED);
               
                // 设置地面的矩阵
                mat_load_view_lh(eye_pos.x, eye_pos.y, eye_pos.z, eye_yaw, eye_pitch, 0);
                mat_set_modelview();
               
                // 画地面
                for(x = -10; x <= 10; x++)
                {
                        g_draw_3d_line( x << FIXED_SHIFT, 0, 10 << FIXED_SHIFT, x << FIXED_SHIFT, 0, -10 << FIXED_SHIFT);
                }
                for(y = -10; y <= 10; y++)
                {
                        g_draw_3d_line( 10 << FIXED_SHIFT, 0, y << FIXED_SHIFT,-10 << FIXED_SHIFT, 0, y << FIXED_SHIFT);
                }
               
                // 根据触屏操作来转动镜头
                if(GetMousePos(pt) == 2)
                {
                        eye_yaw = eye_yaw + pt_old.x - pt.x;
                        eye_pitch = eye_pitch + pt_old.y - pt.y;
                        if(eye_yaw < 0)
                                eye_yaw = eye_yaw + 360;
                        if(eye_yaw > 360)
                                eye_yaw = eye_yaw - 360;
                        if(eye_pitch < 0)
                                eye_pitch = eye_pitch + 360;
                        if(eye_pitch > 360)
                                eye_pitch = eye_pitch - 360;
                }
                pt_old.x = pt.x;
                pt_old.y = pt.y;
               
                // 键盘移动操作
                if(CheckKey(KEY_LEFT))
                {
                        eye_pos.x = eye_pos.x - eye_xaxis.x * time_delta * 10 / 1000;
                        eye_pos.y = eye_pos.y - eye_xaxis.y * time_delta * 10 / 1000;
                        eye_pos.z = eye_pos.z - eye_xaxis.z * time_delta * 10 / 1000;
                }
                if(CheckKey(KEY_RIGHT))
                {
                        eye_pos.x = eye_pos.x + eye_xaxis.x * time_delta * 10 / 1000;
                        eye_pos.y = eye_pos.y + eye_xaxis.y * time_delta * 10 / 1000;
                        eye_pos.z = eye_pos.z + eye_xaxis.z * time_delta * 10 / 1000;
                }
                if(CheckKey(KEY_UP))
                {
                        eye_pos.x = eye_pos.x + eye_zaxis.x * time_delta * 10 / 1000;
                        eye_pos.y = eye_pos.y + eye_zaxis.y * time_delta * 10 / 1000;
                        eye_pos.z = eye_pos.z + eye_zaxis.z * time_delta * 10 / 1000;
                }
                if(CheckKey(KEY_DOWN))
                {
                        eye_pos.x = eye_pos.x - eye_zaxis.x * time_delta * 10 / 1000;
                        eye_pos.y = eye_pos.y - eye_zaxis.y * time_delta * 10 / 1000;
                        eye_pos.z = eye_pos.z - eye_zaxis.z * time_delta * 10 / 1000;
                }
               
                Refresh();
        }
}Src:
Bin:

Ayala 发表于 2017-3-17 18:07:24

本帖最后由 Ayala 于 2017-3-17 18:28 编辑

    但定点数的缺点在于做乘除法的时候,会严重地损失精度,尤其是圆周率,32位浮点数能存储3.1415927,而定点数假设小数点在第10位,那么它只能用 3216 来表示圆周率(3216 = π×1024 取整)。而如果我把小数点放在第16位的位置上(1.0用65536表示)的话,我虽然能精确到1/65536,但我能存储的最大的数值也从之前的4294966272变成了65536,而且我每次做乘除法的时候,我都要用一个64位的整数来存储中间值,这会严重降低运算的效率(然而x86平台还好)
这个问题其实可以用1024* PI*2**8 (定点数 π×1024 * 256 =0xC90FE)来解决 当然也可以用 1024*PI*2**15更高数值
另外PI*1024 取整也应该是3216.990877≈3217 而不是 3216

0xAA55 发表于 2017-3-17 20:06:54

Ayala 发表于 2017-3-17 18:07
这个问题其实可以用1024* PI*2**8 (定点数 π×1024 * 256 =0xC90FE)来解决 当然也可以用 1024*PI*2**15更 ...

其实是说错,不是取整,而是“商”,所以是没有四舍五入的

0xAA55 发表于 2017-9-15 17:52:06

另外因为文曲星的解释器特征,优化方面侧重于多调用它的函数进行绘图,少进行自己的绘图方式。比如它的画线函数,其实虽然明明可以自己写一个不使用乘除法的画线函数,但依然还是推荐使用它自己的画线函数(VLine、HLine、Line)。因为自己写的画线函数在运行的时候需要GVMaker虚拟机对其进行指令层面的判断和模拟执行的操作,而它自己的画线函数则是用文曲星本机的处理器指令执行的。
页: [1]
查看完整版本: 【算法】在文曲星平台上,用gvmaker实现3D场景渲染!(附:文曲星GVMaker开发环境下载,GVMaker模拟器下载,源码下载)