Ein neuerer Ansatz zur Streuung unter der Oberfläche

7

Ich las "Ein praktisches Modell für den Lichttransport unter der Oberfläche" und "Eine schnelle hierarchische Rendering-Technik für durchscheinende Materialien". Wenn ich das richtig verstehe, berücksichtigt der erstere nicht den Fall, in dem ein Objekt hinterleuchtet und wir sehen können, dass das Licht durch ihn hindurchgeht.

Letzteres kann dies erreichen, aber es gibt eine Vorverarbeitungsstufe, die für meinen CUDA-Pfad-Tracer nicht geeignet ist, und ich möchte, dass sie so natürlich wie möglich ist. Haben Sie also ein Papier im Sinn, das nicht veraltet ist, oder würden Sie mir vorschlagen, die vollständige Strahlungsübertragungsgleichung zu lösen?

Mustafa Işık
quelle
1
Möchten Sie die Streuung unter der Oberfläche in Ihrem Pfad-Tracer abschätzen oder sie mithilfe der vollvolumetrischen Streuung genau bewerten?
RichieSams
Ich habe gelesen, dass die Simulation der vollvolumetrischen Streuung sehr langsam ist. Wenn es nicht so langsam ist, würde ich es gerne implementieren. Obwohl Diffusionsnäherungen schneller arbeiten, nehmen sie viele Dinge an oder benötigen hässliche Vorverarbeitungsschritte. Hast du es schon einmal gemacht? Jeder Vorschlag wäre toll!
Mustafa Işık
2
Dies kann je nach Implementierung langsam sein. Ich habe Brute-Force-isotrope Streumedien in meinen Hobby-Pfad-Tracer implementiert. Beispielbild: imgur.com/a/oy9F8 . (Die FPS / Gesamtbildzeiten befinden sich in der oberen linken Ecke). Wenn Sie eine Rückstreuphasenfunktion wie Henyey-Greenstein verwenden, konvergiert sie möglicherweise schneller für undurchsichtigere Materialien, aber ich bin mir nicht sicher, da ich sie nicht ausprobiert habe. Wenn nichts anderes, ist die Implementierung einer vollständigen volumetrischen Streuung eine gute Möglichkeit, um zu erfahren, was die Approximationen zu approximieren versuchen.
RichieSams
Ich werde später heute Abend versuchen, etwas zu schreiben, wenn ich nach Hause komme. In der Zwischenzeit ist hier der Link zu meinem Pfad-Tracer. github.com/RichieSams/lantern Es ist überhaupt nicht produktionsbereit. Es dient vielmehr nur dazu, mir zu helfen, die Mathematik hinter allem zu lernen. Das heißt, es ist Open Source unter Apache 2.0, also zögern Sie nicht, es selbst zu kopieren und daraus zu lernen.
RichieSams
1
Zuletzt sind hier zwei Fragen, die ich hier zu diesem Thema gestellt habe: computergraphics.stackexchange.com/questions/2482/… computergraphics.stackexchange.com/questions/2563/…
RichieSams

Antworten:

8

Wie in den Kommentaren erwähnt, würde ich dringend empfehlen, mit der vollständigen volumetrischen Streuung zu beginnen. Dies ist zweifach:

  1. Da Sie die Pfadverfolgung durchführen, ist das Hinzufügen von Volumetrics nicht besonders schwierig.
  2. Das vollständige Verständnis der Funktionsweise der vollständigen volumetrischen Streuung ist eine gute Grundlage für das Verständnis der Schätzungen. Darüber hinaus kann es hervorragende "Referenzen" liefern, um festzustellen, ob Ihre Schätzungen gut / korrekt funktionieren.

In diesem Sinne finden Sie im Folgenden eine grundlegende Einführung in die Implementierung der vollständigen volumetrischen Streuung in einem Rückwärtspfad-Tracer.


Schauen wir uns zunächst den Code für einen Rückwärtspfad-Tracer an, der nur Reflexion und keine Transmission / Refraktion enthält:

