射线和椭球相交精度的提高

时间:2014-08-24 09:58:04

标签: geometry glsl shader floating-accuracy artifacts

我需要在我的Atmospheric scattering GLSL fragment shader之一中增强函数的精度,它计算单射线和轴对齐的椭圆体之间的交点。

这是我的大气散射着色器的核心功能。旧的原始着色器在floats上,并且正常渲染很好,但是在添加缩放后我发现距离相对较小时精度会丢失。在浮标上,地球的可用距离仅为0.005 AU(天文单位)。所以我试图将关键函数移植到double,这有助于现在可用距离大约为1.0 AU(带有小文物)

这是Fragment Shader中的double版本的函数(旧式源使用已弃用的东西!!!)

#extension GL_ARB_gpu_shader_fp64 : enable
double abs(double x) { if (x<0.0) x=-x; return x; }
// compute length of ray(p0,dp) to intersection with ellipsoid((0,0,0),r) -> view_depth_l0,1
// where r.x is elipsoid rx^-2, r.y = ry^-2 and r.z=rz^-2
float view_depth_l0=-1.0,view_depth_l1=-1.0;
bool _view_depth(vec3 _p0,vec3 _dp,vec3 _r)
    {
    double a,b,c,d,l0,l1;
    dvec3 p0,dp,r;
    p0=dvec3(_p0);
    dp=dvec3(_dp);
    r =dvec3(_r );
    view_depth_l0=-1.0;
    view_depth_l1=-1.0;
    a=(dp.x*dp.x*r.x)
     +(dp.y*dp.y*r.y)
     +(dp.z*dp.z*r.z); a*=2.0;
    b=(p0.x*dp.x*r.x)
     +(p0.y*dp.y*r.y)
     +(p0.z*dp.z*r.z); b*=2.0;
    c=(p0.x*p0.x*r.x)
     +(p0.y*p0.y*r.y)
     +(p0.z*p0.z*r.z)-1.0;
    d=((b*b)-(2.0*a*c));
    if (d<0.0) return false;
    d=sqrt(d);
    l0=(-b+d)/a;
    l1=(-b-d)/a;
    if (abs(l0)>abs(l1)) { a=l0; l0=l1; l1=a; }
    if (l0<0.0)          { a=l0; l0=l1; l1=a; }
    if (l0<0.0) return false;
    view_depth_l0=float(l0);
    view_depth_l1=float(l1);
    return true;
    }
  • 输入是椭球的光线和半径^ -2
  • 输出是从p0到交叉点的距离

    geometric overview

  • 输入和输出变量的精度是浮点数(足够)

这是移植到Double

之后的样子

artifacts

所以问题是:Q1。如何提高此功能的准确性?

  • view_depth_l0的目标准确度,[{1}}距view_depth_l1 +/- 20 m <{1}}

那将是理想的,现在看起来像10澳大利亚距离+/- 5公里这是不好的。即使10次精确计算也会向前迈出一大步?

[edit1] l0,l1范围

我错了|p0|=100 AU的浮点转换是工件的原因。将其移至相对距离后,精度提高了很多。我刚补充一下:

view_depth_l0,view_depth_l1

之前:

    // relative shift to preserve accuracy
    const double m0=1000000000.0; // >= max view depth !!!
    if (l0>m0){ a=floor(l0/m0)*m0; a-=m0; if (l1>l0) l1-=a; l0-=a; }

着色器的其余部分处理 view_depth_l0=float(l0); view_depth_l1=float(l1); return true; } 作为相对值,无论如何,结果如下:

artifacts

对于距离高达10.0 AU的情况,它现在很好(仅在非常高的变焦中才能看到伪影),新的伪影很可能在其他地方引起,所以当我有时间和意愿时,我们必须进一步研究。

[edit2]将p0沿着dp移向(0,0,0)

实现需要相对昂贵的规范化和长度函数,没有范围转换的结果(edit1)比原始函数好一点,但改进不是​​太大。使用范围转换(edit1),结果与之前相同,所以这不是方法。我的结论是所有剩余的工件都不是由视图dept函数本身引起的。

我将尝试将着色器移植到l0,l1以检查输入数据是否未被浮动太多

[edit3]实际源代码

#version 400  + fp64

将其余的着色器移植到double无效。唯一可以改善这一点的是#extension GL_ARB_gpu_shader_fp64 : enable double abs(double x) { if (x<0.0) x=-x; return x; } // compute length of ray(p0,dp) to intersection with ellipsoid((0,0,0),r) -> view_depth_l0,1 // where r.x is elipsoid rx^-2, r.y = ry^-2 and r.z=rz^-2 float view_depth_ll= 0.0, // shift to boost accuracy view_depth_l0=-1.0, // view_depth_ll+view_depth_l0 first hit view_depth_l1=-1.0; // view_depth_ll+view_depth_l1 second hit const double view_depth_max=100000000.0; // > max view depth bool _view_depth(vec3 _p0,vec3 _dp,vec3 _r) { dvec3 p0,dp,r; double a,b,c,d,l0,l1; view_depth_ll= 0.0; view_depth_l0=-1.0; view_depth_l1=-1.0; // conversion to double p0=dvec3(_p0); dp=dvec3(_dp); r =dvec3(_r ); // quadratic equation a.l.l+b.l+c=0; l0,l1=?; a=(dp.x*dp.x*r.x) +(dp.y*dp.y*r.y) +(dp.z*dp.z*r.z); b=(p0.x*dp.x*r.x) +(p0.y*dp.y*r.y) +(p0.z*dp.z*r.z); b*=2.0; c=(p0.x*p0.x*r.x) +(p0.y*p0.y*r.y) +(p0.z*p0.z*r.z)-1.0; // discriminant d=sqrt(b.b-4.a.c) d=((b*b)-(4.0*a*c)); if (d<0.0) return false; d=sqrt(d); // standard solution l0,l1=(-b +/- d)/2.a a*=2.0; l0=(-b+d)/a; l1=(-b-d)/a; // alternative solution q=-0.5*(b+sign(b).d) l0=q/a; l1=c/q; (should be more accurate sometimes) // if (b<0.0) d=-d; d=-0.5*(b+d); // l0=d/a; // l1=c/d; // sort l0,l1 asc if (abs(l0)>abs(l1)) { a=l0; l0=l1; l1=a; } if (l0<0.0) { a=l0; l0=l1; l1=a; } if (l0<0.0) return false; // relative shift to preserve accuracy after conversion back float if (l0>view_depth_max){ a=floor(l0/view_depth_max)*view_depth_max; a-=view_depth_max; view_depth_ll=float(a); if (l1>l0) l1-=a; l0-=a; } // conversion back float view_depth_l0=float(l0); view_depth_l1=float(l1); return true; } 输入数据(输入为double但GL将其转换为double),但我当前的GLSL HW不允许float插值器< / p>

Q2。有没有办法将64 bit插值器从顶点传递到片段着色器?

旧式GLSL中的double或核心配置文件中的varying dvec4 pixel_pos;

0 个答案:

没有答案