用C语言画颗心(三)——着色

上一篇博客中介绍了简单的光照模型,并用字符表示亮度画了一颗心。前两次都是抄的知乎答主的帖子,这次继续抄,不抄袭怎么能进步呢!

我们用的是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语言画颗心(三)——着色

赞 (0)
分享到:更多 ()