Verbesserung der Genauigkeit von Strahlen- und Ellipsoidschnittpunkten

Ich muss die Präzision für die Funktion in einem von mir verbessernAtmosphärisch streuender GLSL-Fragment-Shader Hiermit wird der Schnittpunkt zwischen Einzelstrahl und achsenausgerichtetem Ellipsoid berechnet.

Dies ist die Kernfunktion für meinen atmosphärischen Streushader. Der alte ursprüngliche Shader war eingeschaltetfloats und für normales rendern war es in ordnung, aber nach zoomaddition stellte ich fest, dass bei relativ kleinen abständen die präzision verloren geht. Auf Schwimmern betrug die nutzbare Distanz für die Erde nur bis zu 0,005 AE (Astronomische Einheit). Also habe ich versucht, die kritische Funktion zu portierendouble und es hilft ja jetzt die nutzbare entfernung liegt bei ca. 1,0 au (mit kleinen artefakten)

Dies ist dasdouble Version der Funktion in Fragment Shader (Quelle im alten Stil verwendet veraltete Dinge !!!)

#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;
    }
Eingabe ist Strahl und Radius ^ -2 des Ellipsoids

Ausgabe ist der Abstand von p0 zu Schnittpunkten

Genauigkeit der Eingabe- und Ausgabevariablen ist float (es ist ausreichend)

So sieht es nach der Portierung nach Double aus

Die Frage ist also: Q1. Wie kann die Genauigkeit dieser Funktion verbessert werden?

Zielgenauigkeit fürview_depth_l0,view_depth_l1 ist+/- 20 m für die Entfernung|p0|=100 AU

Das wäre ideal, im Moment scheint es +/- 5 km für eine Entfernung von 10 AU zu sein, was schlecht ist. Selbst eine 10-fach genaue Berechnung ist ein großer Fortschritt.

[edit1] 10, 11 Bereich

Ich habe mich beim float umbau geirrtview_depth_l0,view_depth_l1 ist die Ursache für Artefakte. Nachdem es auf die relative Distanz verschoben wurde, verbesserte sich die Genauigkeit erheblich. Ich habe gerade folgendes hinzugefügt:

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

vor dem:

    view_depth_l0=float(l0);
    view_depth_l1=float(l1);
    return true;
    }

Der Rest des Shader-Handlesl0,l1 als relative Werte ergibt sich wie folgt:

Für Entfernungen bis zu 10,0 AU ist es jetzt in Ordnung (Artefakte sind nur bei sehr hohen Zooms zu bemerken). Neue Artefakte werden höchstwahrscheinlich an anderer Stelle verursacht. Deshalb muss ich weiter recherchieren, wenn ich Zeit habe und will.

[edit2] Verschieben von p0 entlang dp in Richtung (0,0,0)

Die Implementierung erfordert relativ teure Normalisierungs- und Längenfunktionen und das Ergebnis ohne Bereichsverschiebung (edit1) war ein bisschen besser als die Raw-Funktion, aber die Verbesserung ist nicht zu groß. Bei der Bereichsverschiebung (edit1) ist das Ergebnis das gleiche wie zuvor, so dass dies nicht der Fall ist. Mein Fazit ist, dass alle verbleibenden Artefakte nicht durch die Ansichtsfunktion selbst verursacht werden.

Ich werde versuchen, Shader zu portieren#version 400 + fp64 Insgesamt ist zu prüfen, ob die Eingabedaten nicht durch zu viel float gerundet werden

[edit3] aktueller Quellcode

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

Das Portieren des restlichen Shaders auf double hat keine Auswirkung. Das einzige, was dies verbessern könnte, istdouble Eingabedaten (Eingabe istdouble aber GL konvertieren es zufloat), aber meine aktuelle GLSL HW erlaubt nicht64 bit Interpolatoren

Q2. Gibt es einen Weg vorbeidouble Interpolatoren vom Vertex zum Fragment Shader?

So etwas wievarying dvec4 pixel_pos; im alten Stil GLSL oderout smooth dvec4 pixel_pos; im Kernprofil

Antworten auf die Frage(0)

Ihre Antwort auf die Frage