2
// Copyright ļæ½ 1997 - 2001, Paul C. Gregory
4
// Contact: pgregory@aqsis.com
6
// This library is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public
8
// License as published by the Free Software Foundation; either
9
// version 2 of the License, or (at your option) any later version.
11
// This library is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
// General Public License for more details.
16
// You should have received a copy of the GNU General Public
17
// License along with this library; if not, write to the Free Software
18
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
\brief Implements the CqImageBuffer class responsible for rendering the primitives and storing the results.
23
\author Paul C. Gregory (pgregory@aqsis.com)
36
#include "imagepixel.h"
40
START_NAMESPACE( Aqsis )
42
//----------------------------------------------------------------------
46
CqImagePixel::CqImagePixel() :
49
m_MaxDepth( FLT_MAX ),
50
m_MinDepth( FLT_MAX ),
51
m_OcclusionBoxId( -1 ),
52
m_NeedsZUpdate( TqFalse )
56
//----------------------------------------------------------------------
60
CqImagePixel::~CqImagePixel()
64
//----------------------------------------------------------------------
68
CqImagePixel::CqImagePixel( const CqImagePixel& ieFrom )
74
//----------------------------------------------------------------------
75
/** Allocate the subpixel samples array.
76
* \param XSamples Integer samples count in X.
77
* \param YSamples Integer samples count in Y.
80
void CqImagePixel::AllocateSamples( TqInt XSamples, TqInt YSamples )
82
if( m_XSamples != XSamples || m_YSamples != YSamples )
84
m_XSamples = XSamples;
85
m_YSamples = YSamples;
86
TqInt numSamples = m_XSamples * m_YSamples;
88
if ( XSamples > 0 && YSamples > 0 )
90
m_aValues.resize( numSamples );
91
// Initialise the OpaqueSampleEntries to the correct depth for the data we are
92
// rendering, including any AOV data.
93
SqImageSample def( QGetRenderContext() ->GetOutputDataTotalSize() );
94
m_OpaqueValues.resize( numSamples, def );
95
m_Samples.resize( numSamples );
96
m_DofOffsetIndices.resize( numSamples );
101
//----------------------------------------------------------------------
102
/** Fill in the sample array usig the multijitter function from GG IV.
103
* \param vecPixel Cq2DVector pixel coordinate of this image element, used to make sure sample points are absolute, not relative.
104
* \param fJitter Flag indicating whether to apply jittering to the sample points or not.
107
void CqImagePixel::InitialiseSamples( std::vector<CqVector2D>& vecSamples, TqBool fJitter )
109
TqFloat opentime = QGetRenderContext() ->optCurrent().GetFloatOption( "System", "Shutter" ) [ 0 ];
110
TqFloat closetime = QGetRenderContext() ->optCurrent().GetFloatOption( "System", "Shutter" ) [ 1 ];
112
TqInt numSamples = m_XSamples * m_YSamples;
113
TqFloat subcell_width = 1.0f / numSamples;
114
TqInt m = m_XSamples;
115
TqInt n = m_YSamples;
118
vecSamples.resize(numSamples);
121
// Initialise the samples to the centre points.
122
TqFloat XInc = ( 1.0f / m_XSamples ) / 2.0f;
123
TqFloat YInc = ( 1.0f / m_YSamples ) / 2.0f;
125
for ( y = 0; y < m_YSamples; y++ )
127
TqFloat YSam = YInc + ( YInc * y );
129
for ( x = 0; x < m_XSamples; x++ )
130
vecSamples[ ( y * m_XSamples ) + x ] = CqVector2D( XInc + ( XInc * x ), YSam );
134
// Fill in the sample times for motion blur, LOD and SubCellIndex entries
137
TqInt nSamples = m_XSamples*m_YSamples;
138
TqFloat dtime = 1.0f / nSamples;
140
for ( i = 0; i < nSamples; i++ )
142
m_Samples[ i ].m_SubCellIndex = 0;
143
m_Samples[ i ].m_DetailLevel = m_Samples[ i ].m_Time = time;
151
static CqRandom random( 53 );
153
// Initialize points to the "canonical" multi-jittered pattern.
155
for ( i = 0; i < n; i++ )
157
for ( j = 0; j < m; j++ )
159
TqInt which = i * m + j;
160
vecSamples[which].x( i );
161
vecSamples[which].y( j );
165
// Shuffle y coordinates within each row of cells.
166
for ( i = 0; i < n; i++ )
168
for ( j = 0; j < m; j++ )
173
k = random.RandomInt( n - 1 - i ) + i;
174
TqInt i1 = i * m + j;
175
TqInt i2 = k * m + j;
176
assert( i1 < vecSamples.size() && i2 < vecSamples.size() );
177
t = vecSamples[ i1 ].y();
178
vecSamples[ i1 ].y( vecSamples[ i2 ].y() );
179
vecSamples[ i2 ].y( t );
183
// Shuffle x coordinates within each column of cells.
184
for ( i = 0; i < m; i++ )
186
for ( j = 0; j < n; j++ )
191
k = random.RandomInt( n - 1 - j ) + j;
192
TqInt i1 = j * m + i;
193
TqInt i2 = k * m + i;
194
assert( i1 < vecSamples.size() && i2 < vecSamples.size() );
195
t = vecSamples[ i1 ].x();
196
vecSamples[ i1 ].x( vecSamples[ i2 ].x() );
197
vecSamples[ i2 ].x( t );
203
TqFloat subpixelheight = 1.0f / m_YSamples;
204
TqFloat subpixelwidth = 1.0f / m_XSamples;
207
for ( i = 0; i < n; i++ )
209
TqFloat sy = i * subpixelheight;
210
for ( j = 0; j < m; j++ )
212
TqFloat sx = j * subpixelwidth;
213
TqFloat xindex = vecSamples[ which ].x();
214
TqFloat yindex = vecSamples[ which ].y();
215
vecSamples[ which ].x( xindex * subcell_width + ( subcell_width * 0.5f ) + sx );
216
vecSamples[ which ].y( yindex * subcell_width + ( subcell_width * 0.5f ) + sy );
217
m_Samples[ which ].m_SubCellIndex = static_cast<TqInt>( ( yindex * m_YSamples ) + xindex );
222
// Fill in the sample times for motion blur, detail levels for LOD and DoF.
225
TqFloat dtime = 1.0f / numSamples;
226
// We use the same random offset for each sample within a pixel.
227
// This ensures the best possible coverage whilst still avoiding
228
// aliasing. (I reckon). should minimise the noise.
229
TqFloat randomTime = random.RandomFloat( dtime );
232
TqFloat dlod = dtime;
234
for ( i = 0; i < numSamples; i++ )
236
// Scale the value of time to the shutter time.
237
TqFloat t = time + randomTime;
238
t = ( closetime - opentime ) * t + opentime;
239
m_Samples[ i ].m_Time = t;
242
m_Samples[ i ].m_DetailLevel = lod + random.RandomFloat( dlod );
246
// we calculate dof offsets in a grid inside the unit cube and then
247
// project them into the unit circle. This means that the offset
248
// positions match the offset bounding boxes calculated in CqBucket.
249
// The sample test in RenderMicroPoly then can be split into a number
250
// of smaller bounding boxes where we know in advance which samples
251
// fall into each. (This is analagous to what we do for mb now as well).
252
// note that there is an implied symmetry to the way we number the bounding
253
// boxes here and in the bucket code where the bb's are created (it
254
// should be left to right, top to bottom).
255
TqFloat dx = 2.0 / m_XSamples;
256
TqFloat dy = 2.0 / m_YSamples;
257
// We use the same random offset for each sample within a pixel.
258
// This ensures the best possible coverage whilst still avoiding
259
// aliasing. (I reckon). should minimise the noise.
260
TqFloat sx = random.RandomFloat(dx);
261
TqFloat sy = random.RandomFloat(dy);
262
TqFloat xOffset = -1.0 + sx;
263
TqFloat yOffset = -1.0 + sy;
265
std::vector<CqVector2D> tmpDofOffsets(numSamples);
266
for ( i = 0; i < m_YSamples; ++i )
268
for ( j = 0; j < m_XSamples; ++j )
270
tmpDofOffsets[which].x(xOffset);
271
tmpDofOffsets[which].y(yOffset);
272
ProjectToCircle(tmpDofOffsets[which]);
274
m_DofOffsetIndices[which] = which;
283
// we now shuffle the dof offsets but remember which one went where.
284
std::random_shuffle(m_DofOffsetIndices.begin(), m_DofOffsetIndices.end());
285
for( i = 0; i < numSamples; ++i)
287
m_Samples[m_DofOffsetIndices[i]].m_DofOffset = tmpDofOffsets[i];
290
m_Data.m_Data.resize( QGetRenderContext()->GetOutputDataTotalSize() );
294
//----------------------------------------------------------------------
295
/** Clear the relevant data from the image element preparing it for the next usage.
298
void CqImagePixel::Clear()
301
for ( i = ( m_XSamples * m_YSamples ) - 1; i >= 0; i-- )
303
if(!m_aValues[i].empty())
304
m_aValues[ i ].clear( );
306
m_OpaqueValues[i].m_flags = 0;
309
m_OpaqueSampleCount = 0;
310
m_AnySampleUsesSampleList = TqFalse;
311
m_MaxDepth = FLT_MAX;
312
m_MinDepth = FLT_MAX;
313
m_OcclusionBoxId = -1;
314
m_NeedsZUpdate = TqFalse;
318
//----------------------------------------------------------------------
319
/** Get the color at the specified sample point by blending the colors that appear at that point.
322
void CqImagePixel::Combine()
324
TqInt depthfilter = 0;
326
const CqString* pstrDepthFilter = QGetRenderContext() ->optCurrent().GetStringOption( "Hider", "depthfilter" );
327
const CqColor* pzThreshold = QGetRenderContext() ->optCurrent().GetColorOption( "limits", "zthreshold" );
328
CqColor zThreshold(1.0f, 1.0f, 1.0f); // Default threshold of 1,1,1 means that any objects that are partially transparent won't appear in shadow maps.
329
if(NULL != pzThreshold)
330
zThreshold = pzThreshold[0];
332
if ( NULL != pstrDepthFilter )
334
if( !pstrDepthFilter[ 0 ].compare( "min" ) )
336
else if ( !pstrDepthFilter[ 0 ].compare( "midpoint" ) )
338
else if ( !pstrDepthFilter[ 0 ].compare( "max" ) )
340
else if ( !pstrDepthFilter[ 0 ].compare( "average" ) )
343
std::cerr << warning << "Invalid depthfilter \"" << pstrDepthFilter[ 0 ].c_str() << "\", depthfilter set to \"min\"" << std::endl;
346
TqUint samplecount = 0;
347
TqUint numsamples = XSamples() * YSamples();
348
if(m_AnySampleUsesSampleList)
350
TqInt sampleIndex = 0;
351
std::vector<std::vector<SqImageSample> >::iterator end = m_aValues.end();
352
for ( std::vector<std::vector<SqImageSample> >::iterator samples = m_aValues.begin(); samples != end; ++samples )
354
SqImageSample& opaqueValue = m_OpaqueValues[sampleIndex];
357
if(!samples->empty())
359
if(opaqueValue.m_flags & SqImageSample::Flag_Valid)
361
// insert opaqueValue into samples in the right place.
362
std::vector<SqImageSample>::iterator isi = samples->begin();
363
std::vector<SqImageSample>::iterator isend = samples->end();
364
while( isi != isend )
366
if((*isi).Depth() >= opaqueValue.Depth())
371
samples->insert(isi, opaqueValue);
374
// Find out if any of the samples are in a CSG tree.
376
TqBool CqCSGRequired = CqCSGTreeNode::IsRequired();
380
bProcessed = TqFalse;
381
//Warning ProcessTree add or remove elements in samples list
382
//We could not optimized the for loop here at all.
383
for ( std::vector<SqImageSample>::iterator isample = samples->begin(); isample != samples->end(); ++isample )
385
if ( isample->m_pCSGNode )
387
isample->m_pCSGNode->ProcessTree( *samples );
392
} while ( bProcessed );
394
CqColor samplecolor = gColBlack;
395
CqColor sampleopacity = gColBlack;
396
TqBool samplehit = TqFalse;
397
TqFloat opaqueDepths[2] = { FLT_MAX, FLT_MAX };
398
TqFloat maxOpaqueDepth = FLT_MAX;
400
for ( std::vector<SqImageSample>::reverse_iterator sample = samples->rbegin(); sample != samples->rend(); sample++ )
402
if ( sample->m_flags & SqImageSample::Flag_Matte )
404
if ( sample->m_flags & SqImageSample::Flag_Occludes )
406
// Optimise common case
407
samplecolor = gColBlack;
408
sampleopacity = gColBlack;
412
samplecolor.SetColorRGB(
413
LERP( sample->Os().fRed(), samplecolor.fRed(), 0 ),
414
LERP( sample->Os().fGreen(), samplecolor.fGreen(), 0 ),
415
LERP( sample->Os().fBlue(), samplecolor.fBlue(), 0 )
417
sampleopacity.SetColorRGB(
418
LERP( sample->Os().fRed(), sampleopacity.fRed(), 0 ),
419
LERP( sample->Os().fGreen(), sampleopacity.fGreen(), 0 ),
420
LERP( sample->Os().fBlue(), sampleopacity.fBlue(), 0 )
426
samplecolor = ( samplecolor * ( gColWhite - sample->Os() ) ) + sample->Cs();
427
sampleopacity = ( ( gColWhite - sampleopacity ) * sample->Os() ) + sampleopacity;
430
// Now determine if the sample opacity meets the limit for depth mapping.
431
// If so, store the depth in the appropriate nearest opaque sample slot.
432
// The test is, if any channel of the opacity color is greater or equal to the threshold.
433
if(sample->Os().fRed() >= zThreshold.fRed() || sample->Os().fGreen() >= zThreshold.fGreen() || sample->Os().fBlue() >= zThreshold.fBlue())
435
// Make sure we store the nearest and second nearest depth values.
436
opaqueDepths[1] = opaqueDepths[0];
437
opaqueDepths[0] = sample->Depth();
438
// Store the max opaque depth too, if not already stored.
439
if(!(maxOpaqueDepth < FLT_MAX))
440
maxOpaqueDepth = sample->Depth();
450
// Write the collapsed color values back into the opaque entry.
451
if ( !samples->empty() )
453
// Set the color and opacity.
454
opaqueValue.SetCs( samplecolor );
455
opaqueValue.SetOs( sampleopacity );
456
opaqueValue.m_flags |= SqImageSample::Flag_Valid;
458
if ( depthfilter != 0)
460
if ( depthfilter == 1 )
462
//std::cerr << debug << "OpaqueDepths: " << opaqueDepths[0] << " - " << opaqueDepths[1] << std::endl;
463
// Use midpoint for depth
464
if ( samples->size() > 1 )
465
opaqueValue.SetDepth( ( opaqueDepths[0] + opaqueDepths[1] ) * 0.5f );
467
opaqueValue.SetDepth( FLT_MAX );
469
else if ( depthfilter == 2)
471
opaqueValue.SetDepth( maxOpaqueDepth );
473
else if ( depthfilter == 3 )
475
std::vector<SqImageSample>::iterator sample;
476
TqFloat totDepth = 0.0f;
478
for ( sample = samples->begin(); sample != samples->end(); sample++ )
479
if(sample->Os().fRed() >= zThreshold.fRed() || sample->Os().fGreen() >= zThreshold.fGreen() || sample->Os().fBlue() >= zThreshold.fBlue())
481
totDepth += sample->Depth();
484
totDepth /= totCount;
486
opaqueValue.SetDepth( totDepth );
491
opaqueValue.SetDepth( opaqueDepths[0] );
496
if(opaqueValue.m_flags & SqImageSample::Flag_Valid)
505
samplecount = m_OpaqueSampleCount;
509
//----------------------------------------------------------------------
510
/** ReCalculate the min and max z values for this pixel
513
void CqImagePixel::UpdateZValues()
515
float currentMax = 0.0f;
516
float currentMin = FLT_MAX;
517
TqInt sampleIndex = 0;
519
for ( sy = 0; sy < m_YSamples; sy++ )
521
for ( sx = 0; sx < m_XSamples; sx++ )
523
SqImageSample& opaqueSample = m_OpaqueValues[ sampleIndex ];
524
if(opaqueSample.m_flags & SqImageSample::Flag_Valid)
526
if ( opaqueSample.Depth() > currentMax )
528
currentMax = opaqueSample.Depth();
530
if ( opaqueSample.Depth() < currentMin )
532
currentMin = opaqueSample.Depth();
537
currentMax = FLT_MAX;
544
m_MaxDepth = currentMax;
545
m_MinDepth = currentMin;
548
//---------------------------------------------------------------------
550
END_NAMESPACE( Aqsis )