void RenderPixel(uint x, uint y, UniformSampler *sampler) {
    Ray ray = m_scene->Camera->CalculateRayFromPixel(x, y, sampler);

    float3 color(0.0f);
    float3 throughput(1.0f);
    SurfaceInteraction interaction;

    // Bounce the ray around the scene
    const uint maxBounces = 15;
    for (uint bounces = 0; bounces < maxBounces; ++bounces) {
        m_scene->Intersect(ray);

        // The ray missed. Return the background color
        if (ray.GeomID == INVALID_GEOMETRY_ID) {
            color += throughput * m_scene->BackgroundColor;
            break;
        }

        // Fetch the material
        Material *material = m_scene->GetMaterial(ray.GeomID);
        // The object might be emissive. If so, it will have a corresponding light
        // Otherwise, GetLight will return nullptr
        Light *light = m_scene->GetLight(ray.GeomID);

        // If this is the first bounce or if we just had a specular bounce,
        // we need to add the emmisive light
        if ((bounces == 0 || (interaction.SampledLobe & BSDFLobe::Specular) != 0) && light != nullptr) {
            color += throughput * light->Le();
        }

        interaction.Position = ray.Origin + ray.Direction * ray.TFar;
        interaction.Normal = normalize(m_scene->InterpolateNormal(ray.GeomID, ray.PrimID, ray.U, ray.V));
        interaction.OutputDirection = normalize(-ray.Direction);


        // Calculate the direct lighting
        color += throughput * SampleLights(sampler, interaction, material->bsdf, light);


        // Get the new ray direction
        // Choose the direction based on the bsdf        
        material->bsdf->Sample(interaction, sampler);
        float pdf = material->bsdf->Pdf(interaction);

        // Accumulate the weight
        throughput = throughput * material->bsdf->Eval(interaction) / pdf;

        // Shoot a new ray

        // Set the origin at the intersection point
        ray.Origin = interaction.Position;

        // Reset the other ray properties
        ray.Direction = interaction.InputDirection;
        ray.TNear = 0.001f;
        ray.TFar = infinity;


        // Russian Roulette
        if (bounces > 3) {
            float p = std::max(throughput.x, std::max(throughput.y, throughput.z));
            if (sampler->NextFloat() > p) {
                break;
            }

            throughput *= 1 / p;
        }
    }

    m_scene->Camera->FrameBufferData.SplatPixel(x, y, color);
}

Auf Englisch:

  1. Schieße einen Strahl durch die Szene.
  2. Überprüfen Sie, ob wir etwas getroffen haben. Wenn nicht, geben wir die Skybox-Farbe zurück und brechen ab.
  3. Wenn dies der erste Strahl ist oder wir gerade von einer spiegelnden Oberfläche abprallen, prüfen Sie, ob wir auf ein Licht treffen. In diesem Fall addieren wir die Lichtemission zu unserer Farbakkumulation.
  4. Probieren Sie die direkte Beleuchtung.
  5. Wählen Sie eine neue Richtung für den nächsten Strahl. Wir können dies einheitlich tun oder eine wichtige Stichprobe basierend auf dem BRDF.
  6. Bewerten Sie das BRDF und akkumulieren Sie es.
  7. Erstellen Sie einen neuen Strahl basierend auf unserer gewählten Richtung und woher wir gerade gekommen sind.
  8. [Optional] Verwenden Sie russisches Roulette, um zu entscheiden, ob der Strahl beendet werden soll.
  9. Gehe zu 1.

Weitere Informationen zur direkten Lichtabtastung finden Sie in dieser Antwort .

BSDFs

Um die Übertragung hinzuzufügen, müssen wir zuerst einige unserer BRDF-Materialien auf vollständige BSDFs mit einer BTDF-Funktion aktualisieren. BSDF-Erklärung

Zum Beispiel ist ein BSDF für ein ideales spiegelndes Dielektrikum (dh Glas):

