Example #1
0
GaussQuadratureTetrahedron::GaussQuadratureTetrahedron(size_t order)
{
	GaussLegendre quadRule = GaussLegendre(order);
	GaussJacobi10 quadRule10 = GaussJacobi10(order);
	GaussJacobi20 quadRule20 = GaussJacobi20(order);

	m_order = std::min(quadRule.order(), std::min(quadRule10.order(), quadRule20.order()));
	m_numPoints = quadRule.size() * quadRule10.size() * quadRule20.size();
	position_type* pvPoint = new position_type[m_numPoints];
	weight_type* pvWeight = new weight_type[m_numPoints];

	size_t cnt = 0;
	for(size_t i = 0; i < quadRule20.size(); i++) {
		for(size_t j = 0; j < quadRule10.size(); j++) {
			for(size_t k = 0; k < quadRule.size(); k++, cnt++) {
				pvPoint[cnt][0] = quadRule20.point(i)[0];
				pvPoint[cnt][1] = (1.0 - quadRule20.point(i)[0] ) * quadRule10.point(j)[0];
				pvPoint[cnt][2] = (1.0 - quadRule20.point(i)[0]) * (1.0 - quadRule10.point(j)[0]) * quadRule.point(k)[0];
				pvWeight[cnt] = quadRule20.weight(i) * quadRule10.weight(j) * quadRule.weight(k);
			}
		}
	}
	m_pvPoint = pvPoint;
	m_pvWeight = pvWeight;
};
Example #2
0
 /**
  * Integrate down the centre of this unknown
  * This will not take into account changes
  * in the jacobian across the extent of the basis
  * function
  */
 static
 typename UnknownT::NumberT
 integrate(
     const UnknownT& unknown,
     const CoordinatesInterface<DIM,
         typename UnknownT::NumberT>& geom,
     const int direction)
 {
     GaussLegendre<UnknownT::ORDER, NumberT> quad;
     ValueT integral(0.0);
     for (auto interval1D :
          unknown.linearIntervals1D(direction)) {
         NumberT intervalMin = interval1D.minimum(0);
         NumberT intervalMax = interval1D.maximum(0);
         auto nodes = quad.nodes(intervalMin, intervalMax);
         auto weights = quad.weights(intervalMin, intervalMax);
         auto location = unknown.centres();
         for (std::size_t j = 0; j < nodes.size(); ++j) {
             location[direction] = nodes[j];
             integral += weights[j] *
                     unknown.value(direction, nodes[j]) *
                     geom.jacobian(direction, nodes[j]);
         }
     }
     return integral;
 }
Example #3
0
    static
    typename UnknownT::NumberT
    integrateComplex(
        const UnknownT& unknown,
        const CoordinatesInterface<DIM,
            typename UnknownT::NumberT>& geom,
        const std::vector<int>& directions
        )
    {
        static const int QUAD_ORDER = UnknownT::ORDER + 1;
        static const int NUM = GaussLegendre<QUAD_ORDER, NumberT>::NUM;
        assert(DIM > 1);
        // integrate the the copy along direction
        GaussLegendre<QUAD_ORDER, NumberT> quad;
        std::array<int, DIM> indexSizes;
        indexSizes.fill(int(quad.size()));
        auto indices = MathsUtilities::allPermutations<DIM>(indexSizes);
        // The integral of the unknown in the directions
        // we to integrate in
        ValueT integral(0.0);
        for (auto interval : unknown.intervals()) {
            auto minima = interval.minima();
            auto maxima = interval.maxima();
            auto nodes = QuadratureOps<DIM, QUAD_ORDER, NumberT>::
                    nodes(quad, minima, maxima);
            auto weights = QuadratureOps<DIM, QUAD_ORDER, NumberT>::
                    weights(quad, minima, maxima);
            for (auto index : indices) {
                auto locationArray = ContainerUtilities::
                        select<DIM, NUM, NumberT>(nodes, index);
                auto allWeights = ContainerUtilities::
                        select<DIM, NUM, NumberT>(weights, index);
                Vector<DIM, NumberT> location(locationArray);
                assert(interval.isInside(location));

                auto directionWeights = ContainerUtilities::
                        select<DIM, NumberT>(allWeights, directions);

                auto weight = ContainerUtilities::
                        product<NumberT>(directionWeights);

                integral += weight *
                        unknown.value(directions, location) *
                        geom.jacobian(Contravariant<DIM, NumberT>(location));
            }
        }
        static_assert(NUM > 0, "NUM must be greater than zero");
        return integral / NUM;
    }
