subsurface.cpp 9.4 KB
Newer Older
M
Matt Pharr 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// pbrt is Copyright(c) 1998-2020 Matt Pharr, Wenzel Jakob, and Greg Humphreys.
// The pbrt source code is licensed under the Apache License, Version 2.0.
// SPDX: Apache-2.0

#include <pbrt/pbrt.h>

#include <pbrt/bssrdf.h>
#include <pbrt/gpu/accel.h>
#include <pbrt/gpu/launch.h>
#include <pbrt/gpu/pathintegrator.h>
#include <pbrt/interaction.h>
#include <pbrt/lightsamplers.h>
#include <pbrt/samplers.h>
#include <pbrt/util/sampling.h>
#include <pbrt/util/spectrum.h>

namespace pbrt {

19
// GPUPathIntegrator Subsurface Scattering Methods
M
Matt Pharr 已提交
20
void GPUPathIntegrator::SampleSubsurface(int depth) {
21 22 23
    RayQueue *rayQueue = CurrentRayQueue(depth);
    RayQueue *nextRayQueue = NextRayQueue(depth);

M
Matt Pharr 已提交
24 25
    ForAllQueued(
        "Get BSSRDF and enqueue probe ray", bssrdfEvalQueue, maxQueueSize,
26
        PBRT_GPU_LAMBDA(const GetBSSRDFAndProbeRayWorkItem be, int index) {
M
Matt Pharr 已提交
27 28 29 30 31 32 33
            using BSSRDF = typename SubsurfaceMaterial::BSSRDF;
            BSSRDF bssrdf;
            const SubsurfaceMaterial *material = be.material.Cast<SubsurfaceMaterial>();
            MaterialEvalContext ctx = be.GetMaterialEvalContext();
            SampledWavelengths lambda = be.lambda;
            material->GetBSSRDF(BasicTextureEvaluator(), ctx, lambda, &bssrdf);

34
            RaySamples raySamples = pixelSampleState.samples[be.pixelIndex];
M
Matt Pharr 已提交
35 36 37
            Float uc = raySamples.subsurface.uc;
            Point2f u = raySamples.subsurface.u;

M
Matt Pharr 已提交
38
            pstd::optional<BSSRDFProbeSegment> probeSeg = bssrdf.SampleSp(uc, u);
M
Matt Pharr 已提交
39
            if (probeSeg)
40 41 42
                subsurfaceScatterQueue->Push(
                    probeSeg->p0, probeSeg->p1, material, bssrdf, lambda, be.beta,
                    be.uniPathPDF, be.mediumInterface, be.etaScale, be.pixelIndex);
M
Matt Pharr 已提交
43 44
        });

45
    accel->IntersectOneRandom(maxQueueSize, subsurfaceScatterQueue);
M
Matt Pharr 已提交
46 47 48

    ForAllQueued(
        "Handle out-scattering after SSS", subsurfaceScatterQueue, maxQueueSize,
49
        PBRT_GPU_LAMBDA(SubsurfaceScatterWorkItem s, int index) {
M
Matt Pharr 已提交
50 51 52 53 54 55 56 57 58 59 60
            if (s.weight == 0)
                return;

            using BSSRDF = TabulatedBSSRDF;
            BSSRDF bssrdf = s.bssrdf;
            using BxDF = typename BSSRDF::BxDF;
            BxDF bxdf;

            SubsurfaceInteraction &intr = s.ssi;
            BSSRDFSample bssrdfSample = bssrdf.ProbeIntersectionToSample(intr, &bxdf);

M
Matt Pharr 已提交
61
            if (!bssrdfSample.Sp || bssrdfSample.pdf == 0)
M
Matt Pharr 已提交
62 63
                return;

64 65
            SampledSpectrum betap =
                s.beta * bssrdfSample.Sp * s.weight / bssrdfSample.pdf;
66
            SampledWavelengths lambda = s.lambda;
67
            RaySamples raySamples = pixelSampleState.samples[s.pixelIndex];
M
Matt Pharr 已提交
68
            Vector3f wo = bssrdfSample.wo;
M
Matt Pharr 已提交
69
            BSDF &bsdf = bssrdfSample.Sw;
M
Matt Pharr 已提交
70 71 72 73 74 75 76 77 78 79
            Float time = 0;  // TODO: pipe through

            // NOTE: the remainder is copied from the Material/BSDF eval method.
            // Will unify into shared fragments in the book...

            // Indirect...
            {
                Point2f u = raySamples.indirect.u;
                Float uc = raySamples.indirect.uc;

80 81 82 83
                pstd::optional<BSDFSample> bsdfSample = bsdf.Sample_f<BxDF>(wo, uc, u);
                if (bsdfSample) {
                    Vector3f wi = bsdfSample->wi;
                    SampledSpectrum beta = betap * bsdfSample->f * AbsDot(wi, intr.ns);
84
                    SampledSpectrum uniPathPDF = s.uniPathPDF, lightPathPDF = uniPathPDF;
M
Matt Pharr 已提交
85

86 87 88 89
                    PBRT_DBG("%s f*cos[0] %f bsdfSample->pdf %f f*cos/pdf %f\n",
                             BxDF::Name(), bsdfSample->f[0] * AbsDot(wi, intr.ns),
                             bsdfSample->pdf,
                             bsdfSample->f[0] * AbsDot(wi, intr.ns) / bsdfSample->pdf);
M
Matt Pharr 已提交
90

91
                    if (bsdfSample->pdfIsProportional) {
M
Matt Pharr 已提交
92
                        Float pdf = bsdf.PDF(wo, wi);
93
                        beta *= pdf / bsdfSample->pdf;
94
                        uniPathPDF *= pdf;
95
                        PBRT_DBG("Sampled PDF is proportional: pdf %f\n", pdf);
M
Matt Pharr 已提交
96
                    } else
97
                        uniPathPDF *= bsdfSample->pdf;
M
Matt Pharr 已提交
98

99
                    Float etaScale = s.etaScale;
100
                    if (bsdfSample->IsTransmission())
M
Matt Pharr 已提交
101 102 103
                        etaScale *= Sqr(bsdf.eta);

                    // Russian roulette
104
                    SampledSpectrum rrBeta = beta * etaScale / uniPathPDF.Average();
M
Matt Pharr 已提交
105 106 107 108
                    if (rrBeta.MaxComponentValue() < 1 && depth > 1) {
                        Float q = std::max<Float>(0, 1 - rrBeta.MaxComponentValue());
                        if (raySamples.indirect.rr < q) {
                            beta = SampledSpectrum(0.f);
109
                            PBRT_DBG("Path terminated with RR\n");
M
Matt Pharr 已提交
110
                        }
111 112
                        uniPathPDF *= 1 - q;
                        lightPathPDF *= 1 - q;
M
Matt Pharr 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126
                    }

                    if (beta) {
                        Ray ray = SpawnRay(intr.pi, intr.n, time, wi);
                        if (haveMedia)
                            // TODO: should always just take outside in this case?
                            ray.medium = Dot(ray.d, intr.n) > 0
                                             ? s.mediumInterface.outside
                                             : s.mediumInterface.inside;

                        // || rather than | is intentional, to avoid the read if
                        // possible...
                        bool anyNonSpecularBounces = true;

M
Matt Pharr 已提交
127 128 129 130
                        LightSampleContext ctx(intr.pi, intr.n, intr.ns);
                        nextRayQueue->PushIndirect(ray, ctx, beta, uniPathPDF,
                                                   lightPathPDF, lambda, etaScale,
                                                   bsdfSample->IsSpecular(),
131
                                                   anyNonSpecularBounces, s.pixelIndex);
M
Matt Pharr 已提交
132

133 134 135 136
                        PBRT_DBG(
                            "Spawned indirect ray at depth %d. "
                            "Specular %d Beta %f %f %f %f uniPathPDF %f %f %f %f "
                            "lightPathPDF %f "
M
Matt Pharr 已提交
137
                            "%f %f %f "
138
                            "beta/uniPathPDF %f %f %f %f\n",
139 140 141 142 143
                            depth + 1, int(bsdfSample->IsSpecular()), beta[0], beta[1],
                            beta[2], beta[3], uniPathPDF[0], uniPathPDF[1], uniPathPDF[2],
                            uniPathPDF[3], lightPathPDF[0], lightPathPDF[1],
                            lightPathPDF[2], lightPathPDF[3],
                            SafeDiv(beta, uniPathPDF)[0], SafeDiv(beta, uniPathPDF)[1],
144
                            SafeDiv(beta, uniPathPDF)[2], SafeDiv(beta, uniPathPDF)[3]);
M
Matt Pharr 已提交
145 146 147 148 149
                    }
                }
            }

            // Direct lighting...
150
            if (bsdf.IsNonSpecular()) {
M
Matt Pharr 已提交
151 152 153
                LightSampleContext ctx(intr.pi, intr.n, intr.ns);
                pstd::optional<SampledLight> sampledLight =
                    lightSampler.Sample(ctx, raySamples.direct.uc);
154
                if (!sampledLight)
M
Matt Pharr 已提交
155
                    return;
156
                LightHandle light = sampledLight->light;
M
Matt Pharr 已提交
157

158 159
                pstd::optional<LightLiSample> ls = light.SampleLi(
                    ctx, raySamples.direct.u, lambda, LightSamplingMode::WithMIS);
160
                if (!ls || !ls->L || ls->pdf == 0)
M
Matt Pharr 已提交
161 162
                    return;

163
                Vector3f wi = ls->wi;
M
Matt Pharr 已提交
164 165 166 167 168 169
                SampledSpectrum f = bsdf.f<BxDF>(wo, wi);
                if (!f)
                    return;

                SampledSpectrum beta = betap * f * AbsDot(wi, intr.ns);

170 171
                PBRT_DBG(
                    "depth %d beta %f %f %f %f f %f %f %f %f ls.L %f %f %f %f ls.pdf "
M
Matt Pharr 已提交
172 173
                    "%f\n",
                    depth, beta[0], beta[1], beta[2], beta[3], f[0], f[1], f[2], f[3],
174
                    ls->L[0], ls->L[1], ls->L[2], ls->L[3], ls->pdf);
M
Matt Pharr 已提交
175

176
                Float lightPDF = ls->pdf * sampledLight->pdf;
177
                // This causes uniPathPDF to be zero for the shadow ray, so that
M
Matt Pharr 已提交
178 179
                // part of MIS just becomes a no-op.
                Float bsdfPDF = IsDeltaLight(light.Type()) ? 0.f : bsdf.PDF<BxDF>(wo, wi);
180 181
                SampledSpectrum uniPathPDF = s.uniPathPDF * bsdfPDF;
                SampledSpectrum lightPathPDF = s.uniPathPDF * lightPDF;
M
Matt Pharr 已提交
182

183
                SampledSpectrum Ld = SafeDiv(beta * ls->L, lambda.PDF());
M
Matt Pharr 已提交
184

185
                PBRT_DBG("depth %d Ld %f %f %f %f "
186 187 188 189 190 191 192
                         "new beta %f %f %f %f beta/uni %f %f %f %f Ld/uni %f %f %f %f\n",
                         depth, Ld[0], Ld[1], Ld[2], Ld[3], beta[0], beta[1], beta[2],
                         beta[3], SafeDiv(beta, uniPathPDF)[0],
                         SafeDiv(beta, uniPathPDF)[1], SafeDiv(beta, uniPathPDF)[2],
                         SafeDiv(beta, uniPathPDF)[3], SafeDiv(Ld, uniPathPDF)[0],
                         SafeDiv(Ld, uniPathPDF)[1], SafeDiv(Ld, uniPathPDF)[2],
                         SafeDiv(Ld, uniPathPDF)[3]);
M
Matt Pharr 已提交
193

194
                Ray ray = SpawnRayTo(intr.pi, intr.n, time, ls->pLight.pi, ls->pLight.n);
M
Matt Pharr 已提交
195 196 197 198 199
                if (haveMedia)
                    // TODO: as above, always take outside here?
                    ray.medium = Dot(ray.d, intr.n) > 0 ? s.mediumInterface.outside
                                                        : s.mediumInterface.inside;

200 201
                shadowRayQueue->Push(ray, 1 - ShadowEpsilon, lambda, Ld, uniPathPDF,
                                     lightPathPDF, s.pixelIndex);
M
Matt Pharr 已提交
202 203 204 205 206 207 208
            }
        });

    TraceShadowRays(depth);
}

}  // namespace pbrt