class IdealSpecularDielectric : public BSDF {
public:
    IdealSpecularDielectric(float3 albedo, float ior)
        : BSDF(BSDFLobe::Specular, albedo), 
          m_ior(ior) {
    }

private:
    float m_ior;

public:
    float3 Eval(SurfaceInteraction &interaction) const override {
        return m_albedo;
    }

    void Sample(SurfaceInteraction &interaction, UniformSampler *sampler) const override {
        float VdotN = dot(interaction.OutputDirection, interaction.Normal);
        float IORo = m_ior;
        if (VdotN < 0.0f) {
            IORo = 1.0f;
            interaction.Normal = -interaction.Normal;
            VdotN = -VdotN;
        }

        float eta = interaction.IORi / IORo;
        float sinSquaredThetaT = SinSquaredThetaT(VdotN, eta);
        float fresnel = Fresnel(interaction.IORi, IORo, VdotN, sinSquaredThetaT);

        float rand = sampler->NextFloat();
        if (rand <= fresnel) {
            // Reflect
            interaction.InputDirection = reflect(interaction.OutputDirection, interaction.Normal);
            interaction.SampledLobe = BSDFLobe::SpecularReflection;
            interaction.IORo = interaction.IORi;
        } else {
            // Refract
            interaction.InputDirection = refract(interaction.OutputDirection, interaction.Normal, VdotN, eta, sinSquaredThetaT);
            interaction.SampledLobe = BSDFLobe::SpecularTransmission;
            interaction.IORo = IORo;
        }

        if (AnyNan(interaction.InputDirection)) {
            printf("nan");
        }
    }

    float Pdf(SurfaceInteraction &interaction) const override {
        return 1.0f;
    }
};

Der interessante Teil des Codes ist Sample(). Hier entscheidet ein Strahl, ob er reflektiert oder gebrochen wird. Wie wir das machen, liegt wirklich bei uns, wie in dieser Antwort gezeigt . Eine naheliegende Wahl wäre jedoch eine Stichprobe auf der Grundlage der Fresnel-Gleichungen .

Im Fall eines idealen Spiegeldielektrikums gibt es nur zwei mögliche Ausgangsrichtungen: perfekte Brechung oder perfekte Reflexion. Also bewerten wir den Fresnel und wählen zufällig die Brechung / Reflexion mit einem Anteil, der dem Fresnel entspricht.

Medien

Als nächstes müssen wir über Medien sprechen und wie sie sich auf das herumprallende Licht auswirken. Wie von Nathan Reed in dieser Antwort erklärt :

Ich denke gerne über Volumenstreuung nach, dass ein Photon, das sich durch ein Medium bewegt, eine bestimmte Wahrscheinlichkeit pro Längeneinheit der Wechselwirkung hat (gestreut oder absorbiert wird). Solange es nicht interagiert, verläuft es ungehindert und ohne Energieverlust in einer geraden Linie. Je größer die Entfernung ist, desto größer ist die Wahrscheinlichkeit, dass sie irgendwo in dieser Entfernung interagiert. Die Interaktionswahrscheinlichkeit pro Längeneinheit ist der Koeffizient , den Sie in den Gleichungen sehen. Wir haben normalerweise separate Koeffizienten für Streu- und Absorptionswahrscheinlichkeiten, alsoσσ=σs+σa

Diese Wahrscheinlichkeit pro Längeneinheit ist genau der Ursprung des Beer-Lambert-Gesetzes. Schneiden Sie ein Strahlensegment in infinitesimale Intervalle, behandeln Sie jedes Intervall als einen unabhängigen möglichen Ort für die Interaktion und integrieren Sie es dann entlang des Strahls. Sie erhalten eine Exponentialverteilung (mit dem Ratenparameter σσ) für die Wahrscheinlichkeit der Wechselwirkung als Funktion der Entfernung.

