Archive for the ‘PSP编程’ Category
裁剪(clipping)和背面剔除(back-face culling)夹在投影变换和光栅化两步中间,主要负责剔除超出屏幕坐标系的顶点和背向摄像机的三角形,减小渲染压力。裁剪还有另一个重要用途就是剪掉投影变换前z坐标小于0的点,以免出现诡异的结果以及除零错误。
视变换(viewing transform)和投影变换(perspective transform)是3D渲染管线中不可缺少的两个重要变换,前者将顶点坐标由世界坐标系变换到观察坐标系(又称相机坐标系),后者将顶点坐标由观察坐标系变换到屏幕坐标系,之后就是光栅化的步骤了。
关于这两种变换的原理就不多讲,随便找一本图形学的书上面都会有。具体实现也很简单,就是根据参数生成两个矩阵,然后和顶点向量相乘就行了。这里均采用列向量(即P = M2 * M1 * P0,变换顺序是先M1后M2)和右手坐标系。
做着做着发现需要有一个类似于C++ STL里面的vector那样的数据结构,但是本人比较懒,不想去搬STL的源代码重新编译了(估计又会一大堆error……),于是就自己写了个轻量级的vector。
三角形光栅化是传统图形渲染管线的最后一步,也是最重要的一步,需要插值计算出三角形内部每个像素的颜色值,并根据z值进行深度测试以决定像素的远近关系。
光栅化的算法用掉了我好几天的时间,一开始是用Barycentric Coordinates算法进行的,结果帧率只有杯具的3,想办法把很多的乘除法改成了加减法还是没有起色,看来浮点数运算还是坑爹啊,于是决定用经典的Bresenham算法了。但是Bresenham算法是用来画线的,对于光栅化三角形来说还要考虑三角形内部针对顶点颜色的插值,于是我将其变成了Bresenham插值算法……
好久没玩PSP编程,昨天这学期的图形学结课,昨晚也把最后的作业submit了,于是这学期也就差不多结束了。虽然课时不多,但是图形学课的作业设计得确实不错,首先实现了完整的图形渲染流水线(矩阵变换->相机变换->透视映射变换->三角形光栅化与纹理映射),然后做了一些简单的光线追踪算法(反射、硬阴影与折射)。我打算把这学期在PC上实现的这些算法应用到PSP上面,这样就相当于做一个最简单的3D图形引擎了,以后可以在这个基础上再做扩展。
上次简单研究了下SDK自带的GU函数库,现在不打算用了,既然有了VRAM的指针,那干脆就从最底层开始玩吧!而且,为了提高编程效率,我决定在C的基础上加入C++的代码,不然光是C不支持运算符重载这一点就会让向量和矩阵运算痛苦万分的。另外不得不说的是,PSP编程不能直接使用C函数库,于是连sin、cos这种函数都浮云了,需要自己实现,sigh。
在开始绘图之前,首先需要初始化PSP的Graphics Unit,简称GU。PSP允许对VRAM(即Video RAM)进行直接读写,因此我们可以直接控制像素的显示,相当于在PC上拥有直接控制显示缓冲区的能力(让人想起了美好的DOS编程时代)。根据pspdev的wiki所言[1],VRAM的起始地址为0×04000000,大小为2MB,但是必须or 0×40000000使用,不然会有意想不到的后果,原因不明。
配置好开发环境之后,下一步工作当然就是编写著名的Hello World了。鉴于网上关于PSP的Hello World的中英文资料都比较多,再加上我年初实习那阵写的代码和当时配置好的Makefile也找不到了,所以这一节的代码就主要参考了这篇wiki[1]和论坛上的一些资料。
PSP编程最开始接触到的知识点就是PSP的线程模型和回调机制,这是构成PSP自制程序的基本框架。目前还没见到关于这方面内容的详细研究和探索,只是按照sample的代码框架来这么写,顶多查查文档看看每个函数的作用,但无法深入理解内部的机制。所以暂时就不细究了,把程序正常运行起来就大功告成。
另外,在开始写代码之前,请不要忘记设置Visual Studio里Project Properties中的Include Directories的路径,否则虽然可以正常编译链接,但是满屏幕的红色波浪线看着也不爽是吧。