Ray Marching
这是光线步进的一个可以运行在ShaderToy上的例子,加入了阴影与GodRay。
#define fov 60.0
#define beginCamPosition vec3(0.0, 0.0, -14.0)
#define doughnutOffset 1.5
#define doughnutRadius 0.6
#define surfaceThreshold 1e-1
#define planeHeight -4.0
#define farClip 1e+4
#define mainColor vec4(1.0,0.9,0.3,1.0)
float plane_dist_estimator(in vec3 camPos, in vec3 viewDir)
{
if(viewDir.y < 0.0 && camPos.y > planeHeight)
{
return abs(camPos.y - planeHeight) / abs(viewDir.y);
}
else if(viewDir.y > 0.0 && camPos.y < planeHeight)
{
return abs(camPos.y - planeHeight) / abs(viewDir.y);
}
return farClip;
}
float doughnut_dist_estimator(in vec3 surface_pos, in mat3 obj2WorldRot, in mat3 world2ObjRot)
{
vec3 objPos = world2ObjRot * surface_pos;
objPos = vec3(objPos.x,0,objPos.z);
vec3 worldPos = obj2WorldRot * objPos;
//一个平躺的甜甜圈
vec3 refPoint = normalize(worldPos) * doughnutOffset;
float len = length(surface_pos - refPoint);
return len - doughnutRadius;
}
vec3 surface_normal(in vec3 surface_pos, in mat3 obj2WorldRot, in mat3 world2ObjRot)
{
vec3 objPos = world2ObjRot * surface_pos;
objPos = vec3(objPos.x,0,objPos.z);
vec3 worldPos = obj2WorldRot * objPos;
//一个平躺的甜甜圈
vec3 refPoint = normalize(worldPos) * doughnutOffset;
return normalize(surface_pos - refPoint);
}
float surface_shadow(in vec3 surface_pos, in vec3 lightDir, in mat3 obj2WorldRot, in mat3 world2ObjRot)
{
float dis = 0.1; //计算阴影必须有一个初始的距离
for(int i = 0; i < 32; ++i)
{
float delta = doughnut_dist_estimator(surface_pos + dis * -lightDir,obj2WorldRot,world2ObjRot);
dis += delta;
if (delta < surfaceThreshold)
return 0.0;
}
return 1.0;
}
vec4 intersects(in vec3 camPos, in vec3 viewDir, in vec3 lightDir, in mat3 obj2WorldRot, in mat3 world2ObjRot,
out float resultDis)
{
//射线查甜甜圈位置
float dis = 0.0;
for (int i = 0; i < 255; ++i)
{
float delta = doughnut_dist_estimator(camPos + dis * viewDir, obj2WorldRot, world2ObjRot);
dis += delta;
if (delta < surfaceThreshold)
{
resultDis = dis;
//查询到甜甜圈
vec3 surface_pos = camPos+ viewDir * dis;
vec3 normalDir = surface_normal(surface_pos, obj2WorldRot, world2ObjRot);
float dotv = dot(normalDir, -lightDir) * 0.5 + 0.5;
dotv = mix(0.1,1.0,dotv);
return mainColor * dotv * 1.0;
}
}
//射线查地面位置
dis = plane_dist_estimator(camPos, viewDir);
if(dis < farClip)
{
resultDis = dis;
vec3 surface_pos = camPos+ viewDir * dis;
vec3 normalDir = vec3(0,1,0);
float dotv = dot(normalDir, -lightDir) * 0.5 + 0.5;
dotv = mix(0.2,1.0,dotv);
float interplate = mod(floor(surface_pos.x * 0.5) + floor(surface_pos.z * 0.5),2.0);
vec4 col = mix(vec4(0.5, 0.5, 0.5, 1),vec4(1, 1, 1, 1),interplate) * dotv;
float shadow = surface_shadow(surface_pos, lightDir, obj2WorldRot, world2ObjRot);
col = mix(mainColor * 0.5 * col,col,shadow);
return mix(col, vec4(0.4,0.7,0.9,1), smoothstep(5.0,50.0,dis));
}
//天空盒颜色
resultDis = farClip;
return vec4(0.4,0.7,0.9,1);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
//甜甜圈的变换矩阵
float rotSpeed = iTime * 0.6;
vec3 tmpX0 = vec3(1,0,0);
vec3 tmpX1 = vec3(0,cos(rotSpeed),-sin(rotSpeed));
vec3 tmpX2 = vec3(0,sin(rotSpeed),cos(rotSpeed));
mat3 rotX = mat3(tmpX0,tmpX1,tmpX2);
rotSpeed *= 2.0;
vec3 tmpY0 = vec3(cos(rotSpeed),0,sin(rotSpeed));
vec3 tmpY1 = vec3(0,1,0);
vec3 tmpY2 = vec3(-sin(rotSpeed),0,cos(rotSpeed));
mat3 rotY = mat3(tmpY0,tmpY1,tmpY2);
rotSpeed *= 2.0;
vec3 tmpZ0 = vec3(cos(rotSpeed),-sin(rotSpeed),0);
vec3 tmpZ1 = vec3(sin(rotSpeed),cos(rotSpeed),0);
vec3 tmpZ2 = vec3(0,0,1);
mat3 rotZ = mat3(tmpZ0,tmpZ1,tmpZ2);
mat3 obj2WorldRot = rotZ * rotX * rotY;
mat3 world2ObjRot = inverse(obj2WorldRot);
//甜甜圈的变换矩阵结束
//摄像机旋转,拖动屏幕可以旋转摄像机
float radian = mix(-3.1415926,3.1415926,-iMouse.x / iResolution.x);
vec3 tmpCamY0 = vec3(cos(radian),0,sin(radian));
vec3 tmpCamY1 = vec3(0,1,0);
vec3 tmpCamY2 = vec3(-sin(radian),0,cos(radian));
mat3 rotCamY = mat3(tmpCamY0,tmpCamY1,tmpCamY2);
//摄像机旋转结束
//------------------------------------------------------------------
vec2 uv = (vec2(fragCoord.x - iResolution.x / 2.0, fragCoord.y - iResolution.y / 2.0) / max(iResolution.x, iResolution.y)) * 2.0;
float tanValue = tan(radians(fov * 0.5));
float cameraDis = (iResolution.y / iResolution.x) / tanValue;
vec3 viewDir = normalize(vec3(uv, cameraDis)); //这里通过fov确定每个像素的观察方向
viewDir = rotCamY * viewDir;
float lightRotSpeed = iTime * 0.5;
vec3 lightDir = normalize(vec3(sin(-lightRotSpeed), -1, sin(lightRotSpeed * 2.0)));
float resultDis;
vec3 camPosition = rotCamY * beginCamPosition;
fragColor = intersects(camPosition, viewDir, lightDir, obj2WorldRot, world2ObjRot, resultDis);
//上帝光体积渲染
float sinV0 = sqrt(1.0 - pow(dot(normalize(vec3(0,0,0) - camPosition),lightDir),2.0));
float beginVertical = length((vec3(0,0,0) - camPosition)) * sinV0 - (doughnutOffset + doughnutRadius);
float endVertical = length((vec3(0,0,0) - camPosition)) * sinV0 + (doughnutOffset + doughnutRadius);
float sinV1 = sqrt(1.0 - pow(dot(lightDir,viewDir),2.0));
//这里算的是光线步进的开始距离与结束距离
float beginDis = beginVertical / sinV1;
float endDis = endVertical / sinV1;
float totalRay = 0.0;
for (float i = beginDis; i < endDis; i += 0.2)
{
float totalDis = i;
if(totalDis > resultDis)
{
break;
}
vec3 pos = camPosition + viewDir * totalDis;
float shadow = surface_shadow(pos, lightDir, obj2WorldRot, world2ObjRot);
totalRay += 1.0 - shadow;
}
fragColor = mix(fragColor,mainColor,totalRay * 0.04);
return;
}