[Aqsis-commits] [SCM] Aqsis Renderer branch, master, updated. Release_1.6.0_Phase2-167-g9aa5864
Brought to you by:
ltatkinson,
pgregory
From: Chris F. <c4...@us...> - 2010-02-21 05:38:57
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "Aqsis Renderer". The branch, master has been updated via 9aa586485f5d9676a41f6005ed8f07f7a0d513f3 (commit) from 3c8062bfda4b76252d57e61c44c366e814c80557 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 9aa586485f5d9676a41f6005ed8f07f7a0d513f3 Author: Chris Foster <chr...@gm...> Date: Sun Feb 21 15:30:40 2010 +1000 Implement pixel filtering Preliminary implementation of pixel filtering. Also include an option, Options::doFilter, to turn off filtering if desired. The main change here was to implement filtering in the SampleStorage object (also moved this object to its own separate file as it grew larger.) Also added appropriate filtering options to the Options structure, and made sure that all options are sanitized before use. Caveats: - The sample storage is currently a flat array, so turning up the number of samples per pixel will be horribly memory inefficient. - Only gaussian filters are implemented. diff --git a/prototypes/newcore/CMakeLists.txt b/prototypes/newcore/CMakeLists.txt index 872c228..bed1f9d 100644 --- a/prototypes/newcore/CMakeLists.txt +++ b/prototypes/newcore/CMakeLists.txt @@ -28,6 +28,7 @@ set(srcs renderer.cpp ustring/ustring.cpp primvar.cpp + samplestorage.cpp shader.cpp varspec.cpp scenes/tenpatch.cpp @@ -47,6 +48,7 @@ set(hdrs primvar.h renderer.h sample.h + samplestorage.h scenes/scenes.h shader.h simple.h diff --git a/prototypes/newcore/TODO.txt b/prototypes/newcore/TODO.txt index 12fdc6e..992cf94 100644 --- a/prototypes/newcore/TODO.txt +++ b/prototypes/newcore/TODO.txt @@ -2,13 +2,10 @@ Some things which should be done: --------------------------------- * Sampling - - Supersampling and output image filtering - Tile-based allocation for sample points - Motion Blur sampling - Depth of field sampling - Transparency - deep fragment buffer - * Filtering - - Image output filtering * Grid crack solution via stitching * Bucket rendering * Occlusion culling @@ -37,3 +34,6 @@ Done: * Handle general primvars * Handle AOVs * Implement a basic shader interface + * Sampling + - Supersampling + - Image output filtering diff --git a/prototypes/newcore/experiments/float4.h b/prototypes/newcore/experiments/float4.h index 4701d44..c32ebec 100644 --- a/prototypes/newcore/experiments/float4.h +++ b/prototypes/newcore/experiments/float4.h @@ -110,6 +110,7 @@ std::ostream& operator<<(std::ostream& out, float4 f4) { out << "(" << f4[0] << " " << f4[1] << " " << f4[2] << " " << f4[3] << ")"; + return out; } #endif // FLOAT4_H_INCLUDED diff --git a/prototypes/newcore/options.h b/prototypes/newcore/options.h index 24c132f..0d81a54 100644 --- a/prototypes/newcore/options.h +++ b/prototypes/newcore/options.h @@ -21,25 +21,34 @@ #define OPTIONS_H_INCLUDED #include <cfloat> +#include <OpenEXR/ImathVec.h> /// Renderer options. struct Options { + // Reyes options int maxSplits; ///< maximum number of splits before discarding a surface - int xRes; ///< image x-resolution - int yRes; ///< image y-resolution - //Imath::V2i nsumSamples; ///< number of subsamples int gridSize; ///< Desired grid side length. float clipNear; ///< Near clipping plane (cam coords) float clipFar; ///< Far clipping plane (cam coords) + // Output options + int xRes; ///< image x-resolution + int yRes; ///< image y-resolution + Imath::V2i superSamp; ///< supersampling resolution + Imath::V2f filterWidth; ///< filter width + bool doFilter; ///< If false, turn off filtering & return raw samples + Options() : maxSplits(20), - xRes(640), - yRes(480), gridSize(16), clipNear(FLT_EPSILON), - clipFar(FLT_MAX) + clipFar(FLT_MAX), + xRes(640), + yRes(480), + superSamp(2,2), + filterWidth(2,2), + doFilter(true) { } }; diff --git a/prototypes/newcore/renderer.cpp b/prototypes/newcore/renderer.cpp index 91299a5..3aed508 100644 --- a/prototypes/newcore/renderer.cpp +++ b/prototypes/newcore/renderer.cpp @@ -27,6 +27,7 @@ #include "gridstorage.h" #include "microquadsampler.h" #include "sample.h" +#include "samplestorage.h" #include "simple.h" @@ -242,160 +243,10 @@ class TessellationContextImpl : public TessellationContext //------------------------------------------------------------------------------ -// Storage for samples positions and output fragments -class SampleStorage -{ - private: - int m_fragRowStride; ///< length of a row of samples. - int m_xRes; - int m_yRes; - - std::vector<Sample> m_samples; ///< sample positions - - std::vector<float> m_defaultFragment; ///< Default fragment channels - std::vector<float> m_fragments; ///< array of fragments - - /// Fill an array with the default no-hit fragment sample values - static void fillDefault(std::vector<float>& defaultFrag, - const OutvarSet& outVars) - { - int nchans = 0; - for(int i = 0, iend = outVars.size(); i < iend; ++i) - nchans += outVars[i].scalarSize(); - // Set up default values for samples. - defaultFrag.assign(nchans, 0.0f); - // Fill in default depth if relevant - int zIdx = outVars.find(StdOutInd::z); - if(zIdx != OutvarSet::npos) - { - int zOffset = outVars[zIdx].offset; - defaultFrag[zOffset] = FLT_MAX; - } - } - - public: - SampleStorage(const OutvarSet& outVars, const Options& opts) - : m_fragRowStride(0), - m_xRes(opts.xRes), - m_yRes(opts.yRes), - m_samples(), - m_defaultFragment(), - m_fragments() - { - int npixels = opts.xRes*opts.yRes; - // Initialize sample array - m_samples.resize(npixels); - for(int j = 0; j < opts.yRes; ++j) - { - for(int i = 0; i < opts.xRes; ++i) - m_samples[j*opts.xRes + i] = Sample(Vec2(i+0.5f, j+0.5f)); - } - // Initialize default fragment - fillDefault(m_defaultFragment, outVars); - int fragSize = m_defaultFragment.size(); - m_fragRowStride = opts.xRes*fragSize; - // Initialize fragment array - m_fragments.resize(fragSize*npixels); - const float* defFrag = &m_defaultFragment[0]; - for(int i = 0; i < npixels; ++i) - { - std::memcpy(&m_fragments[fragSize*i], defFrag, - fragSize*sizeof(float)); - } - } - - /// Iterator over a rectangular region of samples. - /// - /// Note that the implementation here is quite performance critical, - /// since it's inside one of the inner sampling loops. - /// - class Iterator - { - private: - int m_startx; - int m_endx; - int m_endy; - int m_x; - int m_y; - - int m_rowStride; ///< stride between end of one row & beginning of next. - int m_fragSize; ///< number of floats in a fragment - Sample* m_sample; ///< current sample data - float* m_fragment; ///< current fragment data - - public: - Iterator(const Box& bound, SampleStorage& storage) - { - // Bounding box for relevant samples, clamped to image - // extent. - m_startx = clamp(floor(bound.min.x), 0, storage.m_xRes); - m_endx = clamp(floor(bound.max.x)+1, 0, storage.m_xRes); - int starty = clamp(floor(bound.min.y), 0, storage.m_yRes); - m_endy = clamp(floor(bound.max.y)+1, 0, storage.m_yRes); - - m_x = m_startx; - m_y = starty; - // ensure !valid() if bound is empty in x-direction - if(m_startx >= m_endx) - m_y = m_endy; - - if(valid()) - { - m_rowStride = storage.m_xRes - (m_endx - m_startx); - m_fragSize = storage.fragmentSize(); - - int idx = m_y*storage.m_xRes + m_x; - m_sample = &storage.m_samples[idx]; - m_fragment = &storage.m_fragments[m_fragSize*idx]; - } - } - - /// Advance to next sample - Iterator& operator++() - { - ++m_x; - ++m_sample; - m_fragment += m_fragSize; - if(m_x == m_endx) - { - // Advance to next row. - m_x = m_startx; - ++m_y; - m_sample += m_rowStride; - m_fragment += m_rowStride*m_fragSize; - } - return *this; - } - - /// Determine whether current sample is in the bound - bool valid() const { return m_y < m_endy; } - - /// Get current sample data - Sample& sample() const { return *m_sample; } - /// Get current fragment storage - float* fragment() const { return m_fragment; } - }; - - Iterator begin(const Box& bound) - { - return Iterator(bound, *this); - } - - /// Get a scanline of the output image. - const float* outputScanline(int line) const - { - return &m_fragments[0] + line*m_fragRowStride; - } - - int fragmentSize() const { return m_defaultFragment.size(); } - const float* defaultFragment() const { return &m_defaultFragment[0]; } -}; - - -//------------------------------------------------------------------------------ // Renderer implementation, utility stuff. -static void writeHeader(TIFF* tif, Options& opts, int nchans, bool useFloat) +static void writeHeader(TIFF* tif, const Imath::V2i& imageSize, + int nchans, bool useFloat) { uint16 bitsPerSample = 8; uint16 photometric = PHOTOMETRIC_RGB; @@ -408,8 +259,8 @@ static void writeHeader(TIFF* tif, Options& opts, int nchans, bool useFloat) if(nchans == 1) photometric = PHOTOMETRIC_MINISBLACK; // Write TIFF header - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, uint32(opts.xRes)); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, uint32(opts.yRes)); + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, uint32(imageSize.x)); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, uint32(imageSize.y)); TIFFSetField(tif, TIFFTAG_ORIENTATION, uint16(ORIENTATION_TOPLEFT)); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, uint16(PLANARCONFIG_CONTIG)); TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, uint16(RESUNIT_NONE)); @@ -448,10 +299,40 @@ static void strided_memcpy(void* dest, const void* src, size_t nElems, } } +namespace { +template<typename T> +void clampOptBelow(T& val, const char* name, const T& minVal) +{ + if(val < minVal) + { + std::cerr << "Warning: Option " << name << " = " << val + << " is too small. Clamping to " << minVal << "\n"; + val = minVal; + } +} +} // anon namespace + +void Renderer::sanitizeOptions(Options& opts) +{ +#define CLAMP_OPT_BELOW(optname, minVal) \ + clampOptBelow(opts.optname, #optname, minVal) + CLAMP_OPT_BELOW(maxSplits, 0); + CLAMP_OPT_BELOW(gridSize, 1); + CLAMP_OPT_BELOW(clipNear, FLT_EPSILON); + CLAMP_OPT_BELOW(clipFar, opts.clipNear); + CLAMP_OPT_BELOW(xRes, 1); + CLAMP_OPT_BELOW(yRes, 1); + CLAMP_OPT_BELOW(superSamp.x, 1); + CLAMP_OPT_BELOW(superSamp.y, 1); + CLAMP_OPT_BELOW(filterWidth.x, 1.0f); + CLAMP_OPT_BELOW(filterWidth.y, 1.0f); +} + // Save image to a TIFF file. void Renderer::saveImages(const std::string& baseFileName) { int pixStride = m_sampStorage->fragmentSize(); + Imath::V2i imageSize = m_sampStorage->outputSize(); for(int i = 0, nFiles = m_outVars.size(); i < nFiles; ++i) { int nSamps = m_outVars[i].scalarSize(); @@ -470,22 +351,22 @@ void Renderer::saveImages(const std::string& baseFileName) // Don't quatize if we've got depth data. bool doQuantize = m_outVars[i] != Stdvar::z; - writeHeader(tif, m_opts, nSamps, !doQuantize); + writeHeader(tif, imageSize, nSamps, !doQuantize); // Write image data - int rowSize = nSamps*m_opts.xRes*(doQuantize ? 1 : sizeof(float)); + int rowSize = nSamps*imageSize.x*(doQuantize ? 1 : sizeof(float)); boost::scoped_array<uint8> lineBuf(new uint8[rowSize]); - for(int line = 0; line < m_opts.yRes; ++line) + for(int line = 0; line < imageSize.y; ++line) { const float* src = m_sampStorage->outputScanline(line) + m_outVars[i].offset; if(doQuantize) { - quantize(src, m_opts.xRes, nSamps, pixStride, lineBuf.get()); + quantize(src, imageSize.x, nSamps, pixStride, lineBuf.get()); } else { - strided_memcpy(lineBuf.get(), src, m_opts.xRes, + strided_memcpy(lineBuf.get(), src, imageSize.x, nSamps*sizeof(float), pixStride*sizeof(float)); } TIFFWriteScanline(tif, reinterpret_cast<tdata_t>(lineBuf.get()), @@ -500,12 +381,6 @@ void Renderer::saveImages(const std::string& baseFileName) //------------------------------------------------------------------------------ // Guts of the renderer -/// Initialize the sample and image arrays. -void Renderer::initSamples() -{ -} - - /// Push geometry into the render queue void Renderer::push(const boost::shared_ptr<Geometry>& geom, const SurfaceHolder& parentSurface) @@ -570,6 +445,7 @@ Renderer::Renderer(const Options& opts, const Mat4& camToScreen, m_sampStorage(), m_camToRas() { + sanitizeOptions(m_opts); // Set up output variables. Default is to use Cs. std::vector<OutvarSpec> outVarsInit; if(outVars.size() == 0) @@ -630,7 +506,6 @@ void Renderer::render() // Use the same tessellation context for all split/dice events TessellationContextImpl tessContext(*this); - initSamples(); while(!m_surfaces->empty()) { SurfaceHolder s = m_surfaces->top(); diff --git a/prototypes/newcore/renderer.h b/prototypes/newcore/renderer.h index ada0442..71668a7 100644 --- a/prototypes/newcore/renderer.h +++ b/prototypes/newcore/renderer.h @@ -113,9 +113,10 @@ class Renderer boost::scoped_ptr<SampleStorage> m_sampStorage; ///< Samples & fragments Mat4 m_camToRas; ///< Camera -> raster transformation + static void sanitizeOptions(Options& opts); + void saveImages(const std::string& baseFileName); - void initSamples(); void push(const boost::shared_ptr<Geometry>& geom, const SurfaceHolder& parentSurface); void push(const boost::shared_ptr<Grid>& grid, diff --git a/prototypes/newcore/samplestorage.cpp b/prototypes/newcore/samplestorage.cpp new file mode 100644 index 0000000..00dd14d --- /dev/null +++ b/prototypes/newcore/samplestorage.cpp @@ -0,0 +1,118 @@ +// Aqsis +// Copyright (C) 1997 - 2010, Paul C. Gregory +// +// Contact: pgr...@aq... +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "samplestorage.h" +#include "arrayview.h" + + +SampleStorage::SampleStorage(const OutvarSet& outVars, const Options& opts) + : m_opts(opts), + m_fragSize(0), + m_xSampRes(0), + m_ySampRes(0), + m_samples(), + m_defaultFragment(), + m_fragments(), + m_filterResults(), + m_doFilter(true), + m_filter() +{ + // Initialize cached filter coefficients + cacheFilter(m_filter, opts, m_filtExpand, m_disWidth); + + m_xSampRes = opts.xRes*opts.superSamp.x + 2*m_filtExpand.x; + m_ySampRes = opts.yRes*opts.superSamp.y + 2*m_filtExpand.y; + + // Initialize default fragment + fillDefault(m_defaultFragment, outVars); + m_fragSize = m_defaultFragment.size(); + + m_doFilter = opts.doFilter && m_disWidth != Imath::V2i(1,1); + // Allocate filter result array when necessary + if(m_doFilter) + m_filterResults.resize(m_xSampRes*m_fragSize); + + // Initialize sample array. Non-jittered for now. + int nsamples = m_xSampRes*m_ySampRes; + m_samples.resize(nsamples); + Vec2 invRes = Vec2(1.0f/opts.superSamp.x, 1.0f/opts.superSamp.y); + for(int j = 0; j < m_ySampRes; ++j) + { + for(int i = 0; i < m_xSampRes; ++i) + { + Vec2 pos = invRes*( Vec2(i, j) - Vec2(m_filtExpand) + + Vec2(0.5f) ); + m_samples[j*m_xSampRes + i] = Sample(pos); + } + } + + // Initialize fragment array using default fragment. + m_fragments.resize(m_fragSize*nsamples); + copy(FvecView(&m_fragments[0], m_fragSize), + ConstFvecView(&m_defaultFragment[0], m_fragSize, 0), + nsamples); +} + + +const float* SampleStorage::outputScanline(int line) const +{ + const int xSuperSamp = m_opts.superSamp.x; + const int ySuperSamp = m_opts.superSamp.y; + if(m_doFilter) + { + // Execute filter. + const int rowStride = m_fragSize*(m_xSampRes - m_disWidth.x); + for(int col = 0; col < m_opts.xRes; ++col) + { + // Zero channels of the current output + float* out = &m_filterResults[col*m_fragSize]; + std::memset(out, 0, m_fragSize*sizeof(float)); + // Iterate over samples in the current filter region. + // Non-seperable filter only for now. + const float* src + = &m_fragments[ (m_xSampRes*line*ySuperSamp + + col*xSuperSamp) * m_fragSize ]; + const float* filt = &m_filter[0]; + for(int k = 0; k < m_disWidth.y; ++k) + { + for(int j = 0; j < m_disWidth.x; ++j, ++filt) + { + float w = *filt; + for(int i = 0; i < m_fragSize; ++i, ++src) + out[i] += *src * w; + } + src += rowStride; + } + } + return &m_filterResults[0]; + } + else + { + return &m_fragments[0] + line*m_xSampRes*m_fragSize; + } +} + + +Imath::V2i SampleStorage::outputSize() +{ + if(m_doFilter) + return Imath::V2i(m_opts.xRes, m_opts.yRes); + else + return Imath::V2i(m_xSampRes, m_ySampRes); +} diff --git a/prototypes/newcore/samplestorage.h b/prototypes/newcore/samplestorage.h new file mode 100644 index 0000000..3c3700e --- /dev/null +++ b/prototypes/newcore/samplestorage.h @@ -0,0 +1,234 @@ +// Aqsis +// Copyright (C) 1997 - 2010, Paul C. Gregory +// +// Contact: pgr...@aq... +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef SAMPLESTORAGE_H_INCLUDED +#define SAMPLESTORAGE_H_INCLUDED + +#include <vector> + +#include "util.h" +#include "renderer.h" // For OutvarSet. +#include "sample.h" + +/// Storage for samples positions and output fragments +class SampleStorage +{ + public: + SampleStorage(const OutvarSet& outVars, const Options& opts); + + /// Get a scanline of the output image, filtering if necessary. + const float* outputScanline(int line) const; + /// Get the size of the output image. + Imath::V2i outputSize(); + + /// Get number of floats required to store a fragment + int fragmentSize() const { return m_defaultFragment.size(); } + /// Get array containing the default fragment values + const float* defaultFragment() const { return &m_defaultFragment[0]; } + + class Iterator; + + /// Start iterating over samples inside a given bound + Iterator begin(const Box& bound); + + + private: + // Stuff describing size of sample area + const Options& m_opts; ///< options structure + int m_fragSize; ///< length of single fragment storage + int m_xSampRes; ///< number of samples in x-direction + int m_ySampRes; ///< number of samples in y-direction + + // Stuff describing samples + std::vector<Sample> m_samples; ///< sample positions + + // Stuff describing fragments + std::vector<float> m_defaultFragment; ///< Default fragment channels + std::vector<float> m_fragments; ///< array of fragments + + // Temporary array for filtering results + mutable std::vector<float> m_filterResults; + + // Cached filter info + bool m_doFilter; ///< perform filtering ? + Imath::V2i m_filtExpand; ///< num samples to expand by for filter. + Imath::V2i m_disWidth; ///< discrete filter width + std::vector<float> m_filter; + + /// Fill an array with the default no-hit fragment sample values + static void fillDefault(std::vector<float>& defaultFrag, + const OutvarSet& outVars) + { + int nchans = 0; + for(int i = 0, iend = outVars.size(); i < iend; ++i) + nchans += outVars[i].scalarSize(); + // Set up default values for samples. + defaultFrag.assign(nchans, 0.0f); + // Fill in default depth if relevant + int zIdx = outVars.find(StdOutInd::z); + if(zIdx != OutvarSet::npos) + { + int zOffset = outVars[zIdx].offset; + defaultFrag[zOffset] = FLT_MAX; + } + } + + /// Compute the discrete filter size in sample widths + /// + /// The floating point filter radius implies a discrete filter size + static void filterSize(float radius, int sampsPerPix, int& size, + int& offset) + { + // Separate cases for even & odd numbers of samples per pixel. + if(sampsPerPix%2 == 0) + { + int discreteRadius = floor(radius*sampsPerPix + 0.5); + size = 2*discreteRadius; + offset = discreteRadius - sampsPerPix/2; + } + else + { + int discreteRadius = floor(radius*sampsPerPix); + size = 2*discreteRadius + 1; + offset = discreteRadius - sampsPerPix/2; + } + } + + static float pixelFilter(float x, float y, float xwidth, float ywidth) + { + // Use a gaussian filter for now... + x /= xwidth; + y /= ywidth; + return std::exp(-8*(x*x + y*y)); + } + + // Cache filter coefficients + // + // \param offset - Offset between top left sample in pixel & top-left + // sample in the filter region. + // \param disWidth - Discrete filter width in number of supersamples + static void cacheFilter(std::vector<float>& filter, const Options& opts, + Imath::V2i& offset, Imath::V2i& disWidth) + { + filterSize(opts.filterWidth.x/2, opts.superSamp.x, disWidth.x, offset.x); + filterSize(opts.filterWidth.y/2, opts.superSamp.x, disWidth.y, offset.y); + // Compute filter + filter.resize(disWidth.x*disWidth.y); + float* f = &filter[0]; + float totWeight = 0; + for(int j = 0; j < disWidth.y; ++j) + { + float y = (j-(disWidth.y-1)/2.0f)/opts.superSamp.y; + for(int i = 0; i < disWidth.x; ++i, ++f) + { + float x = (i-(disWidth.x-1)/2.0f)/opts.superSamp.x; + float w = pixelFilter(x, y, opts.filterWidth.x, + opts.filterWidth.y); + *f = w; + totWeight += w; + } + } + // Normalize total weight to 1. + float renorm = 1/totWeight; + for(int i = 0, iend = disWidth.y*disWidth.x; i < iend; ++i) + filter[i] *= renorm; + } +}; + +/// Iterator over a rectangular region of samples. +/// +/// Note that the implementation here is quite performance critical, +/// since it's inside one of the inner sampling loops. +/// +class SampleStorage::Iterator +{ + private: + int m_startx; + int m_endx; + int m_endy; + int m_x; + int m_y; + + int m_rowStride; ///< stride between end of one row & start of next + int m_fragSize; ///< number of floats in a fragment + Sample* m_sample; ///< current sample data + float* m_fragment; ///< current fragment data + + public: + Iterator(const Box& bound, SampleStorage& storage) + { + // Bounding box for relevant samples, clamped to image extent. + Imath::V2i bndMin = floor(vec2_cast(bound.min) + *storage.m_opts.superSamp) + storage.m_filtExpand; + Imath::V2i bndMax = floor(vec2_cast(bound.max) + *storage.m_opts.superSamp) + storage.m_filtExpand; + m_startx = clamp(bndMin.x, 0, storage.m_xSampRes); + m_endx = clamp(bndMax.x+1, 0, storage.m_xSampRes); + int starty = clamp(bndMin.y, 0, storage.m_ySampRes); + m_endy = clamp(bndMax.y+1, 0, storage.m_ySampRes); + + m_x = m_startx; + m_y = starty; + // ensure !valid() if bound is empty in x-direction + if(m_startx >= m_endx) + m_y = m_endy; + + if(valid()) + { + m_rowStride = storage.m_xSampRes - (m_endx - m_startx); + m_fragSize = storage.m_fragSize; + int idx = m_y*storage.m_xSampRes + m_x; + m_sample = &storage.m_samples[idx]; + m_fragment = &storage.m_fragments[m_fragSize*idx]; + } + } + + /// Advance to next sample + Iterator& operator++() + { + ++m_x; + ++m_sample; + m_fragment += m_fragSize; + if(m_x == m_endx) + { + // Advance to next row. + m_x = m_startx; + ++m_y; + m_sample += m_rowStride; + m_fragment += m_rowStride*m_fragSize; + } + return *this; + } + + /// Determine whether current sample is in the bound + bool valid() const { return m_y < m_endy; } + + /// Get current sample data + Sample& sample() const { return *m_sample; } + /// Get current fragment storage + float* fragment() const { return m_fragment; } +}; + + +inline SampleStorage::Iterator SampleStorage::begin(const Box& bound) +{ + return Iterator(bound, *this); +} + +#endif // SAMPLESTORAGE_H_INCLUDED diff --git a/prototypes/newcore/scenes/default.cpp b/prototypes/newcore/scenes/default.cpp index d55629f..c3c9065 100644 --- a/prototypes/newcore/scenes/default.cpp +++ b/prototypes/newcore/scenes/default.cpp @@ -125,6 +125,8 @@ void renderDefaultScene() opts.yRes = 1024; opts.gridSize = 8; opts.clipNear = 0.1; + opts.superSamp = Imath::V2i(2,2); + opts.filterWidth = Vec2(2,2); Attributes attrs; attrs.shadingRate = 1; diff --git a/prototypes/newcore/scenes/tenpatch.cpp b/prototypes/newcore/scenes/tenpatch.cpp index 3abcf5d..34d5309 100644 --- a/prototypes/newcore/scenes/tenpatch.cpp +++ b/prototypes/newcore/scenes/tenpatch.cpp @@ -47,6 +47,8 @@ void renderTenPatchScene() opts.yRes = 1024; opts.gridSize = 8; opts.clipNear = 0.1; + opts.superSamp = Imath::V2i(1,1); + opts.filterWidth = Vec2(1,1); Attributes attrs; attrs.shadingRate = 1; diff --git a/prototypes/newcore/util.h b/prototypes/newcore/util.h index 53ecaa9..e2d7525 100644 --- a/prototypes/newcore/util.h +++ b/prototypes/newcore/util.h @@ -162,6 +162,16 @@ inline int floor(T x) return ix - (x != ix); } +template<typename T> +inline int ceil(T x) +{ + int ix = static_cast<int>(x); + if(x <= 0) + return ix; + else + return ix + (x != ix); +} + inline float deg2rad(float d) { return (M_PI/180) * d; } inline float rad2deg(float r) { return (180/M_PI) * r; } @@ -234,6 +244,12 @@ inline Vec3 hybridRasterTransform(const Vec3& v, const Mat4& m) return Vec3(x*invW, y*invW, v.z); } +template<typename T> +inline Imath::V2i floor(const Imath::Vec2<T>& v) +{ + return Imath::V2i(floor(v.x), floor(v.y)); +} + #define ALLOCA(type, len) static_cast<type*>(alloca(len*sizeof(type))) #define FALLOCA(len) ALLOCA(float, len) ----------------------------------------------------------------------- Summary of changes: prototypes/newcore/CMakeLists.txt | 2 + prototypes/newcore/TODO.txt | 6 +- prototypes/newcore/experiments/float4.h | 1 + prototypes/newcore/options.h | 21 ++- prototypes/newcore/renderer.cpp | 207 ++++++---------------------- prototypes/newcore/renderer.h | 3 +- prototypes/newcore/samplestorage.cpp | 118 ++++++++++++++++ prototypes/newcore/samplestorage.h | 234 +++++++++++++++++++++++++++++++ prototypes/newcore/scenes/default.cpp | 2 + prototypes/newcore/scenes/tenpatch.cpp | 2 + prototypes/newcore/util.h | 16 ++ 11 files changed, 436 insertions(+), 176 deletions(-) create mode 100644 prototypes/newcore/samplestorage.cpp create mode 100644 prototypes/newcore/samplestorage.h hooks/post-receive -- Aqsis Renderer |