在之前的Raymarching文章中,我们都是黑白的世界。本文将提升我们场景的背景,用一些简单的色块表示天空和大地,也为不同的物体打上不同的颜色
为物体增加颜色
为物体增加颜色思路很简单,就是为每一个独立的物体带上一个index, 在输出distance之后输出一个index, 同时根据 IQ的编码规范, map 函数用于构建我们的场景
vec2 opUnion( vec2 d1, vec2 d2 )
{
return (d1.x<d2.x) ? d1 : d2;
}
vec2 map(vec3 p) {
vec2 result = vec2(0.0);
vec2 sphere = vec2(
sdSphere(p-vec3(3.0, 2.0, 0.0), 1.),
0.5
);
vec2 line = vec2(
sdLine(p, vec3(-1., 1., 0.), vec3(1.0, 1.0, 0.0), 0.2),
1.5
);
vec2 box = vec2(
sdBox(p-vec3(-3.0, 2.0, 0.), vec3(1.0)),
2.5
);
vec2 plane = vec2(
sdPlane(p, vec3(0., 1., 0.), 0.0),
0.0
);
result = opUnion(sphere, line);
result = opUnion(result, box);
result = opUnion(result, plane);
return result;
}
另外增加一个 render 函数做渲染动作,利用distance + index 制作颜色
vec3 render(in vec3 ro, in vec3 rd, float time) {
vec3 col = AMBIENT_COLOR;
vec2 res = castRay(ro, rd, time);
if (res.y > 2.0) {
col = vec3(1.0, 0.0, 0.0);
} else if (res.y > 1.0) {
col = vec3(1.0, 1.0, 0.0);
} else if (res.y > 0.0) {
col = vec3(0.0, 0.0, 1.0);
} else {
col = vec3(1.0);
}
vec3 p = ro + rd * res.x;
vec3 directionalLightPosition = vec3(0., 5., 5.);
directionalLightPosition.xz += 3.0 * vec2(sin(0.15), cos(0.15));
float diffuse = GetDiffuseLight(p, directionalLightPosition);
col *= diffuse;
return col;
}
天空大地
为大地贴一些绿色,非常简单因为大地也是一个 SDF, 而它也可以带上index
else if (res.y > 0.0) {
col = vec3(0.0, 0.8, 0.1);
}
而天空没有 SDF, 我们可以换一个角度思考, 如果ray得到结果有最短距离,那么说明ray碰到了物体,如果没有说明 ray 到了极远处也就是我们的天空, 天空可以做一个梯度颜色,在早上越接近于地平线也就是越远的地方,有太阳所以会更加亮一些
vec3 col = vec3(0.5, 0.8, 0.9) - max(rd.y,0.0)*0.5;
if (res.x > -.5) {
col *= diffuse;
}
棋盘
真实世界地面上有很多东西可以帮助我们判断远近,例如建筑物,树。 上面的大地是纯粹的绿色有点难以分辨距离,一般会做一个间隔的棋盘用于观察物体距离。 思路非常简单,在 2d也经常用到。对 XZ平面做一次 floor, 然后 mod xz坐标获取间隔
if (res.y > 0.0) {
col = vec3(0.0, 0.5, 0.1);
vec2 q = floor(p.xz);
float f = mod(q.x+q.y,2.);
col.g += f;
}
但是棋盘有一个问题,在非常远处的时候有一些纹,格子之间混合了。这在图形学中称为摩尔纹,图中不明显,如果看这张图就很清晰
原因是因为在远处地平线一个像素已经要渲染多个格子了,所以出现了随机采样的特征。 关于这个问题的来源和解决方法,GAMES101讲的非常清楚
本质上是要解决的问题是,如果一个像素点覆盖了纹理的多个像素点,那么这个像素点应该显示什么颜色。比如可以选择纹理多个像素点的平均值,也可以选择纹理像素点的最大值。但是在 shadertoy中应该怎么解决这个问题呢?
IQ为这个问题专门写2 文章
目前还没完全搞明白,大概是通过一些数值方法完成了对区域的积分实现了像素的梯度采样与盒滤波器