429 lines
15 KiB
C++
429 lines
15 KiB
C++
//
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
// Copyright (c) Contributors to the OpenEXR Project.
|
|
//
|
|
|
|
#ifndef _PxOneChanDeepOpacity_h_
|
|
#define _PxOneChanDeepOpacity_h_
|
|
|
|
#include "PxBaseDeepHelper.h"
|
|
#include "PxDeepUtils.h"
|
|
|
|
namespace PxDeep
|
|
{
|
|
|
|
//-*****************************************************************************
|
|
// ONE CHANNEL DEEP OPACITY CONTINUOUS
|
|
//-*****************************************************************************
|
|
template <typename RGBA_T>
|
|
class OneChanDeepOpacityContinuous : public BaseDeepHelper<
|
|
RGBA_T,
|
|
OneChanDeepOpacityContinuous<RGBA_T>,
|
|
SpanOpac>
|
|
{
|
|
public:
|
|
typedef BaseDeepHelper<
|
|
RGBA_T,
|
|
OneChanDeepOpacityContinuous<RGBA_T>,
|
|
SpanOpac>
|
|
super_type;
|
|
typedef OneChanDeepOpacityContinuous<RGBA_T> this_type;
|
|
typedef typename super_type::span_type span_type;
|
|
|
|
OneChanDeepOpacityContinuous (
|
|
DtexFile* i_dtexFile, int i_numDtexChans, const Parameters& i_params)
|
|
: BaseDeepHelper<
|
|
RGBA_T,
|
|
OneChanDeepOpacityContinuous<RGBA_T>,
|
|
SpanOpac> (i_dtexFile, i_numDtexChans, i_params)
|
|
{}
|
|
|
|
void processDeepPixel (int i_numPts);
|
|
};
|
|
|
|
//-*****************************************************************************
|
|
// ONE CHANNEL DEEP OPACITY DISCRETE
|
|
//-*****************************************************************************
|
|
template <typename RGBA_T>
|
|
class OneChanDeepOpacityDiscrete : public BaseDeepHelper<
|
|
RGBA_T,
|
|
OneChanDeepOpacityDiscrete<RGBA_T>,
|
|
SpanOpac>
|
|
{
|
|
public:
|
|
typedef BaseDeepHelper<RGBA_T, OneChanDeepOpacityDiscrete<RGBA_T>, SpanOpac>
|
|
super_type;
|
|
typedef OneChanDeepOpacityDiscrete<RGBA_T> this_type;
|
|
typedef typename super_type::span_type span_type;
|
|
|
|
OneChanDeepOpacityDiscrete (
|
|
DtexFile* i_dtexFile, int i_numDtexChans, const Parameters& i_params)
|
|
: BaseDeepHelper<RGBA_T, OneChanDeepOpacityDiscrete<RGBA_T>, SpanOpac> (
|
|
i_dtexFile, i_numDtexChans, i_params)
|
|
{}
|
|
|
|
void processDeepPixel (int i_numPts);
|
|
};
|
|
|
|
//-*****************************************************************************
|
|
template <typename RGBA_T>
|
|
void
|
|
OneChanDeepOpacityContinuous<RGBA_T>::processDeepPixel (int i_numPts)
|
|
{
|
|
assert (i_numPts > 0);
|
|
|
|
// Loop over all the dtex points and get their deepOpacities and
|
|
// depths.
|
|
(this->m_spans).resize ((size_t) i_numPts);
|
|
for (int j = 0; j < i_numPts; ++j)
|
|
{
|
|
float z;
|
|
float pts[4];
|
|
DtexPixelGetPoint ((this->m_pixel), j, &z, (float*) pts);
|
|
|
|
z = ClampDepth (z);
|
|
|
|
span_type& spanJ = (this->m_spans)[j];
|
|
spanJ.clear ();
|
|
spanJ.in = z;
|
|
spanJ.out = z;
|
|
|
|
// Data stored in dtex files for "deepopacity" is actually
|
|
// "deeptransmission", monotonically decreasing from an initial
|
|
// value of 1.0. We just convert it to viz directly.
|
|
// (viz == transmissivity)
|
|
spanJ.deepViz = ClampViz (pts[0]);
|
|
spanJ.index = j;
|
|
}
|
|
|
|
// Sort the spans by depth (and then index)
|
|
std::sort ((this->m_spans).begin (), (this->m_spans).end ());
|
|
|
|
// Combine identical depths, accumulating maximum density
|
|
// along the way. Because we have deep opacity,
|
|
// coincident samples use the maximum deepOpacity value.
|
|
double maxDensity = PXDU_MIN_NON_ZERO_DENSITY;
|
|
{
|
|
int prevSpanIndex = 0;
|
|
int activeBegin = 0;
|
|
int activeEnd = 0;
|
|
float interestingDepth = 0.0f;
|
|
int numRemoved = 0;
|
|
|
|
while (activeBegin < i_numPts)
|
|
{
|
|
span_type& spanActiveBegin = (this->m_spans)[activeBegin];
|
|
float nextInterestingDepth = spanActiveBegin.in;
|
|
assert (nextInterestingDepth > interestingDepth);
|
|
|
|
// This loop combines all the coincident samples
|
|
// into a single sample, invalidates the other coincident
|
|
// samples, and sets activeEnd to point to the next
|
|
// sample with a larger depth.
|
|
activeEnd = i_numPts;
|
|
for (int a = activeBegin + 1; a < i_numPts; ++a)
|
|
{
|
|
span_type& spanNext = (this->m_spans)[a];
|
|
|
|
assert (spanNext.in > interestingDepth);
|
|
assert (spanNext.in >= nextInterestingDepth);
|
|
|
|
if (spanNext.in > nextInterestingDepth)
|
|
{
|
|
// This span is not active in this round,
|
|
// set activeEnd and get out.
|
|
activeEnd = a;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// This span has an identical depth to
|
|
// the previous one, so we use whichever one has the
|
|
// largest deep opacity, which equates to the
|
|
// smallest deep viz.
|
|
spanActiveBegin.deepViz =
|
|
std::min (spanActiveBegin.deepViz, spanNext.deepViz);
|
|
spanNext.in = FLT_MAX;
|
|
spanNext.out = FLT_MAX;
|
|
++numRemoved;
|
|
}
|
|
}
|
|
|
|
// Okay, the deep vizibility at our in point
|
|
// is equal to the total vizibility before us,
|
|
// which is the deep vizibility at the previous point,
|
|
// times the vizibility of this point.
|
|
// deepViz = deepVizPrev * viz
|
|
// viz = deepViz / deepVizPrev;
|
|
if (activeBegin == 0)
|
|
{
|
|
spanActiveBegin.viz = spanActiveBegin.deepViz;
|
|
}
|
|
else
|
|
{
|
|
span_type& spanPrev = (this->m_spans)[prevSpanIndex];
|
|
|
|
// Make sure the deep visibilities are
|
|
// monotonically decreasing with depth.
|
|
spanActiveBegin.deepViz =
|
|
std::min (spanActiveBegin.deepViz, spanPrev.deepViz);
|
|
|
|
if (spanPrev.deepViz > 0.0)
|
|
{
|
|
// If we have non-zero accumulated visibility,
|
|
// we can compute the span visibility.
|
|
spanActiveBegin.viz =
|
|
spanActiveBegin.deepViz / spanPrev.deepViz;
|
|
}
|
|
else
|
|
{
|
|
// If we have zero accumulated visibility,
|
|
// then the span visibility is also zero.
|
|
spanActiveBegin.viz = 0.0;
|
|
}
|
|
|
|
// Use the viz of this span to update the
|
|
// max density.
|
|
spanActiveBegin.viz = ClampViz (spanActiveBegin.viz);
|
|
|
|
spanActiveBegin.in = spanPrev.out;
|
|
double dz = spanActiveBegin.out - spanActiveBegin.in;
|
|
assert (dz > 0.0);
|
|
assert (spanActiveBegin.out > spanActiveBegin.in);
|
|
double density = DensityFromVizDz (spanActiveBegin.viz, dz);
|
|
maxDensity = std::max (maxDensity, density);
|
|
}
|
|
|
|
prevSpanIndex = activeBegin;
|
|
activeBegin = activeEnd;
|
|
interestingDepth = nextInterestingDepth;
|
|
}
|
|
|
|
// If any removed, re-sort the list and remove the end
|
|
// points.
|
|
if (numRemoved > 0)
|
|
{
|
|
assert (numRemoved < i_numPts);
|
|
std::sort ((this->m_spans).begin (), (this->m_spans).end ());
|
|
i_numPts -= numRemoved;
|
|
(this->m_spans).resize (i_numPts);
|
|
}
|
|
}
|
|
|
|
// Handle the single point case.
|
|
if (i_numPts == 1)
|
|
{
|
|
span_type& span0 = (this->m_spans)[0];
|
|
|
|
if ((this->m_params).discardZeroAlphaSamples && span0.viz >= 1.0)
|
|
{
|
|
// Nothing!
|
|
return;
|
|
}
|
|
|
|
span0.in = ClampDepth (DecrementPositiveFloat (span0.out));
|
|
|
|
float alphaF = ClampAlpha (1.0 - span0.viz);
|
|
|
|
(this->m_deepOutPixel).push_back (span0.in, span0.out, alphaF);
|
|
|
|
return;
|
|
}
|
|
|
|
// Put the spans back out.
|
|
// If the first point has a non-zero alpha, extrapolate the
|
|
// maximum density to create a begin point.
|
|
for (int j = 0; j < i_numPts; ++j)
|
|
{
|
|
span_type& spanJ = (this->m_spans)[j];
|
|
|
|
if ((this->m_params.discardZeroAlphaSamples) && spanJ.viz >= 1.0)
|
|
{
|
|
// This span is transparent, ignore it.
|
|
continue;
|
|
}
|
|
|
|
if (j == 0)
|
|
{
|
|
// This is first point.
|
|
// If it has non-zero alpha, it needs depth,
|
|
// which we use the max density for.
|
|
if (spanJ.viz >= 1.0)
|
|
{
|
|
// Don't need to worry about this last span!
|
|
// It is at the end of the continuous span, and
|
|
// is completely transparent.
|
|
continue;
|
|
}
|
|
|
|
double dz = DzFromVizDensity (spanJ.viz, maxDensity);
|
|
spanJ.in = ClampDepth (spanJ.out - dz);
|
|
if (spanJ.out <= spanJ.in)
|
|
{
|
|
spanJ.in = ClampDepth (DecrementPositiveFloat (spanJ.out));
|
|
}
|
|
}
|
|
|
|
float alphaF = ClampAlpha (1.0 - spanJ.viz);
|
|
|
|
// Set the channels!
|
|
(this->m_deepOutPixel).push_back (spanJ.in, spanJ.out, alphaF);
|
|
}
|
|
}
|
|
|
|
//-*****************************************************************************
|
|
template <typename RGBA_T>
|
|
void
|
|
OneChanDeepOpacityDiscrete<RGBA_T>::processDeepPixel (int i_numPts)
|
|
{
|
|
assert (i_numPts > 0);
|
|
|
|
// Loop over all the dtex points and get their deepOpacities and
|
|
// depths.
|
|
(this->m_spans).resize ((size_t) i_numPts);
|
|
for (int j = 0; j < i_numPts; ++j)
|
|
{
|
|
float z;
|
|
float pts[4];
|
|
DtexPixelGetPoint ((this->m_pixel), j, &z, (float*) pts);
|
|
|
|
z = ClampDepth (z);
|
|
|
|
span_type& spanJ = (this->m_spans)[j];
|
|
spanJ.clear ();
|
|
spanJ.in = z;
|
|
spanJ.out = z;
|
|
|
|
// Data stored in dtex files for "deepopacity" is actually
|
|
// "deeptransmission", monotonically decreasing from an initial
|
|
// value of 1.0. We just convert it to viz directly.
|
|
// (viz == transmissivity)
|
|
spanJ.deepViz = ClampViz (pts[0]);
|
|
spanJ.index = j;
|
|
}
|
|
|
|
// Sort the spans.
|
|
std::sort ((this->m_spans).begin (), (this->m_spans).end ());
|
|
|
|
// Combine identical depths. Because we have deep opacity,
|
|
// coincident samples use the maximum deepOpacity value.
|
|
{
|
|
int prevSpanIndex = 0;
|
|
int activeBegin = 0;
|
|
int activeEnd = 0;
|
|
float interestingDepth = 0.0f;
|
|
int numRemoved = 0;
|
|
|
|
while (activeBegin < i_numPts)
|
|
{
|
|
span_type& spanActiveBegin = (this->m_spans)[activeBegin];
|
|
float nextInterestingDepth = spanActiveBegin.in;
|
|
assert (nextInterestingDepth > interestingDepth);
|
|
|
|
// This loop combines all the coincident samples
|
|
// into a single sample, invalidates the other coincident
|
|
// samples, and sets activeEnd to point to the next
|
|
// sample with a larger depth.
|
|
activeEnd = i_numPts;
|
|
for (int a = activeBegin + 1; a < i_numPts; ++a)
|
|
{
|
|
span_type& spanNext = (this->m_spans)[a];
|
|
|
|
assert (spanNext.in > interestingDepth);
|
|
assert (spanNext.in >= nextInterestingDepth);
|
|
|
|
if (spanNext.in > nextInterestingDepth)
|
|
{
|
|
// This span is not active in this round,
|
|
// set activeEnd and get out.
|
|
activeEnd = a;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// This span has an identical depth to
|
|
// the previous one, so we use whichever one has the
|
|
// largest deep opacity, which equates to the
|
|
// smallest deep viz.
|
|
spanActiveBegin.deepViz =
|
|
std::min (spanActiveBegin.deepViz, spanNext.deepViz);
|
|
spanNext.in = FLT_MAX;
|
|
spanNext.out = FLT_MAX;
|
|
++numRemoved;
|
|
}
|
|
}
|
|
|
|
// Okay, the deep vizibility at our in point
|
|
// is equal to the total vizibility before us,
|
|
// which is the deep vizibility at the previous point,
|
|
// times the vizibility of this point.
|
|
// deepViz = deepVizPrev * viz
|
|
// viz = deepViz / deepVizPrev;
|
|
if (activeBegin == 0)
|
|
{
|
|
spanActiveBegin.viz = spanActiveBegin.deepViz;
|
|
}
|
|
else
|
|
{
|
|
span_type& spanPrev = (this->m_spans)[prevSpanIndex];
|
|
|
|
// Make sure the deep visibilities are
|
|
// monotonically decreasing with depth.
|
|
spanActiveBegin.deepViz =
|
|
std::min (spanActiveBegin.deepViz, spanPrev.deepViz);
|
|
|
|
if (spanPrev.deepViz > 0.0)
|
|
{
|
|
// If we have non-zero accumulated visibility,
|
|
// we can compute the span visibility.
|
|
spanActiveBegin.viz =
|
|
spanActiveBegin.deepViz / spanPrev.deepViz;
|
|
}
|
|
else
|
|
{
|
|
// If we have zero accumulated visibility,
|
|
// then the span visibility is also zero.
|
|
spanActiveBegin.viz = 0.0;
|
|
}
|
|
|
|
// Clean up the viz!
|
|
spanActiveBegin.viz = ClampViz (spanActiveBegin.viz);
|
|
}
|
|
|
|
prevSpanIndex = activeBegin;
|
|
activeBegin = activeEnd;
|
|
interestingDepth = nextInterestingDepth;
|
|
}
|
|
|
|
// If any removed, re-sort the list and remove the end
|
|
// points.
|
|
if (numRemoved > 0)
|
|
{
|
|
assert (numRemoved < i_numPts);
|
|
std::sort ((this->m_spans).begin (), (this->m_spans).end ());
|
|
i_numPts -= numRemoved;
|
|
(this->m_spans).resize (i_numPts);
|
|
}
|
|
}
|
|
|
|
// Put the spans back out.
|
|
for (int j = 0; j < i_numPts; ++j)
|
|
{
|
|
span_type& spanJ = (this->m_spans)[j];
|
|
|
|
if ((this->m_params).discardZeroAlphaSamples && spanJ.viz >= 1.0)
|
|
{
|
|
// This span is transparent, ignore it.
|
|
continue;
|
|
}
|
|
|
|
float alphaF = ClampAlpha (1.0 - spanJ.viz);
|
|
|
|
(this->m_deepOutPixel).push_back (spanJ.in, alphaF);
|
|
}
|
|
}
|
|
|
|
} // End namespace PxDeep
|
|
|
|
#endif
|