Skip to content

Commit 0a3982a

Browse files
committed
Merge pull request #287 from danieldresser/shwPerpendicular
Added conversions to SHW reader and writer
2 parents c168bbf + 97351b6 commit 0a3982a

File tree

10 files changed

+146
-41
lines changed

10 files changed

+146
-41
lines changed

include/IECore/DeepPixel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ namespace IECore
4646
IE_CORE_FORWARDDECLARE( DeepPixel )
4747

4848
/// A DeepPixel represents arbitrary channel data stored at varying depths in space.
49+
/// By convention, depth is measured as distance from the eye plane
4950
/// \ingroup deepCompositingGroup
5051
class DeepPixel : public RefCounted
5152
{

include/IECoreRI/SHWDeepImageReader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class SHWDeepImageReader : public IECore::DeepImageReader
8989
Imath::Box2i m_dataWindow;
9090
Imath::M44f m_worldToCamera;
9191
Imath::M44f m_worldToNDC;
92+
Imath::M44f &m_NDCToCamera();
9293
std::string m_inputFileName;
9394
std::string m_channelNames;
9495

include/IECoreRI/SHWDeepImageWriter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ class SHWDeepImageWriter : public IECore::DeepImageWriter
7979
DtexCache *m_dtexCache;
8080
DtexImage *m_dtexImage;
8181
DtexPixel *m_dtexPixel;
82-
82+
83+
Imath::M44f &m_NDCToCamera();
8384
std::string m_outputFileName;
8485
int m_alphaOffset;
8586

src/IECoreRI/SHWDeepImageReader.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@
4343
using namespace IECore;
4444
using namespace IECoreRI;
4545

46+
#include "IECore/ClassData.h"
47+
48+
static IECore::ClassData< SHWDeepImageReader, Imath::M44f > g_ndcClassData;
49+
50+
Imath::M44f &SHWDeepImageReader::m_NDCToCamera()
51+
{
52+
return g_ndcClassData[ this ];
53+
}
54+
55+
4656
IE_CORE_DEFINERUNTIMETYPED( SHWDeepImageReader );
4757

4858
const Reader::ReaderDescription<SHWDeepImageReader> SHWDeepImageReader::g_readerDescription( "shw" );
@@ -52,6 +62,7 @@ SHWDeepImageReader::SHWDeepImageReader()
5262
m_inputFile( 0 ), m_dtexCache( 0 ), m_dtexImage( 0 ), m_dtexPixel( 0 ),
5363
m_dataWindow( Imath::V2i( 0 ), Imath::V2i( 0 ) )
5464
{
65+
g_ndcClassData.create( this, Imath::M44f() );
5566
}
5667

5768
SHWDeepImageReader::SHWDeepImageReader( const std::string &fileName )
@@ -60,11 +71,13 @@ SHWDeepImageReader::SHWDeepImageReader( const std::string &fileName )
6071
m_dataWindow( Imath::V2i( 0 ), Imath::V2i( 0 ) )
6172
{
6273
m_fileNameParameter->setTypedValue( fileName );
74+
g_ndcClassData.create( this, Imath::M44f() );
6375
}
6476

6577
SHWDeepImageReader::~SHWDeepImageReader()
6678
{
6779
clean();
80+
g_ndcClassData.erase( this );
6881
}
6982

7083
bool SHWDeepImageReader::canRead( const std::string &fileName )
@@ -153,10 +166,22 @@ DeepPixelPtr SHWDeepImageReader::doReadPixel( int x, int y )
153166
{
154167
previous[j] = 0.0;
155168
}
156-
169+
170+
float nearClip = m_NDCToCamera()[3][2] / m_NDCToCamera()[3][3];
171+
float correction = 1;
172+
if( m_NDCToCamera()[3][2] != 0 && m_NDCToCamera()[2][3] != 0 )
173+
{
174+
// Compute a correction factor that converts from spherical distance to perpendicular distance,
175+
// by comparing the closest distance to the near clip with the distance to the near clip at the current pixel position
176+
correction = nearClip / ( Imath::V3f(((x+0.5f)/(m_dataWindow.max.x+1) * 2 - 1), -((y+0.5)/(m_dataWindow.max.y+1) * 2 - 1),0) * m_NDCToCamera() ).length();
177+
}
178+
157179
for ( int i=0; i < numSamples; ++i )
158180
{
159181
DtexPixelGetPoint( m_dtexPixel, i, &depth, channelData );
182+
183+
// Convert from "3delight distance" ( spherical distance from near clip ) to Z ( distance from eye plane )
184+
depth = depth * correction + nearClip;
160185

161186
for ( unsigned j=0; j < numRealChannels; ++j )
162187
{
@@ -190,6 +215,7 @@ bool SHWDeepImageReader::open( bool throwOnFailure )
190215
m_dataWindow.max.y = 0;
191216
m_worldToCamera = Imath::M44f();
192217
m_worldToNDC = Imath::M44f();
218+
m_NDCToCamera() = Imath::M44f();
193219

194220
clean();
195221

@@ -214,6 +240,7 @@ bool SHWDeepImageReader::open( bool throwOnFailure )
214240

215241
DtexNl( m_dtexImage, m_worldToCamera.getValue() );
216242
DtexNP( m_dtexImage, m_worldToNDC.getValue() );
243+
m_NDCToCamera() = m_worldToNDC.inverse() * m_worldToCamera;
217244
}
218245
else
219246
{

src/IECoreRI/SHWDeepImageWriter.cpp

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@
4343
using namespace IECore;
4444
using namespace IECoreRI;
4545

46+
#include "IECore/ClassData.h"
47+
48+
static IECore::ClassData< SHWDeepImageWriter, Imath::M44f > g_ndcClassData;
49+
50+
Imath::M44f &SHWDeepImageWriter::m_NDCToCamera()
51+
{
52+
return g_ndcClassData[ this ];
53+
}
54+
55+
4656
IE_CORE_DEFINERUNTIMETYPED( SHWDeepImageWriter );
4757

4858
const DeepImageWriter::DeepImageWriterDescription<SHWDeepImageWriter> SHWDeepImageWriter::g_writerDescription( "shw" );
@@ -56,6 +66,9 @@ SHWDeepImageWriter::SHWDeepImageWriter()
5666

5767
m_tileSizeParameter = new V2iParameter( "tileSize", "The tile size for the image cache. Must be equal or less than resolution.", new V2iData( Imath::V2i( 32, 32 ) ) );
5868
parameters()->addParameter( m_tileSizeParameter );
69+
70+
g_ndcClassData.create( this, Imath::M44f() );
71+
5972
}
6073

6174
SHWDeepImageWriter::SHWDeepImageWriter( const std::string &fileName )
@@ -69,11 +82,15 @@ SHWDeepImageWriter::SHWDeepImageWriter( const std::string &fileName )
6982

7083
m_tileSizeParameter = new V2iParameter( "tileSize", "The tile size for the image cache. Must be equal or less than resolution.", new V2iData( Imath::V2i( 32, 32 ) ) );
7184
parameters()->addParameter( m_tileSizeParameter );
85+
86+
g_ndcClassData.create( this, Imath::M44f() );
7287
}
7388

7489
SHWDeepImageWriter::~SHWDeepImageWriter()
7590
{
7691
clean();
92+
g_ndcClassData.erase( this );
93+
7794
}
7895

7996
bool SHWDeepImageWriter::canWrite( const std::string &fileName )
@@ -108,6 +125,17 @@ void SHWDeepImageWriter::doWritePixel( int x, int y, const DeepPixel *pixel )
108125

109126
float previous = 0.0;
110127
unsigned numSamples = pixel->numSamples();
128+
129+
const Imath::V2i &resolution = m_resolutionParameter->getTypedValue();
130+
float nearClip = m_NDCToCamera()[3][2] / m_NDCToCamera()[3][3];
131+
float correction = 1;
132+
if( m_NDCToCamera()[3][2] != 0 && m_NDCToCamera()[2][3] != 0 )
133+
{
134+
// Compute a correction factor that converts from perpendicular distance to spherical distance,
135+
// by comparing the closest distance to the near clip with the distance to the near clip at the current pixel position
136+
correction = ( Imath::V3f(((x+0.5f)/resolution.x * 2 - 1), -((y+0.5)/resolution.y * 2 - 1),0) * m_NDCToCamera() ).length() / nearClip;
137+
}
138+
111139
for ( unsigned i=0; i < numSamples; ++i )
112140
{
113141
// SHW files require composited values, accumulated over depth, but we have uncomposited values
@@ -124,8 +152,13 @@ void SHWDeepImageWriter::doWritePixel( int x, int y, const DeepPixel *pixel )
124152
{
125153
adjustedData[c] = value;
126154
}
127-
128-
DtexAppendPixel( m_dtexPixel, pixel->getDepth( i ), numChannels, adjustedData, 0 );
155+
156+
float depth = pixel->getDepth( i );
157+
158+
// Convert from Z ( distance from eye plane ) to "3delight distance" ( spherical distance from near clip )
159+
depth = ( depth - nearClip ) * correction;
160+
161+
DtexAppendPixel( m_dtexPixel, depth, numChannels, adjustedData, 0 );
129162
}
130163

131164
DtexFinishPixel( m_dtexPixel );
@@ -183,13 +216,16 @@ void SHWDeepImageWriter::open()
183216

184217
float *NL = worldToCameraParameter()->getTypedValue().getValue();
185218
float *NP = worldToNDCParameter()->getTypedValue().getValue();
219+
220+
m_NDCToCamera() = worldToNDCParameter()->getTypedValue().inverse() * worldToCameraParameter()->getTypedValue();
186221

187222
/// \todo: does image name mean anything for this format?
188223
int status = DtexAddImage(
189224
m_outputFile, "", numChannels,
190225
resolution.x, resolution.y, tileSize.x, tileSize.y,
191226
NP, NL, DTEX_COMPRESSION_NONE, DTEX_TYPE_FLOAT, &m_dtexImage
192227
);
228+
193229

194230
if ( status != DTEX_NOERR )
195231
{
@@ -198,7 +234,7 @@ void SHWDeepImageWriter::open()
198234
clean();
199235
throw IOException( std::string( "Failed to create the main sub-image in \"" ) + fileName() + "\" for writing." );
200236
}
201-
237+
202238
m_dtexPixel = DtexMakePixel( numChannels );
203239
}
204240

