构造摄像机
在上一篇文章中,我们是基于原始的uv构建的坐标系。但如果我们希望摄像机更自由的移动,最好还是把uv和空间坐标系脱离开。
通常在3d引擎中渲染时,我们要将物体的坐标转换到世界坐标系,在转换到摄像机坐标系和裁剪坐标系。
但是raymarching中,所有的坐标要转换到计算SDF的坐标系,即世界坐标系。因此,我们要对摄像机的坐标进行转换,就需要获得摄像机空间的坐标轴向量。
mat3 setCamera(in vec3 camtar, in vec3 campos){
vec3 z = normalize(camtar-campos);//z轴从摄像机指向目标点
vec3 cp = vec3(0.,1.,0.);//辅助向量,确定一个经过z且垂直地面的平面
vec3 x = normalize(cross(cp,z));//从而叉乘计算出x轴
vec3 y = cross(z,x);
return mat3(x,y,z);
}
在这里我们的摄像机由三个值控制:摄像机的位置,摄像机的目标点,取景框到摄像机的距离。
其中,构造摄像机空间的坐标系统已经用到了前两个值,计算好了我们look at的方向。而最后一个值,决定了我们视野的范围。因为我们摄像机的视锥体就是摄像机位置到平面四点的连线,所以距离越近,视锥角越大,看到的范围越广。
vec3 render(vec2 uv){
...
mat3 cam = setCamera(cam_tar,cam_pos);
vec3 rd = cam*vec3(uv,3.);
float t = rayMarch(cam_pos,rd);
...
}
注意,这里的vec3(uv,3.)
是在摄像机空间中的坐标,其他的都是世界空间的坐标。
让我们改变cam_pos,将tar留在原点,就可以得到这样的环视效果:
摄像机旋转
上面的摄像机模型是默认相机保持水平,如果希望沿着视野方向旋转,可以将cp这个辅助向量改写成这样: mat3 setCamera(in vec3 camtar, in vec3 campos, in float camro){ vec3 z = normalize(camtar-campos); vec3 cp = vec3(sin(camro),cos(camro),0.); vec3 x = normalize(cross(cp,z)); vec3 y = cross(z,x); return mat3(x,y,z); } camro为0时,cp就是朝上方的向量。 mat3 cam = setCamera(cam_tar,ro,sin(iTime)PI); 另外我还希望摄像机的方向对鼠标位置进行相应,在PI/2的范围内上下左右小幅度旋转。 这个的实现有很多种方式,我这里是在摄像机空间对rd向量进行了旋转: mat2 rotate(float a){ return mat2(cos(a),sin(a),-sin(a),cos(a)); } ... vec3 render(vec2 uv){ ... vec3 rd = vec3(uv,3.); //decide view width if(iMouse.x!=0. || iMouse.y!=0. ){ float angle_x = -(iMouse.x /iResolution.x-0.5) 0.5PI; float angle_y = (iMouse.y /iResolution.y-0.5) 0.5PI; rd.yz = rotate(angle_y); rd.zx = rotate(angle_x);} rd = normalize(setCamera(cam_tar,cam_pos,0.)rd);//viewing frustum float t = rayMarch(cam_pos,rd); ... }