Sie können den Abstand zwischen Ereignissen technisch wählen, wie Sie möchten, solange Sie den Pfad korrekt gewichten, um die Wahrscheinlichkeit zu ermitteln, dass ein Photon es zwischen zwei benachbarten Ereignissen schaffen kann, ohne mit dem Medium zu interagieren. Mit anderen Worten, jedes Pfadsegment innerhalb des Mediums trägt einen Gewichtsfaktor von , wobei die Länge des Segments ist.eσxx

In Anbetracht dessen ist es normalerweise eine gute Wahl für die Entfernung, sie aus der Exponentialverteilung zu ermitteln. Mit anderen Worten, Sie setzenx=(lnξ)/σ

Zusammenfassend:

  1. Ein Strahl, der sich durch ein Medium bewegt, hat eine gewisse Wahrscheinlichkeit:
    • Sei absorbiert
    • Sei verstreut
  2. Da dies die Monte-Carlo-Integration ist, können wir den Interaktionsabstand frei wählen, wie wir möchten. Eine gute Wahl ist jedoch die Wichtigkeit einer Stichprobe aus einer Exponentialverteilung ähnlich wie bei Beer-Lambert.

Ich entschied mich dafür, dies mit einem Streukoeffizienten zu implementieren und Beer-Lambert den Absorptionskoeffizienten zu überlassen.

class Medium {
public:
    Medium(float3 absorptionColor, float absorptionAtDistance) 
        : m_absorptionCoefficient(-log(absorptionColor) / absorptionAtDistance) {
        // This method for calculating the absorption coefficient is borrowed from Burley's 2015 Siggraph Course Notes "Extending the Disney BRDF to a BSDF with Integrated Subsurface Scattering"
        // It's much more intutive to specify a color and a distance, then back-calculate the coefficient
    }
    virtual ~Medium() {
    }

protected:
    const float3a m_absorptionCoefficient;

public:
    virtual float SampleDistance(UniformSampler *sampler, float tFar, float *weight, float *pdf) const = 0;

    virtual float3a SampleScatterDirection(UniformSampler *sampler, float3a &wo, float *pdf) const = 0;
    virtual float ScatterDirectionPdf(float3a &wi, float3a &wo) const = 0;

    virtual float3 Transmission(float distance) const = 0;
};

Ein nicht streuendes Medium ist ein Medium, durch das sich ein Photon gerade bewegt, das jedoch nach Beer-Lambert auf dem Weg abgeschwächt wird:

class NonScatteringMedium : public Medium {
public:
    NonScatteringMedium(float3 color, float atDistance)
        : Medium(color, atDistance) {
    }

public:
    float SampleDistance(UniformSampler *sampler, float tFar, float *weight, float *pdf) const override {
        *pdf = 1.0f;
        return tFar;
    }

    float3a SampleScatterDirection(UniformSampler *sampler, float3a &wo, float *pdf) const override {
        return wo;
    }
    float ScatterDirectionPdf(float3a &wi, float3a &wo) const override {
        return 1.0f;
    }

    float3 Transmission(float distance) const override {
        return exp(-m_absorptionCoefficient * distance);
    }
};

Nicht streuendes Beispiel

Ein isotropes Streumedium ermöglicht es dem Strahl, Streuereignisse (Reflexionsereignisse) zu haben, während der Strahl durch ihn wandert. Der Strahl kann in jeder Richtung in der Kugel um den Wechselwirkungspunkt reflektiert werden (daher isotrop).

Erklärung der isotropen Streuung

Der Abstand zwischen Reflexionen ist zufällig, basierend auf einer exponentiellen "Streu" -Wahrscheinlichkeit.