test/IECoreRI/SHWDeepImageReaderTest.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
class TestSHWDeepImageReader( unittest.TestCase ) :
4141

4242
__shw = "test/IECoreRI/data/shw/translucentBoxes.shw"
43+
__shwConstantPlane = "test/IECoreRI/data/shw/constantPlane.shw"
44+
__shwConstantPlane2 = "test/IECoreRI/data/shw/constantPlane2.shw"
45+
__shwOrtho = "test/IECoreRI/data/shw/constantPlaneOrtho.shw"
46+
4347
__exr = "test/IECoreRI/data/shw/groundPlane.exr"
4448

4549
def testConstructor( self ) :
@@ -113,16 +117,16 @@ def testReadPixel( self ) :
113117
p = reader.readPixel( 100, 100 )
114118
self.assertEqual( p.channelNames(), ( "A", ) )
115119
self.assertEqual( p.numSamples(), 1 )
116-
self.assertAlmostEqual( p.getDepth( 0 ), 107.5978927, 6 )
120+
self.assertAlmostEqual( p.getDepth( 0 ), 102.17636108, 6 )
117121
self.assertAlmostEqual( p[0][0], 1.0, 6 )
118122

119123
# hits one box then ground plane
120124
p2 = reader.readPixel( 256, 256 )
121125
self.assertEqual( p2.channelNames(), tuple(reader.channelNames()) )
122126
self.assertEqual( p2.numSamples(), 3 )
123-
self.assertAlmostEqual( p2.getDepth( 0 ), 71.7940826, 6 )
124-
self.assertAlmostEqual( p2.getDepth( 1 ), 76.9240646, 6 )
125-
self.assertAlmostEqual( p2.getDepth( 2 ), 84.8475646, 6 )
127+
self.assertAlmostEqual( p2.getDepth( 0 ), 72.6087493, 6 )
128+
self.assertAlmostEqual( p2.getDepth( 1 ), 77.7387313, 6 )
129+
self.assertAlmostEqual( p2.getDepth( 2 ), 85.6622314, 6 )
126130

