Ray Marching

Ray Marching

这是光线步进的一个可以运行在ShaderToy上的例子,加入了阴影与GodRay。

Shadertoy地址

#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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值