好久没玩PSP编程,昨天这学期的图形学结课,昨晚也把最后的作业submit了,于是这学期也就差不多结束了。虽然课时不多,但是图形学课的作业设计得确实不错,首先实现了完整的图形渲染流水线(矩阵变换->相机变换->透视映射变换->三角形光栅化与纹理映射),然后做了一些简单的光线追踪算法(反射、硬阴影与折射)。我打算把这学期在PC上实现的这些算法应用到PSP上面,这样就相当于做一个最简单的3D图形引擎了,以后可以在这个基础上再做扩展。
上次简单研究了下SDK自带的GU函数库,现在不打算用了,既然有了VRAM的指针,那干脆就从最底层开始玩吧!而且,为了提高编程效率,我决定在C的基础上加入C++的代码,不然光是C不支持运算符重载这一点就会让向量和矩阵运算痛苦万分的。另外不得不说的是,PSP编程不能直接使用C函数库,于是连sin、cos这种函数都浮云了,需要自己实现,sigh。
使用C++
因为SDK带了psp-g++编译器,所以问题不大,不过需要加个库文件的链接,里面实现了new和delete的操作符。
把上次的pspgu删掉吧,以后大概也用不到了。
实现基本数学运算库
目前急需使用的是sin、cos和求平方根的三个函数。sin和cos可以使用Taylor展开式来进行计算,至于平方根,大家应该知道Carmack的那个神奇平方根倒数算法吧,就是用到了谜之常数0x5f3759df的那个算法,我就向卡马克前辈致敬一下吧。
首先是sin(由Ailsa实现,请参考这篇文章[1]):
{
// 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):
{
return sin(PI_2 - r);
}
请注意sin和cos的参数都使用弧度而非角度。
最后是平方根的倒数的实现,算法原理请参考这篇文章[2]:
{
// 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求平方根梦幻算法
» 转载请注明来源及链接:未来代码研究所