127131
expected = ( 0.5, 0.5, 1.0 )
128132
for i in range( 0, len(expected) ) :
@@ -132,11 +136,11 @@ def testReadPixel( self ) :
132136
p3 = reader.readPixel( 195, 225 )
133137
self.assertEqual( p3.channelNames(), tuple(reader.channelNames()) )
134138
self.assertEqual( p3.numSamples(), 5 )
135-
self.assertAlmostEqual( p3.getDepth( 0 ), 68.2118148, 6 )
136-
self.assertAlmostEqual( p3.getDepth( 1 ), 74.9367370, 6 )
137-
self.assertAlmostEqual( p3.getDepth( 2 ), 77.0554046, 6 )
138-
self.assertAlmostEqual( p3.getDepth( 3 ), 79.7311859, 6 )
139-
self.assertAlmostEqual( p3.getDepth( 4 ), 88.5616073, 6 )
139+
self.assertAlmostEqual( p3.getDepth( 0 ), 68.6177368, 6 )
140+
self.assertAlmostEqual( p3.getDepth( 1 ), 75.3023605, 6 )
141+
self.assertAlmostEqual( p3.getDepth( 2 ), 77.4083328, 6 )
142+
self.assertAlmostEqual( p3.getDepth( 3 ), 80.0680771, 6 )
143+
self.assertAlmostEqual( p3.getDepth( 4 ), 88.8455811, 6 )
140144