Example #4
0
 static
 typename std::array<typename GaussLegendre<ORDER, T>::NodesAndWeights, DIM>
 nodesWeightsPairs(
     const Vector<DIM, T>& mins,
     const Vector<DIM, T>& maxs
     )
 {
     GaussLegendre<ORDER, T> quad;
     std::array<GaussLegendre<ORDER, T>, DIM> allNodesAndWeights;
     for (int i = 0; i < DIM; ++i) {
         assert(maxs[i] > mins[i]);
         allNodesAndWeights[i] = quad.weights(mins[i], maxs[i]);
     }
     return allNodesAndWeights;
 }
Example #5
0
GaussQuadratureQuadrilateral::GaussQuadratureQuadrilateral(size_t order)
{
	GaussLegendre quadRule = GaussLegendre(order);

	m_order = std::min(quadRule.order(), quadRule.order());
	m_numPoints = quadRule.size() * quadRule.size();
	position_type* pvPoint = new position_type[m_numPoints];
	weight_type* pvWeight = new weight_type[m_numPoints];

	size_t cnt  = 0;
	for(size_t i = 0; i < quadRule.size(); i ++) {
		for(size_t j = 0; j < quadRule.size(); j++, cnt++) {
			pvPoint[cnt][0] = quadRule.point(i)[0];
			pvPoint[cnt][1] = quadRule.point(j)[0];
			pvWeight[cnt] = quadRule.weight(i) * quadRule.weight(j);
		}
	}
	m_pvPoint = pvPoint;
	m_pvWeight = pvWeight;
};
Example #6
0
 static
 std::array<std::array<T, ORDER+1>, DIM>
 weights(const GaussLegendre<ORDER, T>& quad,
         const Vector<DIM, T>& mins,
         const Vector<DIM, T>& maxs)
 {
     std::array<std::array<T, ORDER+1>, DIM> allWeights;
     for (uint i = 0; i < DIM; ++i) {
         assert(maxs[i] > mins[i]);
         allWeights[i] = quad.weights(mins[i], maxs[i]);
     }
     return allWeights;
 }