class IsotropicScatteringMedium : public Medium {
public:
    IsotropicScatteringMedium(float3 absorptionColor, float absorptionAtDistance, float scatteringDistance)
        : Medium(absorptionColor, absorptionAtDistance),
          m_scatteringCoefficient(1 / scatteringDistance) {
    }

private:
    float m_scatteringCoefficient;

public:
    float SampleDistance(UniformSampler *sampler, float tFar, float *weight, float *pdf) const override {
        float distance = -logf(sampler->NextFloat()) / m_scatteringCoefficient;
        // If we sample a distance farther than the next intersecting surface, clamp to the surface distance
        if (distance >= tFar) {
            *pdf = 1.0f;
            return tFar;
        }

        *pdf = std::exp(-m_scatteringCoefficient * distance);
        return distance;
    }
    float3a SampleScatterDirection(UniformSampler *sampler, float3a &wo, float *pdf) const override {
        *pdf = 0.25f * M_1_PI; // 1 / (4 * PI)
        return UniformSampleSphere(sampler);
    }

    float ScatterDirectionPdf(float3a &wi, float3a &wo) const override {
        return 0.25f * M_1_PI; // 1 / (4 * PI)
    }
    float3 Transmission(float distance) const override {
        return exp(-m_absorptionCoefficient * distance);
    }
};

Bei hohen Streuabständen verhält sich das Material fast wie ein NonScatteringMedium, da es eine sehr geringe Streuwahrscheinlichkeit aufweist, bevor es sich durch das Medium bewegt.

Hohe Streuentfernung

Bei geringen Streuabständen verhält sich das Material wie Wachs oder Jade.

Geringe Streuentfernung

Wenn der Streuabstand immer kleiner wird, haben wir eine immer höhere Wahrscheinlichkeit, auf dem Weg durch das Medium zu streuen. Dies wirft einige Probleme auf:

  1. Die Anzahl der Bounces pro Strahl wird explodieren.
  2. Wenn russisches Roulette den Strahl tötet oder wenn Sie "maxBounces" überschreiten, "verlieren" Sie jeden Farbbeitrag aus dem Medium heraus. Über Max springt Beispiel

Daher müssen Sie maxBounces auf eine hohe Zahl einstellen und sich im allgemeinen Fall auf russisches Roulette verlassen, um Strahlen zu beenden. Darüber hinaus ist IsotropicScatteringMedium für die Darstellung meist undurchsichtiger Materialien äußerst ineffizient. Um eine bessere Leistung zu erzielen, sollten wir eine nicht-isotrope Phasenfunktion wie Henyey-Greenstein oder Schlick verwenden . Diese spannen die Streuung in eine bestimmte Richtung vor. So könnten wir sie auf eine hohe Rückstreuung einstellen, wodurch das Problem der "verlorenen" Strahlen verringert wird.

Alles zusammenfügen

Wie modifizieren wir mit diesen neuen Informationen den Integrator, um BSDFs und Medien zu verstehen?

Zuerst müssen wir verfolgen, in welchem ​​Medium wir uns gerade befinden und welchen Brechungsindex (IOR).

SurfaceInteraction interaction;
interaction.IORi = 1.0f; // Vacuum 
Medium *medium = nullptr; // nullptr == vacuum

Dann teilen wir den Integrator in zwei Teile: Übertragung und Materialinteraktion.

