在上一篇博客中介绍了简单的光照模型,并用字符表示亮度画了一颗心。前两次都是抄的知乎答主的帖子,这次继续抄,不抄袭怎么能进步呢!
我们用的是ppm格式的没有经过任何压缩的图片,关于这个文件的介绍可以参考csdn上的这篇文章。简而言之,这是一个非常简单的格式,三分钟就能上手,缺点是未经任何压缩,体积庞大,而且格式冷门,很少有工具能读取。好在ppm可以用ps打开,还有一个网站可以在线转换图片格式(传送门)。
画图方式非常简单,与在控制台用打印字符的方式大同小异。前者将夹角余弦映射到字符,后者将其映射到RGB颜色空间。由于心是纯红色的,实际上只有R需要处理。主要代码如下
int main(int argc, char const *argv[]) { int width = 512, height = 512; FILE *fp = fopen("heart3d.ppm","w"); fprintf(fp, "P3 %d %d 255\n", width, height); double x, y, z, nx, ny, nz, a; // 光源方向(入射光方向) double lx = 1, ly = 1, lz = -1; double L = sqrt(lx*lx+ly*ly+lz*lz); lx = lx / L; ly = ly / L; lz = lz / L; for (int h=0; h<height; h++) { // z \in [-1.5, 1.5] z = - h*3.0/height + 1.5; for (int w=0; w<width; w++) { // z \in [-1.5, 1.5] x = w*3.0/width - 1.5; int r = 0; // 点在心形图内 if (f(x,0,z) < 0) { // 计算y y = compute_y(x,z); // 计算梯度(法向量) nx = fx(x,y,z); ny = fy(x,y,z); nz = fz(x,y,z); // light source (1,1,-0.5) a = (lx*nx + ly*ny + lz*nz)/sqrt(nx*nx+ny*ny+nz*nz); // 根据入射光与法线夹角余弦值计算光强 r = (int) ((0.5 * a + 0.5)*255); } fprintf(fp, "%d 0 0 ", r); } fprintf(fp,"\n"); } return 0; }
跟上回一样,这里也涉及到了坐标系转换的问题,另一个是需要进行坐标缩放。在用C语言画颗心(一)——心形曲面中我们给出的公式对应的心形图的跨度为[-1.5,1.5],因此需要将图像坐标系上的点映射到这个区间上。
结果图长成底下这个样子,
这张图比原作者的图更加精细一点,原因在于用了二分法求解\(y\)、用了二阶精度的差分格式和改变了法向量的计算方式。但是不知道为何,图像中间出现了一根黑线,目前还没找到bug在哪里,若有高人解惑,不胜感激。
完整代码地址 https://gitee.com/CharlieLUO/math-game/blob/master/heart3d.c
未经允许不得转载:Charlie小站 » 用C语言画颗心(三)——着色
评论前必须登录!
登陆 注册