好久没玩PSP编程,昨天这学期的图形学结课,昨晚也把最后的作业submit了,于是这学期也就差不多结束了。虽然课时不多,但是图形学课的作业设计得确实不错,首先实现了完整的图形渲染流水线(矩阵变换->相机变换->透视映射变换->三角形光栅化与纹理映射),然后做了一些简单的光线追踪算法(反射、硬阴影与折射)。我打算把这学期在PC上实现的这些算法应用到PSP上面,这样就相当于做一个最简单的3D图形引擎了,以后可以在这个基础上再做扩展。

上次简单研究了下SDK自带的GU函数库,现在不打算用了,既然有了VRAM的指针,那干脆就从最底层开始玩吧!而且,为了提高编程效率,我决定在C的基础上加入C++的代码,不然光是C不支持运算符重载这一点就会让向量和矩阵运算痛苦万分的。另外不得不说的是,PSP编程不能直接使用C函数库,于是连sin、cos这种函数都浮云了,需要自己实现,sigh。

使用C++

因为SDK带了psp-g++编译器,所以问题不大,不过需要加个库文件的链接,里面实现了new和delete的操作符。

LIBS = -lpsplibc

把上次的pspgu删掉吧,以后大概也用不到了。

实现基本数学运算库

目前急需使用的是sin、cos和求平方根的三个函数。sin和cos可以使用Taylor展开式来进行计算,至于平方根,大家应该知道Carmack的那个神奇平方根倒数算法吧,就是用到了谜之常数0x5f3759df的那个算法,我就向卡马克前辈致敬一下吧。

首先是sin(由Ailsa实现,请参考这篇文章[1]):

float sin(float r)
{
    // By Ailsa
    float result = r, temp = r;
    float den = r, fac = 1.0;
    int n = 1, sign = 1;
    while ((temp > 1e-5) || (temp < -1e-5))        
    {
        n++, fac *= n, den *= r;
        n++, fac *= n, den *= r;
        temp = den / fac;
        sign = -sign;
        result = sign > 0 ? result + temp : result - temp;
    }
    return result;
}

然后是cos,为了提高效率直接inline然后返回sin(π/2-r):

inline float cos(float r)
{
    return sin(PI_2 - r);
}

请注意sin和cos的参数都使用弧度而非角度。

最后是平方根的倒数的实现,算法原理请参考这篇文章[2]:

float rsqrt(float a)
{
    // By Carmack
    int i;
    float x2, y;
    const float threehalfs = 1.5;
    char buf[16];

    x2 = a*0.5;
    y = a;

    sprintf(buf,"%x",y);
    i = *(int*)&y;  // evil floating point bit level hacking
    i = 0x5f3759df - (i>>1); // what the fuck?
    sprintf(buf,"%x",i);
    y = *(float*)&i;

    y = y * (threehalfs - (x2 * y * y)); // 1st iteration

    return y;
}

注意到里面的sprintf了吗?之所以使用这个函数是因为如果不把y和i都printf一下的话就会死机……原因不明。

实现3D运算基本类型、函数与操作符

为了进行3D运算,我们需要有三个class,分别是Point、Vector和Matrix,以及相应的运算符(Point+Vector=Point等)和运算函数(点乘、叉乘、矩阵求逆等)。具体实现方式无数的书里都有讲,就不贴出来了。

===========cotaku的分割线===========

下一篇终于要到光栅化三角形了么……进度真得是慢得令人发指啊。

参考资料:
[1] 用C语言实现正弦和余弦
[2] Carmack求平方根梦幻算法

» 转载请注明来源及链接:未来代码研究所

Related Posts:

Leave a Reply

World Line
Time Machine
Friendly Links
Online Tools