Example #7
0
void HairBcsdf::precomputeAzimuthalDistributions()
{
    const int Resolution = PrecomputedAzimuthalLobe::AzimuthalResolution;
    std::unique_ptr<Vec3f[]> valuesR  (new Vec3f[Resolution*Resolution]);
    std::unique_ptr<Vec3f[]> valuesTT (new Vec3f[Resolution*Resolution]);
    std::unique_ptr<Vec3f[]> valuesTRT(new Vec3f[Resolution*Resolution]);

    // Ideally we could simply make this a constexpr, but MSVC does not support that yet (boo!)
    #define NumPoints 140

    GaussLegendre<NumPoints> integrator;
    const auto points = integrator.points();
    const auto weights = integrator.weights();

    // Cache the gammaI across all integration points
    std::array<float, NumPoints> gammaIs;
    for (int i = 0; i < NumPoints; ++i)
        gammaIs[i] = std::asin(points[i]);

    // Precompute the Gaussian detector and sample it into three 1D tables.
    // This is the only part of the precomputation that is actually approximate.
    // 2048 samples are enough to support the lowest roughness that the BCSDF
    // can reliably simulate
    const int NumGaussianSamples = 2048;
    std::unique_ptr<float[]> Ds[3];
    for (int p = 0; p < 3; ++p) {
        Ds[p].reset(new float[NumGaussianSamples]);
        for (int i = 0; i < NumGaussianSamples; ++i)
            Ds[p][i] = D(_betaR, i/(NumGaussianSamples - 1.0f)*TWO_PI);
    }

    // Simple wrapped linear interpolation of the precomputed table
    auto approxD = [&](int p, float phi) {
        float u = std::abs(phi*(INV_TWO_PI*(NumGaussianSamples - 1)));
        int x0 = int(u);
        int x1 = x0 + 1;
        u -= x0;
        return Ds[p][x0 % NumGaussianSamples]*(1.0f - u) + Ds[p][x1 % NumGaussianSamples]*u;
    };

    // Here follows the actual precomputation of the azimuthal scattering functions
    // The scattering functions are parametrized with the azimuthal angle phi,
    // and the cosine of the half angle, cos(thetaD).
    // This parametrization makes the azimuthal function relatively smooth and allows using
    // really low resolutions for the table (64x64 in this case) without any visual
    // deviation from ground truth, even at the lowest supported roughness setting
    for (int y = 0; y < Resolution; ++y) {
        float cosHalfAngle = y/(Resolution - 1.0f);

        // Precompute reflection Fresnel factor and reduced absorption coefficient
        float iorPrime = std::sqrt(Eta*Eta - (1.0f - cosHalfAngle*cosHalfAngle))/cosHalfAngle;
        float cosThetaT = std::sqrt(1.0f - (1.0f - cosHalfAngle*cosHalfAngle)*sqr(1.0f/Eta));
        Vec3f sigmaAPrime = _sigmaA/cosThetaT;

        // Precompute gammaT, f_t and internal absorption across all integration points
        std::array<float, NumPoints> fresnelTerms, gammaTs;
        std::array<Vec3f, NumPoints> absorptions;
        for (int i = 0; i < NumPoints; ++i) {
            gammaTs[i] = std::asin(clamp(points[i]/iorPrime, -1.0f, 1.0f));
            fresnelTerms[i] = Fresnel::dielectricReflectance(1.0f/Eta, cosHalfAngle*std::cos(gammaIs[i]));
            absorptions[i] = std::exp(-sigmaAPrime*2.0f*std::cos(gammaTs[i]));
        }

        for (int phiI = 0; phiI < Resolution; ++phiI) {
            float phi = TWO_PI*phiI/(Resolution - 1.0f);

            float integralR = 0.0f;
            Vec3f integralTT(0.0f);
            Vec3f integralTRT(0.0f);

            // Here follows the integration across the fiber width, h.
            // Since we were able to precompute most of the factors that
            // are constant w.r.t phi for a given h,
            // we don't have to do much work here.
            for (int i = 0; i < integrator.numSamples(); ++i) {
                float fR = fresnelTerms[i];
                Vec3f T = absorptions[i];

                float AR = fR;
                Vec3f ATT = (1.0f - fR)*(1.0f - fR)*T;
                Vec3f ATRT = ATT*fR*T;

                integralR   += weights[i]*approxD(0, phi - Phi(gammaIs[i], gammaTs[i], 0))*AR;
                integralTT  += weights[i]*approxD(1, phi - Phi(gammaIs[i], gammaTs[i], 1))*ATT;
                integralTRT += weights[i]*approxD(2, phi - Phi(gammaIs[i], gammaTs[i], 2))*ATRT;
            }

            valuesR  [phiI + y*Resolution] = Vec3f(0.5f*integralR);
            valuesTT [phiI + y*Resolution] = 0.5f*integralTT;
            valuesTRT[phiI + y*Resolution] = 0.5f*integralTRT;
        }
    }

    // Hand the values off to the helper class to construct sampling CDFs and so forth
    _nR  .reset(new PrecomputedAzimuthalLobe(std::move(valuesR)));
    _nTT .reset(new PrecomputedAzimuthalLobe(std::move(valuesTT)));
    _nTRT.reset(new PrecomputedAzimuthalLobe(std::move(valuesTRT)));
}