在学习关于Ray Marching的过程中,我发现对于Ray Marching的理解可以通过ShaderToy来加深一下。
如上图所属,我们的摄像机需要获取4组数据,UV、摄像机位置(CameraPos)、视线(ViewDir)、缩放,然后根据这四组数据进行输出光线,这个光线是个向量,然后经过一个黑箱操作(这里指Ray Marching)输出一组数据,我们在这组数据上进行材质的添加与系列操作,最终输出到我们的片元着色器/像素着色器(fragment shader/pixel shader)。而我们需要进行的操作就是如何进行黑箱计算。
如上我们具体所做的就是上面图中的操作,不断的使用光线步进来检测物体
第一次步进接触到最靠近相机的物体表面,我们在这打个标记,表示第一次步进,然后依次为圆心再对其他的光线进行步进
第二次步进我们在次发散出去的光又一次接触到了离此处最近的物体表面也就是黄圈的半径,我们在此再次打个点进行我们下一步的步进。然后周而复始达到最远的点。
如此我们就能勾勒出一个大体的形状.我们在这一部的代码可以如下
float raymarching(vec3 ro, vec3 rd){
float dO=0.;
for(int i=0; i<MAX_STEPS;i++){
vec3 p = ro + rd*dO;
float dS = GetDist(p);
dO +=dS;
if (dO>MAX_DIST || dS<SURF_DIST) break;
}
return dO;
}
在这个函数之前,我们需要再写一段函数来获得步进距离的函数。
首先我们先获得两个距离,一个是相机到地面的距离,一个是相机到物体表面的距离,在这里物体是一个球,相机到地面的距离可以通过cameraPos.y-plane.y来获得,在这里相机到地面的距离就是相机的y坐标。相机到球体表面的距离是相机坐标减去球心坐标取模,再减去球的半径。我们为了确保光线能够安全的达到物体表面,我们需要将相机与任意物体的距离作为初始的步进距离,在这里我们用相机到地面的距离。
所以此处的步进方式应该如下
如此步进便能安全有效的达到物体的表面。此处代码为
float GetDist(vec3 p){
vec4 s = vec4 (0, 1, 6, 1);
float sphereDist = length(p-s.xyz)-s.w;
float planeDist=p.y;
float d = min(sphereDist,planeDist);
return d;
}
之后添加灯光并获取法线就能进行简单的模型编写。
完整代码如下:
#define MAX_STEPS 100
#define MAX_DIST 100.
#define SURF_DIST .01
float GetDist(vec3 p){
vec4 s = vec4 (0, 1, 6, 1);
float sphereDist = length(p-s.xyz)-s.w;
float planeDist=p.y;
float d = min(sphereDist,planeDist);
return d;
}
float raymarching(vec3 ro, vec3 rd){
float dO=0.;
for(int i=0; i<MAX_STEPS;i++){
vec3 p = ro + rd*dO;
float dS = GetDist(p);
dO +=dS;
if (dO>MAX_DIST || dS<SURF_DIST) break;
}
return dO;
}
vec3 GetNormal(vec3 p){
float d = GetDist(p);
vec2 e = vec2(.01,0);
vec3 n = d- vec3(
GetDist(p-e.xyy),
GetDist(p-e.yxy),
GetDist(p-e.yyx));
return normalize(n);
}
float GetLight(vec3 p){
vec3 lightPos = vec3(0, 5, 6);
lightPos.xy += vec2(sin(iTime),cos(iTime))*2.;
vec3 l = normalize(lightPos-p);
vec3 n = GetNormal(p);
float dif = clamp(dot(n,l),0.,1.);
float d = raymarching(p+n*SURF_DIST*2.,l);
if(d<length(lightPos-p)) dif *=.1;
return dif;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (fragCoord-0.5*iResolution.xy)/iResolution.y;
vec3 col = vec3(0);
vec3 ro =vec3(0,1,0);
vec3 rd = normalize(vec3(uv.x,uv.y,1));
float d=raymarching(ro,rd);
vec3 p = ro + rd * d;
float dif = GetLight(p);
col = vec3(dif);
fragColor = vec4(col,1.0);
}