141145
expected = ( 0.5, 0.75, 0.5, 0.5, 1.0 )
142146
for i in range( 0, len(expected) ) :
@@ -170,5 +174,31 @@ def testComposite( self ) :
170174
for i in range( imageData.size() ) :
171175
self.assertAlmostEqual( imageData[i], realImageData[i], 6 )
172176

177+
def __testDepthConversionWithFile( self, filename, tolerance ) :
178+
179+
reader = IECore.DeepImageReader.create( filename )
180+
dataWindow = reader.dataWindow()
181+
182+
for y in range( dataWindow.min.y, dataWindow.max.y + 1 ) :
183+
for x in range( dataWindow.min.x, dataWindow.max.x + 1 ) :
184+
p = reader.readPixel( x, y )
185+
s = p.numSamples()
186+
avgDepth = 0
187+
for i in range( s ):
188+
avgDepth += p.getDepth( i ) / s
189+
self.assertAlmostEqual( avgDepth, 10, tolerance )
190+
191+
def testDepthConversion( self ) :
192+
193+
# The first constant plane test is rendered with a single sample, and no jittering, so the depth should match to 6 significant digits
194+
self.__testDepthConversionWithFile( TestSHWDeepImageReader.__shwConstantPlane, 4 )
195+
196+
# The second constant plane was rendered with a small number of jittered samples, and because of the low resolution, there is quite a bit
197+
# of variation in the correction factor across the pixel, so it won't be a perfect match, but it should still be close
198+
self.__testDepthConversionWithFile( TestSHWDeepImageReader.__shwConstantPlane2, 2 )
199+
200+
# Test out an ortho render as well
201+
self.__testDepthConversionWithFile( TestSHWDeepImageReader.__shwOrtho, 2 )
202+
173203
if __name__ == "__main__":
174204
unittest.main()

0 commit comments

Comments
 (0)