void RenderPixel(uint x, uint y, UniformSampler *sampler) const {
    Ray ray = m_scene->Camera->CalculateRayFromPixel(x, y, sampler);

    float3 color(0.0f);
    float3 throughput(1.0f);
    SurfaceInteraction interaction;
    interaction.IORi = 1.0f; // Air
    Medium *medium = nullptr;
    bool hitSurface = false;

    // Bounce the ray around the scene
    uint bounces = 0;
    const uint maxBounces = 1500;
    for (; bounces < maxBounces; ++bounces) {
        m_scene->Intersect(ray);

        // The ray missed. Return the background color
        if (ray.GeomID == INVALID_GEOMETRY_ID) {
            color += throughput * m_scene->BackgroundColor;
            break;
        }

        // We hit an object
        hitSurface = true;

        // Calculate any transmission
        if (medium != nullptr) {
            float weight = 1.0f;
            float pdf = 1.0f;
            float distance = medium->SampleDistance(sampler, ray.TFar, &weight, &pdf);
            float3 transmission = medium->Transmission(distance);
            throughput = throughput * weight * transmission;

            if (distance < ray.TFar) {
                // Create a scatter event
                hitSurface = false;

                ray.Origin = ray.Origin + ray.Direction * distance;

                // Reset the other ray properties
                float directionPdf;
                float3a wo = normalize(ray.Direction);
                ray.Direction = medium->SampleScatterDirection(sampler, wo, &directionPdf);
                ray.TNear = 0.001f;
                ray.TFar = infinity;
                ray.GeomID = INVALID_GEOMETRY_ID;
                ray.PrimID = INVALID_PRIMATIVE_ID;
                ray.InstID = INVALID_INSTANCE_ID;
                ray.Mask = 0xFFFFFFFF;
                ray.Time = 0.0f;
            }
        }

        if (hitSurface) {
            // Fetch the material
            Material *material = m_scene->GetMaterial(ray.GeomID);
            // The object might be emissive. If so, it will have a corresponding light
            // Otherwise, GetLight will return nullptr
            Light *light = m_scene->GetLight(ray.GeomID);

            // If this is the first bounce or if we just had a specular bounce,
            // we need to add the emmisive light
            if ((bounces == 0 || (interaction.SampledLobe & BSDFLobe::Specular) != 0) && light != nullptr) {
                color += throughput * light->Le();
            }

            interaction.Position = ray.Origin + ray.Direction * ray.TFar;
            interaction.Normal = normalize(m_scene->InterpolateNormal(ray.GeomID, ray.PrimID, ray.U, ray.V));
            interaction.OutputDirection = normalize(-ray.Direction);
            interaction.IORo = 0.0f;


            // Calculate the direct lighting
            color += throughput * SampleOneLight(sampler, interaction, material->bsdf, light);


            // Get the new ray direction
            // Choose the direction based on the bsdf        
            material->bsdf->Sample(interaction, sampler);
            float pdf = material->bsdf->Pdf(interaction);

            // Accumulate the weight
            throughput = throughput * material->bsdf->Eval(interaction) / pdf;

            // Update the current IOR and medium if we refracted
            if (interaction.SampledLobe == BSDFLobe::SpecularTransmission) {
                interaction.IORi = interaction.IORo;
                medium = material->medium;
            }

            // Shoot a new ray

            // Set the origin at the intersection point
            ray.Origin = interaction.Position;

            // Reset the other ray properties
            ray.Direction = interaction.InputDirection;
            if (AnyNan(ray.Direction))
                printf("bad");
            ray.TNear = 0.001f;
            ray.TFar = infinity;
            ray.GeomID = INVALID_GEOMETRY_ID;
            ray.PrimID = INVALID_PRIMATIVE_ID;
            ray.InstID = INVALID_INSTANCE_ID;
            ray.Mask = 0xFFFFFFFF;
            ray.Time = 0.0f;
        }

        // Russian Roulette
        if (bounces > 3) {
            float p = std::max(throughput.x, std::max(throughput.y, throughput.z));
            if (sampler->NextFloat() > p) {
                break;
            }

            throughput *= 1 / p;
        }
    }

    if (bounces == maxBounces) {
        printf("Over max bounces");
    }

    m_scene->Camera->FrameBufferData.SplatPixel(x, y, color);
}

