213123sdasddadas

举例:Raytrace 实现效果图

当我们想利用 Raytrace 技术实现一些伪 3D 的效果的时候,常常会遇到 ViewRay 和平面 是否相交的问题。

首先我们来看看普通版的解决方法。dsadad

普通版

如果只是简单的判断从相机射出的光线 ViewRay 是否和平面相交,我们只需要把光线向量和平面的法向量进行点乘即可,如下代码所示 

/// @note 光线追踪之平面
/// @param pl 平面法线
/// @param ro Ray Origin
/// @param rd Ray Direction
float tracePlane( in vec4 pl, in vec3 ro, in vec3 rd )
{
    /// @note 最直接的方式,直接用 ViewRay 的方向向量和平面法线做点乘
    /// 注意 ViewRay 和平面法线是反向的,所以要加负号。
    /// 但缺点是这样没有景深的效果
    return -dot(pl.xyz, rd); 
}

 效果如图所示

白色为天空,黑色为地面

 但这样缺点是,如果我们想根据 SDF (有向距离场)做进一步的复杂酷炫特效处理时,会发现是没有景深信息的。

还好我们只需要稍加修改(ViewRay 和平面相交的 Tricky 妙用),即把相机点到平面的距离加到上面的式子,就可以营造出景深的效果。如下图所示

白色为天空,黑色为地面,越黑表示距离越小,越白表示距离越大

 优化升级版

令我们有一个平面,通过 “点法式” 的 S 点和法线 N 来确定,接着有一条射线从 O 发出沿着 D 方向,与平面相交于 P 点,则我们可以列出以下方程

\vec{P}=\vec{O}+t\vec{D} ,t 表示从 O 射出的有符号的距离

|(\vec{P}-\vec{S})\cdot \vec{N}|=0

 两式联立,可以结出 t,即如下所示

\prod \left ( \right )t=\frac{(\vec{S}-\vec{O})\cdot\vec{N}}{\vec{D} \cdot \vec{N}}

t=\frac{(\vec{S}-\vec{O})\cdot\vec{N}}{\vec{D} \cdot \vec{N}} 或 t=\frac{s-\vec{O}\cdot\vec{N}}{\vec{D}\cdot\vec{N}}

有一点需要注意,当 \vec{D}\cdot\vec{N}=0 的时候,意味着射线是和平面平行的,那么除非 O 点本身就在平面上,否则是无解的。

/// @note 光线追踪之平面
/// @param pl 平面法线
/// @param ro Ray Origin
/// @param rd Ray Direction
float tracePlane( in vec4 pl, in vec3 ro, in vec3 rd )
{
    /// @note 加入了相机和平面的距离,有景深距离的效果(离相机越远颜色越白)
    /// w 是 “点法式” 的平面表示法中的点到平面的距离,即 dot(S, N),作用是微调距离,例如本例中的平面的拉近拉远(升高降低)
    return -(dot(pl.xyz, ro) + pl.w) / dot(pl.xyz, rd);
}

有了距离之后,我们就可以结合相机的坐标和 ViewRay 的方向将纹理坐标给计算出来

vec3 p = from + dir * distGround;
地面的纹理坐标可视化

完整代码

float tracePlane( in vec4 pl, in vec3 ro, in vec3 rd )
{
    return -(dot(pl.xyz, ro) + pl.w) / dot(pl.xyz, rd);
    // return -dot(pl.xyz, rd); ///< 没有景深距离的效果(离相机越远颜色越白)
}

#iUniform float w = 0. in {-1., 1.}
bool traceWall( in vec3 from, in vec3 dir, inout float dist, inout vec3 norm )
{
    vec3 wallNorm = vec3(0, -1, 0);
    float distWall = tracePlane( vec4(wallNorm, w), from, dir );
    if (distWall > 0.0)
    {
        dist = distWall;
        norm = wallNorm;
        return true;
    }
    return false;
}

bool traceGround( in vec3 from, in vec3 dir, inout float dist, inout vec3 norm )
{
    vec3 groundNorm = vec3(0, 0, 1);
    float distGround = tracePlane( vec4(groundNorm, w), from, dir );
    if (distGround > 0.0)
    {
        dist = distGround;
        norm = groundNorm;
        return true;
    }
    return false;
}

void mainImage()
{
    vec2 uv = fragCoord.xy / iResolution.xy * 2.0 - 1.0;
    uv.x *= iResolution.x / iResolution.y;

    vec3 from = vec3(0, -6.5, 2);
    vec3 dir = normalize(vec3(uv.x, 1.5, uv.y));    ///< 注意,uv.y 在 z 分量上

    vec2 mouse = vec2(0);
    if (iMouse.z > 0.0) mouse = iMouse.xy / iResolution.xy * 2.0 - 1.0;
    dir.yz *= rot(-mouse.y * 0.4);
    dir.xy *= rot(mouse.x * 0.4);

    float distWall = 99999.9;
    vec3 normWall = vec3(0);
    bool hitWall = traceWall(from, dir, distWall, normWall);


    float distGround = 99999.9;
    vec3 normGround = vec3(0);
    bool hitGround = traceGround(from, dir, distGround, normGround);

    fragColor = vec4(distGround / 50.);
    fragColor.a = 1.;
}

猜你喜欢

转载自blog.csdn.net/cpongo1/article/details/111560009