Auf Englisch:

  1. Schieße einen Strahl durch die Szene.
  2. Überprüfen Sie, ob wir etwas getroffen haben. Wenn nicht, geben wir die Skybox-Farbe zurück und brechen ab.
  3. Überprüfen Sie, ob wir uns derzeit in einem Medium befinden (nicht in einem Vakuum).
    1. Probieren Sie das Medium für eine Entfernung.
    2. Bewerten Sie die Übertragung des Mediums und akkumulieren Sie den Durchsatz.
    3. Wenn der Streuabstand kleiner als der Abstand vom Strahlursprung zur nächsten Oberfläche ist, führen Sie ein Streuereignis durch.
    4. Probieren Sie das Medium für eine Streurichtung.
  4. Überprüfen Sie, ob wir auf eine Oberfläche treffen (dh wir hatten kein Streuereignis).
    1. Wenn dies der erste Strahl ist oder wir gerade von einer spiegelnden Oberfläche abprallen, prüfen Sie, ob wir auf ein Licht treffen. In diesem Fall addieren wir die Lichtemission zu unserer Farbakkumulation.
    2. Probieren Sie die direkte Beleuchtung.
    3. Wählen Sie eine neue Richtung für den nächsten Strahl. Wir können dies einheitlich tun oder eine wichtige Stichprobe basierend auf dem BRDF.
    4. Bewerten Sie das BRDF und akkumulieren Sie es.
    5. Wenn wir das Material gebrochen haben, aktualisieren Sie die IOR und das Medium für den nächsten Sprung.
  5. Erstellen Sie einen neuen Strahl basierend auf unserer gewählten Richtung und woher wir gerade gekommen sind.
  6. [Optional] Verwenden Sie russisches Roulette, um zu entscheiden, ob der Strahl beendet werden soll.
  7. Gehe zu 1.
RichieSams
quelle
Vielen Dank für diese erstaunliche Antwort! Das Problem ist, dass meine Ausgaben, egal was ich für die Koeffizienten versuche, wie ein Glas aussehen. Bedeutet das, dass ich einen Teil der Streuung falsch mache? Hier ist die Reihenfolge der Ausgaben: imgur.com/a/gg2u1 Ich habe nur den Streukoeffizienten geändert, während die Absorption gleich blieb
Mustafa Işık
1
Sind Sie sicher, dass Sie den Koeffizienten nicht versehentlich gleich der Streuentfernung einstellen? Es sollte ScatteringCoefficient = (1 / ScatteringDistance) sein. Auch Absorptionskoeffizient = -log (Absorptionsfarbe) / Absorptionsabstand. Am Ende spielt es keine Rolle, wie Sie auf den Koeffizienten kommen. Ein niedrigerer Koeffizient bedeutet jedoch weniger Absorption / Streuung. Ein höherer Koeffizient bedeutet weniger. Für Künstler ist es etwas einfacher, sich diese als Entfernungen vorzustellen, daher berechnen wir die Koeffizienten aus Entfernungen zurück.
RichieSams
Nein, der Parametereinstellungsteil ist der gleiche. Ich konnte das Problem nicht lösen, aber wenn ich es löse, werde ich hier einen Kommentar abgeben.
Mustafa Işık
4

In Fällen, in denen die Diffusionsnäherung der vollständigen volumetrischen Pfadverfolgung vorgezogen wird, ist die von Solid Angle veröffentlichte Methode ziemlich effizient: https://www.solidangle.com/research/s2013_bssrdf_slides.pdf

Es ist in der Arnold-Render-Engine, Blender's Cycles und pbrt implementiert, wobei letzteres Open Source ist.

Die Datei, die es in PBRT implementiert, befindet sich hier: https://github.com/mmp/pbrt-v3/blob/7095acfd8331cdefb03fe0bcae32c2fc9bd95980/src/core/bssrdf.cpp

Stefan Werner
quelle
Die PBRT-Implementierung scheint eine Photonenstrahldiffusion zu sein. Sind Photonenstrahldiffusion und der Festkörperwinkel dasselbe? Ich frage, weil ich dachte, dass PBD auch Punktproben auf der Geometrie als Vorverarbeitungsschritt nimmt, aber Vollwinkel-Folien tun dies nicht.
Mustafa Işık
1
Hier sind zwei Dinge zu unterscheiden: Die zu integrierende Funktion (BSSRDF) und die Integrationsmethode. Das erste sind Funktionen wie Dipol, Multipol, Photonenstrahldiffusion usw. Das zweite sind Methoden wie Jensens hierarchisches Octree oder Solid Angles wichtige Abtaststrahlverfolgung.
Stefan Werner