Showing preview only (236K chars total). Download the full file or copy to clipboard to get everything.
Repository: SmallVCM/SmallVCM
Branch: master
Commit: a13690f03d34
Files: 28
Total size: 226.4 KB
Directory structure:
gitextract_o0afi678/
├── .gitignore
├── LICENSE
├── Makefile
├── Makefile.osx
├── README
├── SmallVCM.sln
├── SmallVCM.vcxproj
├── SmallVCM.vcxproj.filters
└── src/
├── bsdf.hxx
├── camera.hxx
├── config.hxx
├── eyelight.hxx
├── frame.hxx
├── framebuffer.hxx
├── geometry.hxx
├── hashgrid.hxx
├── html_writer.hxx
├── lights.hxx
├── materials.hxx
├── math.hxx
├── pathtracer.hxx
├── ray.hxx
├── renderer.hxx
├── rng.hxx
├── scene.hxx
├── smallvcm.cxx
├── utils.hxx
└── vertexcm.hxx
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.bmp
*.ppm
*.pfm
*.hdr
*.suo
*.sdf
*.opensdf
*.html
SmallVCM.vcxproj.user
/build/*
/ipch/*
================================================
FILE: LICENSE
================================================
Notes:
Because I have been told this really ought to have a license,
I picked the MIT License (http://en.wikipedia.org/wiki/MIT_License).
If you need anything else, just drop me a line at tomas@davidovic.cz
License:
Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: Makefile
================================================
# This is under MIT licence
# Also, I am not at all proud of this makefile, feel free to make better
all:
g++ -o smallvcm ./src/smallvcm.cxx -O3 -std=c++0x -fopenmp
old_rng:
g++ -o smallvcm ./src/smallvcm.cxx -O3 -fopenmp -DLEGACY_RNG
clean:
rm smallvcm
unreport:
rm *.bmp index.html
================================================
FILE: Makefile.osx
================================================
# This is under MIT licence
# Also, I am not at all proud of this makefile, feel free to make better
all:
c++ -o smallvcm ./src/smallvcm.cxx -O3 -std=c++0x -stdlib=libc++ -DNO_OMP
old_rng:
c++ -o smallvcm ./src/smallvcm.cxx -O3 -DLEGACY_RNG -DNO_OMP
clean:
rm smallvcm
unreport:
rm *.bmp index.html
================================================
FILE: README
================================================
================================================================================
SmallVCM
================================================================================
SmallVCM is a small physically based renderer that implements the vertex
connection and merging algorithm described in the paper
"Light Transport Simulation with Vertex Connection and Merging"
Iliyan Georgiev, Jaroslav Krivanek, Tomas Davidovic, Philipp Slusallek
ACM Transactions on Graphics 31(6) (SIGGRAPH Asia 2012)
as well as a number of other algorithms, notably including progressive photon
mapping, (progressive) bidirectional photon mapping, and bidirectional path
tracing. The code compiles to a command line program that can render images of a
number of predefined scenes using the provided algorithms.
Disclaimer:
* Unless you really care, you can safely ignore all licenses in the source
code.
* This code is meant for educational purposes, and is not the code that was
used to render the images in the aforementioned paper. The provided scenes
are too simple to provide a complete understanding of the performance of
every implemented rendering algorithm and the differences between them.
* We are aware that the description below is not as detailed as it can be, and
apologize for any errors and confusion.
* If you have any questions and/or input w.r.t. improving and adding
explanations, feel free to contact Tomas Davidovic <tomas@davidovic.cz>,
the primary maintainer of this project, or Iliyan Georgiev <me@iliyan.com>,
the primary author of the above paper, and we will figure out whatever you
might need.
With that out of the way, let's go over the usual stuff.
================================================================================
1) INSTALLATION and COMPILATION
================================================================================
Synopsis: Compile smallvcm.cxx with OpenMP. If you don't have C++11 support,
define LEGACY_RNG (automatic for VS2008, found in rng.hxx).
The whole program consists of one C++ source file and a multiple header files.
It was developed in VS2010, however we did some limited testing on Linux, and
the provided Makefile works for g++ 4.4 and 4.6.3 at least.
The major hurdle can come from the fact that the C++11 <random> library is used,
but an alternative random number generator is also provided (`make old_rng` on
Linux). The code expects OpenMP is available, but getting rid of that is very
straightforward (simply comment out the few #pragma omp directives in the code).
Other than that, there are no dependencies, so simply compile smallvcm.cxx.
================================================================================
2) OPERATION
================================================================================
Quick start: Run `smallvcm --report -t 10`. In about 5-6 minutes it will
generate an index.html file that compares 7 different algorithms on 4 different
Cornell box variants (listed below).
The features and settings of the program can be explored by running
`smallvcm --help`, which outputs the following information:
Usage: smallvcm [ -s <scene_id> | -a <algorithm> |
-t <time> | -i <iteration> | -o <output_name> | --report ]
-s Selects the scene (default 0):
0 glossy small spheres + sun (directional)
1 glossy large mirror sphere + ceiling (area)
2 glossy small spheres + point
3 glossy small spheres + background (env. lighting)
-a Selects the rendering algorithm (default vcm):
el eye light
pt path tracing
lt light tracing
ppm progressive photon mapping
bpm bidirectional photon mapping
bpt bidirectional path tracing
vcm vertex connection and merging
-t Number of seconds to run the algorithm
-i Number of iterations to run the algorithm (default 1)
-o User specified output name, with extension .bmp or .hdr (default .bmp)
--report
Renders all scenes using all algorithms and generates an index.html file
that displays all images. Obeys the -t and -i options, ignores the rest.
Recommended usage: --report -i 1 (fastest preview)
Recommended usage: --report -t 10 (takes 5.5 min)
Recommended usage: --report -t 60 (takes 30 min)
Note: Time (-t) takes precedence over iterations (-i) if both are defined
'glossy' applies to the floor of the Cornell box.
'small spheres' variants have one mirror and one glass spheres in the box.
The program can run in two modes:
1) If --report is not set, a single image of the specified scene will be
rendered using the specified algorithm. If no option is specified, the output
is a 512x512 image of scene 0 is rendered using vertex connection and merging
with 1 iteration.
2) Setting the --report option renders all scenes using all algorithms, obeying
the (optional) number of iterations and/or maximum runtime for each
scene-algorithm configuration, ignoring the other options.
All default settings are set in the ParseCommandline function in config.hxx.
Some settings have no command line switch, but can be changed in the code:
mNumThreads Number of rendering threads (default 0, means 1 thread/core)
mBaseSeed Seed for random number generators (default 1234)
mMinPathLength Minimal path length (i.e. number of segments) (default 0)
mMaxPathLength Maximal path length (i.e. number of segments) (default 10)
mResolution Image resolution (default 512x512)
mRadiusFactor Scene diameter fraction for the merging radius (default 0.003)
mRadiusAlpha Merging radius reduction parameter (default 0.75)
================================================================================
3) VCM RENDERER
================================================================================
The VertexCM renderer implements a number of algorithms that share almost
identical code paths. The main differences between the algorithms lie in the
multiple importance sampling (MIS) weight computation, as well as shortcuts
through unused code. In order to make the understanding of the code easier,
below we describe how the VertexCM renderer operates. On a high level, it runs
in three stages:
1) Light sub-path tracing (ppm, bpm, bpt, vcm)
2) Range search hash grid construction over light vertices (ppm, bpm, vcm)
3) Camera sub-path tracing (all but lt)
PathVertex (also PathElement variant and typedefs CameraVertex and LightVertex)
is the basic structure describing the state of a random walk. The only unusual
members are dVCM, dVC, dVM, which are used for iterative MIS weight computation:
dVCM used for both connections (bpt, vcm) and merging (bpm, vcm)
dVC used for connections (bpt, vcm)
dVM used for merging (bpm, vcm)
Note: All bidirectional algorithms sample the same number of light and camera
sub-paths per iteration, which is the number of pixels in the image.
* Light tracing (lt)
Utilizes only light sub-path tracing. Each path vertex is directly connected
to camera and then discarded (i.e. not stored). No MIS, hash grid, or camera
tracing are used.
* Progressive photon mapping (ppm)
Light sub-paths are traced, storing their vertices (as LightVertex objects) on
surfaces with non-specular (i.e. non-delta) materials, and building a hash
grid over them. The camera sub-paths are traced until hitting a non-specular
surface, where merging with light vertices (i.e. photon lookup) is performed,
terminating the camera sub-path thereafter. No MIS is used, and the radiance
from directly hit lights is accounted for only when all surface interactions
on the path are specular.
* Bidirectional photon mapping (bpm)
An extension to ppm, terminates camera sub-paths stochastically (unlike ppm)
and performs merging at all non-specular vertices. MIS is used (dVCM and dVM)
to weight the different possible ways of constructing the same path by merging
at any (non-specular) interior path vertex.
* Bidirectional path tracing (bpt)
Light sub-paths are traced, their non-specular vertices are first connected to
the camera (as in light tracing) and then stored (without a hash grid). Next,
the camera sub-paths are traced, connecting each non-specular vertex to a
light source and to all non-specular vertices of the light sub-path
corresponding to the current pixel. MIS is used (dVCM, dVC).
* Vertex connection and merging (vcm)
Effectively a combination of bidirectional photon mapping and bidirectional
path tracing. Light sub-path tracing projects and stores the non-specular
vertices, and also builds a hash grid over them. In the camera sub-path
tracing, each vertex is connected to a light source, to the vertices of the
corresponding light sub-path, and also merged with the nearby vertices of all
light sub-paths. MIS is used (dVCM, dVM, dVC).
================================================================================
4) FEATURES and LIMITATIONS
================================================================================
The renderer was originally intended to be a compact reference implementation,
akin to smallpt, however it grew over time. Here is a list of the features and
the limitations of the framework:
Infrastructural features:
* All basic light source types -- area, point, directional, and env. map --
are supported, although the current env. map implementation uses a constant
radiance distribution.
* Basic surface scattering models are implemented, including diffuse, glossy
(Phong), as well as specular reflection and refraction. It should be fairly
straightforward to implement new materials. Also, the material interfaces
should suffice for most purposes, as the bidirectional algorithms provided
are already quite demanding on them.
* The material/shading instance for a given ray hit point is represented by
a BSDF object. In addition to storing the the surface scattering properties,
this object also holds the local shading frame, as well we the incoming
(fixed) direction; all its methods (sample, evaluate, pdf) compute their
results always w.r.t. this direction.
Rendering features:
* A simple renderer with eye light (dot normal) shading for fast previews.
Red color denotes backface orientation. Implementation is in eyelight.hxx.
* Traditional path tracing with next even estimation (area and env. map).
Kept as a separate implementation is in pathtracer.hxx.
* Light tracing (lt), progressive photon mapping (ppm), bidirectional photon
mapping (bpm), bidirectional path tracing (bpt), and our vertex connection
and merging (vcm). All these are implemented in the VertexCM renderer, with
code path switches for the different algorithms.
Limitations:
* No acceleration structure for ray intersection (can be added to scene.hxx).
* Scenes are hard-coded (see LoadCornellBox in scene.hxx).
* The ppm algorithm does not handle diffuse+specular materials correctly.
This limitation can be lifted by adding a parameter to the BSDF::Sample
method that would specify the types of scattering events to be sampled.
The ppm implementation could then be modified to continue camera sub-paths
only for the specular parts of diffuse+specular materials.
* Each area light is an individual triangle, and requires its very own
material for identification. This can and should be changed if code is
used for scenes with complex area light sources. Also, the way of finding
whether and which area light is hit by a random ray can be improved.
* No shading normals. Extending the framework with shading normals should
happen within the BSDF object (which already supports adjoint BSDFs required
by refraction).
* No infrastructural support for participating media or subsurface scattering.
================================================
FILE: SmallVCM.sln
================================================
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SmallVCM", "SmallVCM.vcxproj", "{50BA09E6-1299-41FF-B89E-234A6A9EB55B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{50BA09E6-1299-41FF-B89E-234A6A9EB55B}.Debug|Win32.ActiveCfg = Debug|Win32
{50BA09E6-1299-41FF-B89E-234A6A9EB55B}.Debug|Win32.Build.0 = Debug|Win32
{50BA09E6-1299-41FF-B89E-234A6A9EB55B}.Release|Win32.ActiveCfg = Release|Win32
{50BA09E6-1299-41FF-B89E-234A6A9EB55B}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
================================================
FILE: SmallVCM.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{50BA09E6-1299-41FF-B89E-234A6A9EB55B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>SmallVCM</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)\build\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)\build\$(Configuration)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)\build\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)\build\$(Configuration)\obj\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<OpenMPSupport>false</OpenMPSupport>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<OpenMPSupport>true</OpenMPSupport>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\smallvcm.cxx" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\bsdf.hxx" />
<ClInclude Include="src\config.hxx" />
<ClInclude Include="src\frame.hxx" />
<ClInclude Include="src\hashgrid.hxx" />
<ClInclude Include="src\html_writer.hxx" />
<ClInclude Include="src\pathtracer.hxx" />
<ClInclude Include="src\renderer.hxx" />
<ClInclude Include="src\eyelight.hxx" />
<ClInclude Include="src\camera.hxx" />
<ClInclude Include="src\framebuffer.hxx" />
<ClInclude Include="src\geometry.hxx" />
<ClInclude Include="src\lights.hxx" />
<ClInclude Include="src\materials.hxx" />
<ClInclude Include="src\math.hxx" />
<ClInclude Include="src\ray.hxx" />
<ClInclude Include="src\rng.hxx" />
<ClInclude Include="src\scene.hxx" />
<ClInclude Include="src\utils.hxx" />
<ClInclude Include="src\vertexcm.hxx" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
================================================
FILE: SmallVCM.vcxproj.filters
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\smallvcm.cxx">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\geometry.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\lights.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\materials.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\math.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ray.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\camera.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\scene.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\framebuffer.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\eyelight.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\renderer.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\frame.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\utils.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\pathtracer.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\hashgrid.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\rng.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\vertexcm.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\bsdf.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\html_writer.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\config.hxx">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
================================================
FILE: src/bsdf.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __BSDF_HXX__
#define __BSDF_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
#include "frame.hxx"
#include "ray.hxx"
#include "scene.hxx"
#include "utils.hxx"
//////////////////////////////////////////////////////////////////////////
// BSDF, most magic happens here
//
// One of important conventions is prefixing direction with World when
// are in world coordinates and with Local when they are in local frame,
// i.e., mFrame.
//
// Another important convention if suffix Fix and Gen.
// For PDF computation, we need to know which direction is given (Fix),
// and which is the generated (Gen) direction. This is important even
// when simply evaluating BSDF.
// In BPT, we call Evaluate() when directly connecting to light/camera.
// This gives us both directions required for evaluating BSDF.
// However, for MIS we also need to know probabilities of having sampled
// this path via BSDF sampling, and we need that for both possible directions.
// The Fix/Gen convention (along with Direct and Reverse for PDF) clearly
// establishes which PDF is which.
//
// The BSDF is also templated by direction of tracing, whether from camera
// (BSDF<false>) or from light (BSDF<true>). This is identical to Veach's
// Adjoint BSDF (except the name is more straightforward).
// For us this is only used when refracting.
#define EPS_PHONG 1e-3f
template<bool FixIsLight>
class BSDF
{
struct ComponentProbabilities
{
float diffProb;
float phongProb;
float reflProb;
float refrProb;
};
public:
enum Events
{
kNONE = 0,
kDiffuse = 1,
kPhong = 2,
kReflect = 4,
kRefract = 8,
kSpecular = (kReflect | kRefract),
kNonSpecular = (kDiffuse | kPhong),
kAll = (kSpecular | kNonSpecular)
};
public:
BSDF():mMaterialID(-1){};
BSDF(
const Ray &aRay,
const Isect &aIsect,
const Scene &aScene)
{
Setup(aRay, aIsect, aScene);
}
void Setup(
const Ray &aRay,
const Isect &aIsect,
const Scene &aScene)
{
mMaterialID = -1;
mFrame.SetFromZ(aIsect.normal);
mLocalDirFix = mFrame.ToLocal(-aRay.dir);
// reject rays that are too parallel with tangent plane
if(std::abs(mLocalDirFix.z) < EPS_COSINE)
{
return;
}
const Material &mat = aScene.GetMaterial(aIsect.matID);
GetComponentProbabilities(mat, mProbabilities);
mIsDelta = (mProbabilities.diffProb == 0) && (mProbabilities.phongProb == 0);
// now it becomes valid
mMaterialID = aIsect.matID;
}
/* \brief Given a direction, evaluates BSDF
*
* Returns value of BSDF, as well as cosine for the
* aWorldDirGen direction.
* Can return probability (w.r.t. solid angle W),
* of having sampled aWorldDirGen given mLocalDirFix (oDirectPdfW),
* and of having sampled mLocalDirFix given aWorldDirGen (oReversePdfW).
*
*/
Vec3f Evaluate(
const Scene &aScene,
const Vec3f &aWorldDirGen,
float &oCosThetaGen,
float *oDirectPdfW = NULL,
float *oReversePdfW = NULL) const
{
Vec3f result(0);
if(oDirectPdfW) *oDirectPdfW = 0;
if(oReversePdfW) *oReversePdfW = 0;
const Vec3f localDirGen = mFrame.ToLocal(aWorldDirGen);
if(localDirGen.z * mLocalDirFix.z < 0)
return result;
oCosThetaGen = std::abs(localDirGen.z);
const Material &mat = aScene.GetMaterial(mMaterialID);
result += EvaluateDiffuse(mat, localDirGen, oDirectPdfW, oReversePdfW);
result += EvaluatePhong(mat, localDirGen, oDirectPdfW, oReversePdfW);
return result;
}
/* \brief Given a direction, evaluates Pdf
*
* By default returns PDF with which would be aWorldDirGen
* generated from mLocalDirFix. When aEvalRevPdf == true,
* it provides PDF for the reverse direction.
*/
float Pdf(
const Scene &aScene,
const Vec3f &aWorldDirGen,
const bool aEvalRevPdf = false) const
{
const Vec3f localDirGen = mFrame.ToLocal(aWorldDirGen);
if(localDirGen.z * mLocalDirFix.z < 0)
return 0;
const Material &mat = aScene.GetMaterial(mMaterialID);
float directPdfW = 0;
float reversePdfW = 0;
PdfDiffuse(mat, localDirGen, &directPdfW, &reversePdfW);
PdfPhong(mat, localDirGen, &directPdfW, &reversePdfW);
return aEvalRevPdf ? reversePdfW : directPdfW;
}
/* \brief Given 3 random numbers, samples new direction from BSDF.
*
* Uses z component of random triplet to pick BSDF component from
* which it will sample direction. If non-specular component is chosen,
* it will also evaluate the other (non-specular) BSDF components.
* Return BSDF factor for given direction, as well as PDF choosing that direction.
* Can return event which has been sampled.
* If result is Vec3f(0,0,0), then the sample should be discarded.
*/
Vec3f Sample(
const Scene &aScene,
const Vec3f &aRndTriplet,
Vec3f &oWorldDirGen,
float &oPdfW,
float &oCosThetaGen,
uint *oSampledEvent = NULL) const
{
uint sampledEvent;
if(aRndTriplet.z < mProbabilities.diffProb)
sampledEvent = kDiffuse;
else if(aRndTriplet.z < mProbabilities.diffProb + mProbabilities.phongProb)
sampledEvent = kPhong;
else if(aRndTriplet.z < mProbabilities.diffProb + mProbabilities.phongProb + mProbabilities.reflProb)
sampledEvent = kReflect;
else
sampledEvent = kRefract;
if(oSampledEvent)
*oSampledEvent = sampledEvent;
const Material &mat = aScene.GetMaterial(mMaterialID);
oPdfW = 0;
Vec3f result(0);
Vec3f localDirGen;
if(sampledEvent == kDiffuse)
{
result += SampleDiffuse(mat, aRndTriplet.GetXY(), localDirGen, oPdfW);
if(result.IsZero())
return Vec3f(0);
result += EvaluatePhong(mat, localDirGen, &oPdfW);
}
else if(sampledEvent == kPhong)
{
result += SamplePhong(mat, aRndTriplet.GetXY(), localDirGen, oPdfW);
if(result.IsZero())
return Vec3f(0);
result += EvaluateDiffuse(mat, localDirGen, &oPdfW);
}
else if(sampledEvent == kReflect)
{
result += SampleReflect(mat, aRndTriplet.GetXY(), localDirGen, oPdfW);
if(result.IsZero())
return Vec3f(0);
}
else
{
result += SampleRefract(mat, aRndTriplet.GetXY(), localDirGen, oPdfW);
if(result.IsZero())
return Vec3f(0);
}
oCosThetaGen = std::abs(localDirGen.z);
if(oCosThetaGen < EPS_COSINE)
return Vec3f(0.f);
oWorldDirGen = mFrame.ToWorld(localDirGen);
return result;
}
bool IsValid() const { return mMaterialID >= 0; }
bool IsDelta() const { return mIsDelta; }
float ContinuationProb() const { return mContinuationProb; }
float CosThetaFix() const { return mLocalDirFix.z; }
Vec3f WorldDirFix() const { return mFrame.ToWorld(mLocalDirFix); }
private:
////////////////////////////////////////////////////////////////////////////
// Sampling methods
// All sampling methods take material, 2 random numbers [0-1[,
// and return BSDF factor, generated direction in local coordinates, and PDF
////////////////////////////////////////////////////////////////////////////
Vec3f SampleDiffuse(
const Material &aMaterial,
const Vec2f &aRndTuple,
Vec3f &oLocalDirGen,
float &oPdfW) const
{
if(mLocalDirFix.z < EPS_COSINE)
return Vec3f(0);
float unweightedPdfW;
oLocalDirGen = SampleCosHemisphereW(aRndTuple, &unweightedPdfW);
oPdfW += unweightedPdfW * mProbabilities.diffProb;
return aMaterial.mDiffuseReflectance * INV_PI_F;
}
Vec3f SamplePhong(
const Material &aMaterial,
const Vec2f &aRndTuple,
Vec3f &oLocalDirGen,
float &oPdfW) const
{
oLocalDirGen = SamplePowerCosHemisphereW(aRndTuple, aMaterial.mPhongExponent, NULL);
// Due to numeric issues in MIS, we actually need to compute all pdfs
// exactly the same way all the time!!!
const Vec3f reflLocalDirFixed = ReflectLocal(mLocalDirFix);
{
Frame frame;
frame.SetFromZ(reflLocalDirFixed);
oLocalDirGen = frame.ToWorld(oLocalDirGen);
}
const float dot_R_Wi = Dot(reflLocalDirFixed, oLocalDirGen);
if(dot_R_Wi <= EPS_PHONG)
return Vec3f(0.f);
PdfPhong(aMaterial, oLocalDirGen, &oPdfW);
const Vec3f rho = aMaterial.mPhongReflectance *
(aMaterial.mPhongExponent + 2.f) * 0.5f * INV_PI_F;
return rho * std::pow(dot_R_Wi, aMaterial.mPhongExponent);
}
Vec3f SampleReflect(
const Material &aMaterial,
const Vec2f &aRndTuple,
Vec3f &oLocalDirGen,
float &oPdfW) const
{
oLocalDirGen = ReflectLocal(mLocalDirFix);
oPdfW += mProbabilities.reflProb;
// BSDF is multiplied (outside) by cosine (oLocalDirGen.z),
// for mirror this shouldn't be done, so we pre-divide here instead
return mReflectCoeff * aMaterial.mMirrorReflectance /
std::abs(oLocalDirGen.z);
}
Vec3f SampleRefract(
const Material &aMaterial,
const Vec2f &aRndTuple,
Vec3f &oLocalDirGen,
float &oPdfW) const
{
if(aMaterial.mIOR < 0)
return Vec3f(0);
float cosI = mLocalDirFix.z;
float cosT;
float etaIncOverEtaTrans;
if(cosI < 0.f) // hit from inside
{
etaIncOverEtaTrans = aMaterial.mIOR;
cosI = -cosI;
cosT = 1.f;
}
else
{
etaIncOverEtaTrans = 1.f / aMaterial.mIOR;
cosT = -1.f;
}
const float sinI2 = 1.f - cosI * cosI;
const float sinT2 = Sqr(etaIncOverEtaTrans) * sinI2;
if(sinT2 < 1.f) // no total internal reflection
{
cosT *= std::sqrt(std::max(0.f, 1.f - sinT2));
oLocalDirGen = Vec3f(
-etaIncOverEtaTrans * mLocalDirFix.x,
-etaIncOverEtaTrans * mLocalDirFix.y,
cosT);
oPdfW += mProbabilities.refrProb;
const float refractCoeff = 1.f - mReflectCoeff;
// only camera paths are multiplied by this factor, and etas
// are swapped because radiance flows in the opposite direction
if(!FixIsLight)
return Vec3f(refractCoeff * Sqr(etaIncOverEtaTrans) / std::abs(cosT));
else
return Vec3f(refractCoeff / std::abs(cosT));
}
//else total internal reflection, do nothing
oPdfW += 0.f;
return Vec3f(0.f);
}
////////////////////////////////////////////////////////////////////////////
// Evaluation methods
////////////////////////////////////////////////////////////////////////////
Vec3f EvaluateDiffuse(
const Material &aMaterial,
const Vec3f &aLocalDirGen,
float *oDirectPdfW = NULL,
float *oReversePdfW = NULL) const
{
if(mProbabilities.diffProb == 0)
return Vec3f(0);
if(mLocalDirFix.z < EPS_COSINE || aLocalDirGen.z < EPS_COSINE)
return Vec3f(0);
if(oDirectPdfW)
*oDirectPdfW += mProbabilities.diffProb * std::max(0.f, aLocalDirGen.z * INV_PI_F);
if(oReversePdfW)
*oReversePdfW += mProbabilities.diffProb * std::max(0.f, mLocalDirFix.z * INV_PI_F);
return aMaterial.mDiffuseReflectance * INV_PI_F;
}
Vec3f EvaluatePhong(
const Material &aMaterial,
const Vec3f &aLocalDirGen,
float *oDirectPdfW = NULL,
float *oReversePdfW = NULL) const
{
if(mProbabilities.phongProb == 0)
return Vec3f(0);
if(mLocalDirFix.z < EPS_COSINE || aLocalDirGen.z < EPS_COSINE)
return Vec3f(0);
// assumes this is never called when rejectShadingCos(oLocalDirGen.z) is true
const Vec3f reflLocalDirIn = ReflectLocal(mLocalDirFix);
const float dot_R_Wi = Dot(reflLocalDirIn, aLocalDirGen);
if(dot_R_Wi <= EPS_PHONG)
return Vec3f(0.f);
if(oDirectPdfW || oReversePdfW)
{
// the sampling is symmetric
const float pdfW = mProbabilities.phongProb *
PowerCosHemispherePdfW(reflLocalDirIn, aLocalDirGen, aMaterial.mPhongExponent);
if(oDirectPdfW)
*oDirectPdfW += pdfW;
if(oReversePdfW)
*oReversePdfW += pdfW;
}
const Vec3f rho = aMaterial.mPhongReflectance *
(aMaterial.mPhongExponent + 2.f) * 0.5f * INV_PI_F;
return rho * std::pow(dot_R_Wi, aMaterial.mPhongExponent);
}
////////////////////////////////////////////////////////////////////////////
// Pdf methods
////////////////////////////////////////////////////////////////////////////
void PdfDiffuse(
const Material &aMaterial,
const Vec3f &aLocalDirGen,
float *oDirectPdfW = NULL,
float *oReversePdfW = NULL) const
{
if(mProbabilities.diffProb == 0)
return;
if(oDirectPdfW)
*oDirectPdfW += mProbabilities.diffProb *
std::max(0.f, aLocalDirGen.z * INV_PI_F);
if(oReversePdfW)
*oReversePdfW += mProbabilities.diffProb *
std::max(0.f, mLocalDirFix.z * INV_PI_F);
}
void PdfPhong(
const Material &aMaterial,
const Vec3f &aLocalDirGen,
float *oDirectPdfW = NULL,
float *oReversePdfW = NULL) const
{
if(mProbabilities.phongProb == 0)
return;
// assumes this is never called when rejectShadingCos(oLocalDirGen.z) is true
const Vec3f reflLocalDirIn = ReflectLocal(mLocalDirFix);
const float dot_R_Wi = Dot(reflLocalDirIn, aLocalDirGen);
if(dot_R_Wi <= EPS_PHONG)
return;
if(oDirectPdfW || oReversePdfW)
{
// the sampling is symmetric
const float pdfW = PowerCosHemispherePdfW(reflLocalDirIn, aLocalDirGen,
aMaterial.mPhongExponent) * mProbabilities.phongProb;
if(oDirectPdfW)
*oDirectPdfW += pdfW;
if(oReversePdfW)
*oReversePdfW += pdfW;
}
}
////////////////////////////////////////////////////////////////////////////
// Albedo methods
////////////////////////////////////////////////////////////////////////////
float AlbedoDiffuse(const Material& aMaterial) const
{
return Luminance(aMaterial.mDiffuseReflectance);
}
float AlbedoPhong(const Material& aMaterial) const
{
return Luminance(aMaterial.mPhongReflectance);
}
float AlbedoReflect(const Material& aMaterial) const
{
return Luminance(aMaterial.mMirrorReflectance);
}
float AlbedoRefract(const Material& aMaterial) const
{
return aMaterial.mIOR > 0.f ? 1.f : 0.f;
}
void GetComponentProbabilities(
const Material &aMaterial,
ComponentProbabilities &oProbabilities)
{
mReflectCoeff = FresnelDielectric(mLocalDirFix.z, aMaterial.mIOR);
const float albedoDiffuse = AlbedoDiffuse(aMaterial);
const float albedoPhong = AlbedoPhong(aMaterial);
const float albedoReflect = mReflectCoeff * AlbedoReflect(aMaterial);
const float albedoRefract = (1.f - mReflectCoeff) * AlbedoRefract(aMaterial);
const float totalAlbedo = albedoDiffuse + albedoPhong + albedoReflect + albedoRefract;
if(totalAlbedo < 1e-9f)
{
oProbabilities.diffProb = 0.f;
oProbabilities.phongProb = 0.f;
oProbabilities.reflProb = 0.f;
oProbabilities.refrProb = 0.f;
mContinuationProb = 0.f;
}
else
{
oProbabilities.diffProb = albedoDiffuse / totalAlbedo;
oProbabilities.phongProb = albedoPhong / totalAlbedo;
oProbabilities.reflProb = albedoReflect / totalAlbedo;
oProbabilities.refrProb = albedoRefract / totalAlbedo;
// The continuation probability is max component from reflectance.
// That way the weight of sample will never rise.
// Luminance is another very valid option.
mContinuationProb =
(aMaterial.mDiffuseReflectance +
aMaterial.mPhongReflectance +
mReflectCoeff * aMaterial.mMirrorReflectance).Max() +
(1.f - mReflectCoeff);
mContinuationProb = std::min(1.f, std::max(0.f, mContinuationProb));
}
}
private:
int mMaterialID; //!< Id of scene material, < 0 ~ invalid
Frame mFrame; //!< Local frame of reference
Vec3f mLocalDirFix; //!< Incoming (fixed) direction, in local
bool mIsDelta; //!< True when material is purely specular
ComponentProbabilities mProbabilities; //!< Sampling probabilities
float mContinuationProb; //!< Russian roulette probability
float mReflectCoeff; //!< Fresnel reflection coefficient (for glass)
};
#endif //__BSDF_HXX__
================================================
FILE: src/camera.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __CAMERA_HXX__
#define __CAMERA_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
#include "ray.hxx"
class Camera
{
public:
void Setup(
const Vec3f &aPosition,
const Vec3f &aForward,
const Vec3f &aUp,
const Vec2f &aResolution,
float aHorizontalFOV)
{
const Vec3f forward = Normalize(aForward);
const Vec3f up = Normalize(Cross(aUp, -forward));
const Vec3f left = Cross(-forward, up);
mPosition = aPosition;
mForward = forward;
mResolution = aResolution;
const Vec3f pos(
Dot(up, aPosition),
Dot(left, aPosition),
Dot(-forward, aPosition));
Mat4f worldToCamera = Mat4f::Identity();
worldToCamera.SetRow(0, up, -pos.x);
worldToCamera.SetRow(1, left, -pos.y);
worldToCamera.SetRow(2, -forward, -pos.z);
const Mat4f perspective = Mat4f::Perspective(aHorizontalFOV, 0.1f, 10000.f);
const Mat4f worldToNScreen = perspective * worldToCamera;
const Mat4f nscreenToWorld = Invert(worldToNScreen);
mWorldToRaster =
Mat4f::Scale(Vec3f(aResolution.x * 0.5f, aResolution.y * 0.5f, 0)) *
Mat4f::Translate(Vec3f(1.f, 1.f, 0)) * worldToNScreen;
mRasterToWorld = nscreenToWorld *
Mat4f::Translate(Vec3f(-1.f, -1.f, 0)) *
Mat4f::Scale(Vec3f(2.f / aResolution.x, 2.f / aResolution.y, 0));
const float tanHalfAngle = std::tan(aHorizontalFOV * PI_F / 360.f);
mImagePlaneDist = aResolution.x / (2.f * tanHalfAngle);
}
int RasterToIndex(const Vec2f &aPixelCoords) const
{
return int(std::floor(aPixelCoords.x) + std::floor(aPixelCoords.y) * mResolution.x);
}
Vec2f IndexToRaster(const int &aPixelIndex) const
{
const float y = std::floor(aPixelIndex / mResolution.x);
const float x = float(aPixelIndex) - y * mResolution.x;
return Vec2f(x, y);
}
Vec3f RasterToWorld(const Vec2f &aRasterXY) const
{
return mRasterToWorld.TransformPoint(Vec3f(aRasterXY.x, aRasterXY.y, 0));
}
Vec2f WorldToRaster(const Vec3f &aWorldPos) const
{
Vec3f temp = mWorldToRaster.TransformPoint(aWorldPos);
return Vec2f(temp.x, temp.y);
}
// returns false when raster position is outside screen space
bool CheckRaster(const Vec2f &aRasterPos) const
{
return aRasterPos.x >= 0 && aRasterPos.y >= 0 &&
aRasterPos.x < mResolution.x && aRasterPos.y < mResolution.y;
}
Ray GenerateRay(const Vec2f &aRasterXY) const
{
const Vec3f worldRaster = RasterToWorld(aRasterXY);
Ray res;
res.org = mPosition;
res.dir = Normalize(worldRaster - mPosition);
res.tmin = 0;
return res;
}
public:
Vec3f mPosition;
Vec3f mForward;
Vec2f mResolution;
Mat4f mRasterToWorld;
Mat4f mWorldToRaster;
float mImagePlaneDist;
};
#endif //__CAMERA_HXX__
================================================
FILE: src/config.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __CONFIG_HXX__
#define __CONFIG_HXX__
#include <vector>
#include <cmath>
#include <time.h>
#include <cstdlib>
#include "math.hxx"
#include "ray.hxx"
#include "geometry.hxx"
#include "camera.hxx"
#include "framebuffer.hxx"
#include "scene.hxx"
#include "eyelight.hxx"
#include "pathtracer.hxx"
#include "bsdf.hxx"
#include "vertexcm.hxx"
#include "html_writer.hxx"
#ifndef NO_OMP
#include <omp.h>
#endif
#include <string>
#include <set>
#include <sstream>
// Renderer configuration, holds algorithm, scene, and all other settings
struct Config
{
enum Algorithm
{
kEyeLight,
kPathTracing,
kLightTracing,
kProgressivePhotonMapping,
kBidirectionalPhotonMapping,
kBidirectionalPathTracing,
kVertexConnectionMerging,
kAlgorithmMax
};
static const char* GetName(Algorithm aAlgorithm)
{
static const char* algorithmNames[7] =
{
"eye light",
"path tracing",
"light tracing",
"progressive photon mapping",
"bidirectional photon mapping",
"bidirectional path tracing",
"vertex connection and merging"
};
if(aAlgorithm < 0 || aAlgorithm > 7)
return "unknown algorithm";
return algorithmNames[aAlgorithm];
}
static const char* GetAcronym(Algorithm aAlgorithm)
{
static const char* algorithmNames[7] = {
"el", "pt", "lt", "ppm", "bpm", "bpt", "vcm" };
if(aAlgorithm < 0 || aAlgorithm > 7)
return "unknown";
return algorithmNames[aAlgorithm];
}
const Scene *mScene;
Algorithm mAlgorithm;
int mIterations;
float mMaxTime;
float mRadiusFactor;
float mRadiusAlpha;
Framebuffer *mFramebuffer;
int mNumThreads;
int mBaseSeed;
uint mMaxPathLength;
uint mMinPathLength;
std::string mOutputName;
Vec2i mResolution;
bool mFullReport; // ignore scene and algorithm and do html report instead
};
// Utility function, essentially a renderer factory
AbstractRenderer* CreateRenderer(
const Config& aConfig,
const int aSeed)
{
const Scene& scene = *aConfig.mScene;
switch(aConfig.mAlgorithm)
{
case Config::kEyeLight:
return new EyeLight(scene, aSeed);
case Config::kPathTracing:
return new PathTracer(scene, aSeed);
case Config::kLightTracing:
return new VertexCM(scene, VertexCM::kLightTrace,
aConfig.mRadiusFactor, aConfig.mRadiusAlpha, aSeed);
case Config::kProgressivePhotonMapping:
return new VertexCM(scene, VertexCM::kPpm,
aConfig.mRadiusFactor, aConfig.mRadiusAlpha, aSeed);
case Config::kBidirectionalPhotonMapping:
return new VertexCM(scene, VertexCM::kBpm,
aConfig.mRadiusFactor, aConfig.mRadiusAlpha, aSeed);
case Config::kBidirectionalPathTracing:
return new VertexCM(scene, VertexCM::kBpt,
aConfig.mRadiusFactor, aConfig.mRadiusAlpha, aSeed);
case Config::kVertexConnectionMerging:
return new VertexCM(scene, VertexCM::kVcm,
aConfig.mRadiusFactor, aConfig.mRadiusAlpha, aSeed);
default:
printf("Unknown algorithm!!\n");
exit(2);
}
}
// Scene configurations
uint g_SceneConfigs[] = {
Scene::kGlossyFloor | Scene::kBothSmallSpheres | Scene::kLightSun,
Scene::kGlossyFloor | Scene::kLargeMirrorSphere | Scene::kLightCeiling,
Scene::kGlossyFloor | Scene::kBothSmallSpheres | Scene::kLightPoint,
Scene::kGlossyFloor | Scene::kBothSmallSpheres | Scene::kLightBackground
};
std::string DefaultFilename(
const uint aSceneConfig,
const Scene &aScene,
const Config::Algorithm aAlgorithm)
{
std::string filename;
// if scene has glossy floor, name will be prefixed by g
if((aSceneConfig & Scene::kGlossyFloor) != 0)
filename = "g";
// We use scene acronym
filename += aScene.mSceneAcronym;
// We add acronym of the used algorithm
filename += "_";
filename += Config::GetAcronym(aAlgorithm);
// And it will be written as bmp
filename += ".bmp";
return filename;
}
// Utility function, gives length of array
template <typename T, size_t N>
inline int SizeOfArray( const T(&)[ N ] )
{
return int(N);
}
void PrintRngWarning()
{
#if defined(LEGACY_RNG)
printf("The code was not compiled for C++11.\n");
printf("It will be using Tiny Encryption Algorithm-based"
"random number generator.\n");
printf("This is worse than the Mersenne Twister from C++11.\n");
printf("Consider setting up for C++11.\n");
printf("Visual Studio 2010, and g++ 4.6.3 and later work.\n\n");
#endif
}
void PrintHelp(const char *argv[])
{
printf("\n");
printf("Usage: %s [ -s <scene_id> | -a <algorithm> |\n", argv[0]);
printf(" -t <time> | -i <iteration> | -o <output_name> | --report ]\n\n");
printf(" -s Selects the scene (default 0):\n");
for(int i = 0; i < SizeOfArray(g_SceneConfigs); i++)
printf(" %d %s\n", i, Scene::GetSceneName(g_SceneConfigs[i]).c_str());
printf(" -a Selects the rendering algorithm (default vcm):\n");
for(int i = 0; i < (int)Config::kAlgorithmMax; i++)
printf(" %-3s %s\n",
Config::GetAcronym(Config::Algorithm(i)),
Config::GetName(Config::Algorithm(i)));
printf(" -t Number of seconds to run the algorithm\n");
printf(" -i Number of iterations to run the algorithm (default 1)\n");
printf(" -o User specified output name, with extension .bmp or .hdr (default .bmp)\n");
printf(" --report\n");
printf(" Renders all scenes using all algorithms and generates an index.html file\n");
printf(" that displays all images. Obeys the -t and -i options, ignores the rest.\n");
printf(" Recommended usage: --report -i 1 (fastest preview)\n");
printf(" Recommended usage: --report -t 10 (takes 5.5 mins)\n");
printf(" Recommended usage: --report -t 60 (takes 30 mins)\n");
printf("\n Note: Time (-t) takes precedence over iterations (-i) if both are defined\n");
}
// Parses command line, setting up config
void ParseCommandline(int argc, const char *argv[], Config &oConfig)
{
// Parameters marked with [cmd] can be change from command line
oConfig.mScene = NULL; // [cmd] When NULL, renderer will not run
oConfig.mAlgorithm = Config::kAlgorithmMax; // [cmd]
oConfig.mIterations = 1; // [cmd]
oConfig.mMaxTime = -1.f; // [cmd]
oConfig.mOutputName = ""; // [cmd]
oConfig.mNumThreads = 0;
oConfig.mBaseSeed = 1234;
oConfig.mMaxPathLength = 10;
oConfig.mMinPathLength = 0;
oConfig.mResolution = Vec2i(512, 512);
oConfig.mFullReport = false;
oConfig.mRadiusFactor = 0.003f;
oConfig.mRadiusAlpha = 0.75f;
//oConfig.mFramebuffer = NULL; // this is never set by any parameter
int sceneID = 0; // default 0
// Load arguments
for(int i=1; i<argc; i++)
{
std::string arg(argv[i]);
// print help string (at any position)
if(arg == "-h" || arg == "--help" || arg == "/?")
{
PrintHelp(argv);
return;
}
if(arg[0] != '-') // all our commands start with -
{
continue;
}
else if(arg == "--report")
{
oConfig.mFullReport = true;
}
else if(arg == "-s") // scene to load
{
if(++i == argc)
{
printf("Missing <sceneID> argument, please see help (-h)\n");
return;
}
std::istringstream iss(argv[i]);
iss >> sceneID;
if(iss.fail() || sceneID < 0 || sceneID >= SizeOfArray(g_SceneConfigs))
{
printf("Invalid <sceneID> argument, please see help (-h)\n");
return;
}
}
else if(arg == "-a") // algorithm to use
{
if(++i == argc)
{
printf("Missing <algorithm> argument, please see help (-h)\n");
return;
}
std::string alg(argv[i]);
for(int i=0; i<Config::kAlgorithmMax; i++)
if(alg == Config::GetAcronym(Config::Algorithm(i)))
oConfig.mAlgorithm = Config::Algorithm(i);
if(oConfig.mAlgorithm == Config::kAlgorithmMax)
{
printf("Invalid <algorithm> argument, please see help (-h)\n");
return;
}
}
else if(arg == "-i") // number of iterations to run
{
if(++i == argc)
{
printf("Missing <iteration> argument, please see help (-h)\n");
return;
}
std::istringstream iss(argv[i]);
iss >> oConfig.mIterations;
if(iss.fail() || oConfig.mIterations < 1)
{
printf("Invalid <iteration> argument, please see help (-h)\n");
return;
}
}
else if(arg == "-t") // number of seconds to run
{
if(++i == argc)
{
printf("Missing <time> argument, please see help (-h)\n");
return;
}
std::istringstream iss(argv[i]);
iss >> oConfig.mMaxTime;
if(iss.fail() || oConfig.mMaxTime < 0)
{
printf("Invalid <time> argument, please see help (-h)\n");
return;
}
oConfig.mIterations = -1; // time has precedence
}
else if(arg == "-o") // number of seconds to run
{
if(++i == argc)
{
printf("Missing <output_name> argument, please see help (-h)\n");
return;
}
oConfig.mOutputName = argv[i];
if(oConfig.mOutputName.length() == 0)
{
printf("Invalid <output_name> argument, please see help (-h)\n");
return;
}
}
}
// When doing full report, we ignore algorithm and scene settings
if(oConfig.mFullReport)
return;
// Check algorithm was selected
if(oConfig.mAlgorithm == Config::kAlgorithmMax)
{
oConfig.mAlgorithm = Config::kVertexConnectionMerging;
}
// Load scene
Scene *scene = new Scene;
scene->LoadCornellBox(oConfig.mResolution, g_SceneConfigs[sceneID]);
scene->BuildSceneSphere();
oConfig.mScene = scene;
// If no output name is chosen, create a default one
if(oConfig.mOutputName.length() == 0)
{
oConfig.mOutputName = DefaultFilename(g_SceneConfigs[sceneID],
*oConfig.mScene, oConfig.mAlgorithm);
}
// Check if output name has valid extension (.bmp or .hdr) and if not add .bmp
std::string extension = "";
if(oConfig.mOutputName.length() > 4) // must be at least 1 character before .bmp
extension = oConfig.mOutputName.substr(
oConfig.mOutputName.length() - 4, 4);
if(extension != ".bmp" && extension != ".hdr")
oConfig.mOutputName += ".bmp";
}
#endif //__CONFIG_HXX__
================================================
FILE: src/eyelight.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __EYELIGHT_HXX__
#define __EYELIGHT_HXX__
#include <vector>
#include <cmath>
#ifndef NO_OMP
#include <omp.h>
#endif
#include "renderer.hxx"
#include "rng.hxx"
class EyeLight : public AbstractRenderer
{
public:
EyeLight(
const Scene& aScene,
int aSeed = 1234
) :
AbstractRenderer(aScene), mRng(aSeed)
{}
virtual void RunIteration(int aIteration)
{
const int resX = int(mScene.mCamera.mResolution.x);
const int resY = int(mScene.mCamera.mResolution.y);
for(int pixID = 0; pixID < resX * resY; pixID++)
{
//////////////////////////////////////////////////////////////////////////
// Generate ray
const int x = pixID % resX;
const int y = pixID / resX;
const Vec2f sample = Vec2f(float(x), float(y)) +
(aIteration == 1 ? Vec2f(0.5f) : mRng.GetVec2f());
Ray ray = mScene.mCamera.GenerateRay(sample);
Isect isect;
isect.dist = 1e36f;
if(mScene.Intersect(ray, isect))
{
float dotLN = Dot(isect.normal, -ray.dir);
if(dotLN > 0)
mFramebuffer.AddColor(sample, Vec3f(dotLN));
else
mFramebuffer.AddColor(sample, Vec3f(-dotLN, 0, 0));
}
}
mIterations++;
}
Rng mRng;
};
#endif //__EYELIGHT_HXX__
================================================
FILE: src/frame.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __FRAME_HXX__
#define __FRAME_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
class Frame
{
public:
Frame()
{
mX = Vec3f(1,0,0);
mY = Vec3f(0,1,0);
mZ = Vec3f(0,0,1);
};
Frame(
const Vec3f& x,
const Vec3f& y,
const Vec3f& z
) :
mX(x),
mY(y),
mZ(z)
{}
void SetFromZ(const Vec3f& z)
{
Vec3f tmpZ = mZ = Normalize(z);
Vec3f tmpX = (std::abs(tmpZ.x) > 0.99f) ? Vec3f(0,1,0) : Vec3f(1,0,0);
mY = Normalize( Cross(tmpZ, tmpX) );
mX = Cross(mY, tmpZ);
}
Vec3f ToWorld(const Vec3f& a) const
{
return mX * a.x + mY * a.y + mZ * a.z;
}
Vec3f ToLocal(const Vec3f& a) const
{
return Vec3f(Dot(a, mX), Dot(a, mY), Dot(a, mZ));
}
const Vec3f& Binormal() const { return mX; }
const Vec3f& Tangent () const { return mY; }
const Vec3f& Normal () const { return mZ; }
public:
Vec3f mX, mY, mZ;
};
#endif //__FRAME_HXX__
================================================
FILE: src/framebuffer.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __FRAMEBUFFER_HXX__
#define __FRAMEBUFFER_HXX__
#include <vector>
#include <cmath>
#include <fstream>
#include <string.h>
#include "utils.hxx"
class Framebuffer
{
public:
Framebuffer()
{}
//////////////////////////////////////////////////////////////////////////
// Accumulation
void AddColor(
const Vec2f& aSample,
const Vec3f& aColor)
{
if(aSample.x < 0 || aSample.x >= mResolution.x)
return;
if(aSample.y < 0 || aSample.y >= mResolution.y)
return;
int x = int(aSample.x);
int y = int(aSample.y);
mColor[x + y * mResX] = mColor[x + y * mResX] + aColor;
}
//////////////////////////////////////////////////////////////////////////
// Methods for framebuffer operations
void Setup(const Vec2f& aResolution)
{
mResolution = aResolution;
mResX = int(aResolution.x);
mResY = int(aResolution.y);
mColor.resize(mResX * mResY);
Clear();
}
void Clear()
{
memset(&mColor[0], 0, sizeof(Vec3f) * mColor.size());
}
void Add(const Framebuffer& aOther)
{
for(size_t i=0; i<mColor.size(); i++)
mColor[i] = mColor[i] + aOther.mColor[i];
}
void Scale(float aScale)
{
for(size_t i=0; i<mColor.size(); i++)
mColor[i] = mColor[i] * Vec3f(aScale);
}
//////////////////////////////////////////////////////////////////////////
// Statistics
float TotalLuminance()
{
float lum = 0;
for(int y=0; y<mResY; y++)
{
for(int x=0; x<mResX; x++)
{
lum += Luminance(mColor[x + y*mResX]);
}
}
return lum;
}
//////////////////////////////////////////////////////////////////////////
// Saving
void SavePPM(
const char *aFilename,
float aGamma = 1.f)
{
const float invGamma = 1.f / aGamma;
std::ofstream ppm(aFilename);
ppm << "P3" << std::endl;
ppm << mResX << " " << mResY << std::endl;
ppm << "255" << std::endl;
Vec3f *ptr = &mColor[0];
for(int y=0; y<mResY; y++)
{
for(int x=0; x<mResX; x++)
{
ptr = &mColor[x + y*mResX];
int r = int(std::pow(ptr->x, invGamma) * 255.f);
int g = int(std::pow(ptr->y, invGamma) * 255.f);
int b = int(std::pow(ptr->z, invGamma) * 255.f);
ppm << std::min(255, std::max(0, r)) << " "
<< std::min(255, std::max(0, g)) << " "
<< std::min(255, std::max(0, b)) << " ";
}
ppm << std::endl;
}
}
void SavePFM(const char* aFilename)
{
std::ofstream ppm(aFilename, std::ios::binary);
ppm << "PF" << std::endl;
ppm << mResX << " " << mResY << std::endl;
ppm << "-1" << std::endl;
ppm.write(reinterpret_cast<const char*>(&mColor[0]),
mColor.size() * sizeof(Vec3f));
}
//////////////////////////////////////////////////////////////////////////
// Saving BMP
struct BmpHeader
{
uint mFileSize; // Size of file in bytes
uint mReserved01; // 2x 2 reserved bytes
uint mDataOffset; // Offset in bytes where data can be found (54)
uint mHeaderSize; // 40B
int mWidth; // Width in pixels
int mHeight; // Height in pixels
short mColorPlates; // Must be 1
short mBitsPerPixel; // We use 24bpp
uint mCompression; // We use BI_RGB ~ 0, uncompressed
uint mImageSize; // mWidth x mHeight x 3B
uint mHorizRes; // Pixels per meter (75dpi ~ 2953ppm)
uint mVertRes; // Pixels per meter (75dpi ~ 2953ppm)
uint mPaletteColors; // Not using palette - 0
uint mImportantColors; // 0 - all are important
};
void SaveBMP(
const char *aFilename,
float aGamma = 1.f)
{
std::ofstream bmp(aFilename, std::ios::binary);
BmpHeader header;
bmp.write("BM", 2);
header.mFileSize = uint(sizeof(BmpHeader) + 2) + mResX * mResY * 3;
header.mReserved01 = 0;
header.mDataOffset = uint(sizeof(BmpHeader) + 2);
header.mHeaderSize = 40;
header.mWidth = mResX;
header.mHeight = mResY;
header.mColorPlates = 1;
header.mBitsPerPixel = 24;
header.mCompression = 0;
header.mImageSize = mResX * mResY * 3;
header.mHorizRes = 2953;
header.mVertRes = 2953;
header.mPaletteColors = 0;
header.mImportantColors = 0;
bmp.write((char*)&header, sizeof(header));
const float invGamma = 1.f / aGamma;
for(int y=0; y<mResY; y++)
{
for(int x=0; x<mResX; x++)
{
// bmp is stored from bottom up
const Vec3f &rgbF = mColor[x + (mResY-y-1)*mResX];
typedef unsigned char byte;
float gammaBgr[3];
gammaBgr[0] = std::pow(rgbF.z, invGamma) * 255.f;
gammaBgr[1] = std::pow(rgbF.y, invGamma) * 255.f;
gammaBgr[2] = std::pow(rgbF.x, invGamma) * 255.f;
byte bgrB[3];
bgrB[0] = byte(std::min(255.f, std::max(0.f, gammaBgr[0])));
bgrB[1] = byte(std::min(255.f, std::max(0.f, gammaBgr[1])));
bgrB[2] = byte(std::min(255.f, std::max(0.f, gammaBgr[2])));
bmp.write((char*)&bgrB, sizeof(bgrB));
}
}
}
//////////////////////////////////////////////////////////////////////////
// Saving HDR
void SaveHDR(const char* aFilename)
{
std::ofstream hdr(aFilename, std::ios::binary);
hdr << "#?RADIANCE" << '\n';
hdr << "# SmallVCM" << '\n';
hdr << "FORMAT=32-bit_rle_rgbe" << '\n' << '\n';
hdr << "-Y " << mResY << " +X " << mResX << '\n';
for(int y=0; y<mResY; y++)
{
for(int x=0; x<mResX; x++)
{
typedef unsigned char byte;
byte rgbe[4] = {0,0,0,0};
const Vec3f &rgbF = mColor[x + y*mResX];
float v = std::max(rgbF.x, std::max(rgbF.y, rgbF.z));
if(v >= 1e-32f)
{
int e;
v = float(frexp(v, &e) * 256.f / v);
rgbe[0] = byte(rgbF.x * v);
rgbe[1] = byte(rgbF.y * v);
rgbe[2] = byte(rgbF.z * v);
rgbe[3] = byte(e + 128);
}
hdr.write((char*)&rgbe[0], 4);
}
}
}
private:
std::vector<Vec3f> mColor;
Vec2f mResolution;
int mResX;
int mResY;
};
#endif //__FRAMEBUFFER_HXX__
================================================
FILE: src/geometry.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __GEOMETRY_HXX__
#define __GEOMETRY_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
#include "ray.hxx"
//////////////////////////////////////////////////////////////////////////
// Geometry
class AbstractGeometry
{
public:
virtual ~AbstractGeometry(){};
// Finds the closest intersection
virtual bool Intersect (const Ray& aRay, Isect& oResult) const = 0;
// Finds any intersection, default calls Intersect
virtual bool IntersectP(const Ray& aRay, Isect& oResult) const
{
return Intersect(aRay, oResult);
}
// Grows given bounding box by this object
virtual void GrowBBox(Vec3f &aoBBoxMin, Vec3f &aoBBoxMax) = 0;
};
class GeometryList : public AbstractGeometry
{
public:
virtual ~GeometryList()
{
for(int i=0; i<(int)mGeometry.size(); i++)
delete mGeometry[i];
};
virtual bool Intersect(const Ray& aRay, Isect& oResult) const
{
bool anyIntersection = false;
for(int i=0; i<(int)mGeometry.size(); i++)
{
bool hit = mGeometry[i]->Intersect(aRay, oResult);
if(hit)
anyIntersection = hit;
}
return anyIntersection;
}
virtual bool IntersectP(
const Ray &aRay,
Isect &oResult) const
{
for(int i=0; i<(int)mGeometry.size(); i++)
{
if(mGeometry[i]->IntersectP(aRay, oResult))
return true;
}
return false;
}
virtual void GrowBBox(
Vec3f &aoBBoxMin,
Vec3f &aoBBoxMax)
{
for(int i=0; i<(int)mGeometry.size(); i++)
mGeometry[i]->GrowBBox(aoBBoxMin, aoBBoxMax);
}
public:
std::vector<AbstractGeometry*> mGeometry;
};
class Triangle : public AbstractGeometry
{
public:
Triangle(){}
Triangle(
const Vec3f &p0,
const Vec3f &p1,
const Vec3f &p2,
int aMatID)
{
p[0] = p0;
p[1] = p1;
p[2] = p2;
matID = aMatID;
mNormal = Normalize(Cross(p[1] - p[0], p[2] - p[0]));
}
virtual bool Intersect(
const Ray &aRay,
Isect &oResult) const
{
const Vec3f ao = p[0] - aRay.org;
const Vec3f bo = p[1] - aRay.org;
const Vec3f co = p[2] - aRay.org;
const Vec3f v0 = Cross(co, bo);
const Vec3f v1 = Cross(bo, ao);
const Vec3f v2 = Cross(ao, co);
const float v0d = Dot(v0, aRay.dir);
const float v1d = Dot(v1, aRay.dir);
const float v2d = Dot(v2, aRay.dir);
if(((v0d < 0.f) && (v1d < 0.f) && (v2d < 0.f)) ||
((v0d >= 0.f) && (v1d >= 0.f) && (v2d >= 0.f)))
{
const float distance = Dot(mNormal, ao) / Dot(mNormal, aRay.dir);
if((distance > aRay.tmin) && (distance < oResult.dist))
{
oResult.normal = mNormal;
oResult.matID = matID;
oResult.dist = distance;
return true;
}
}
return false;
}
virtual void GrowBBox(
Vec3f &aoBBoxMin,
Vec3f &aoBBoxMax)
{
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
aoBBoxMin.Get(j) = std::min(aoBBoxMin.Get(j), p[i].Get(j));
aoBBoxMax.Get(j) = std::max(aoBBoxMax.Get(j), p[i].Get(j));
}
}
}
public:
Vec3f p[3];
int matID;
Vec3f mNormal;
};
class Sphere : public AbstractGeometry
{
public:
Sphere(){}
Sphere(
const Vec3f &aCenter,
float aRadius,
int aMatID)
{
center = aCenter;
radius = aRadius;
matID = aMatID;
}
// Taken from:
// http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
virtual bool Intersect(
const Ray &aRay,
Isect &oResult) const
{
// we transform ray origin into object space (center == origin)
const Vec3f transformedOrigin = aRay.org - center;
const float A = Dot(aRay.dir, aRay.dir);
const float B = 2 * Dot(aRay.dir, transformedOrigin);
const float C = Dot(transformedOrigin, transformedOrigin) - (radius * radius);
// Must use doubles, because when B ~ sqrt(B*B - 4*A*C)
// the resulting t is imprecise enough to get around ray epsilons
const double disc = B*B - 4*A*C;
if(disc < 0)
return false;
const double discSqrt = std::sqrt(disc);
const double q = (B < 0) ? ((-B - discSqrt) / 2.f) : ((-B + discSqrt) / 2.f);
double t0 = q / A;
double t1 = C / q;
if(t0 > t1) std::swap(t0, t1);
float resT;
if(t0 > aRay.tmin && t0 < oResult.dist)
resT = float(t0);
else if(t1 > aRay.tmin && t1 < oResult.dist)
resT = float(t1);
else
return false;
oResult.dist = resT;
oResult.matID = matID;
oResult.normal = Normalize(transformedOrigin + Vec3f(resT) * aRay.dir);
return true;
}
virtual void GrowBBox(
Vec3f &aoBBoxMin,
Vec3f &aoBBoxMax)
{
for(int i=0; i<8; i++)
{
Vec3f p = center;
Vec3f half(radius);
for(int j=0; j<3; j++)
if(i & (1 << j)) half.Get(j) = -half.Get(j);
p += half;
for(int j=0; j<3; j++)
{
aoBBoxMin.Get(j) = std::min(aoBBoxMin.Get(j), p.Get(j));
aoBBoxMax.Get(j) = std::max(aoBBoxMax.Get(j), p.Get(j));
}
}
}
public:
Vec3f center;
float radius;
int matID;
};
#endif //__GEOMETRY_HXX__
================================================
FILE: src/hashgrid.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __HASHGRID_HXX__
#define __HASHGRID_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
class HashGrid
{
public:
void Reserve(int aNumCells)
{
mCellEnds.resize(aNumCells);
}
template<typename tParticle>
void Build(
const std::vector<tParticle> &aParticles,
float aRadius)
{
mRadius = aRadius;
mRadiusSqr = Sqr(mRadius);
mCellSize = mRadius * 2.f;
mInvCellSize = 1.f / mCellSize;
mBBoxMin = Vec3f( 1e36f);
mBBoxMax = Vec3f(-1e36f);
for(size_t i=0; i<aParticles.size(); i++)
{
const Vec3f &pos = aParticles[i].GetPosition();
for(int j=0; j<3; j++)
{
mBBoxMax.Get(j) = std::max(mBBoxMax.Get(j), pos.Get(j));
mBBoxMin.Get(j) = std::min(mBBoxMin.Get(j), pos.Get(j));
}
}
mIndices.resize(aParticles.size());
memset(&mCellEnds[0], 0, mCellEnds.size() * sizeof(int));
// set mCellEnds[x] to number of particles within x
for(size_t i=0; i<aParticles.size(); i++)
{
const Vec3f &pos = aParticles[i].GetPosition();
mCellEnds[GetCellIndex(pos)]++;
}
// run exclusive prefix sum to really get the cell starts
// mCellEnds[x] is now where the cell starts
int sum = 0;
for(size_t i=0; i<mCellEnds.size(); i++)
{
int temp = mCellEnds[i];
mCellEnds[i] = sum;
sum += temp;
}
for(size_t i=0; i<aParticles.size(); i++)
{
const Vec3f &pos = aParticles[i].GetPosition();
const int targetIdx = mCellEnds[GetCellIndex(pos)]++;
mIndices[targetIdx] = int(i);
}
// now mCellEnds[x] points to the index right after the last
// element of cell x
//// DEBUG
//for(size_t i=0; i<aParticles.size(); i++)
//{
// const Vec3f &pos = aParticles[i].GetPosition();
// Vec2i range = GetCellRange(GetCellIndex(pos));
// bool found = false;
// for(;range.x < range.y; range.x++)
// {
// if(mIndices[range.x] == i)
// found = true;
// }
// if(!found)
// printf("Error at particle %d\n", i);
//}
}
template<typename tParticle, typename tQuery>
void Process(
const std::vector<tParticle> &aParticles,
tQuery& aQuery)
{
const Vec3f queryPos = aQuery.GetPosition();
const Vec3f distMin = queryPos - mBBoxMin;
const Vec3f distMax = mBBoxMax - queryPos;
for(int i=0; i<3; i++)
{
if(distMin.Get(i) < 0.f) return;
if(distMax.Get(i) < 0.f) return;
}
const Vec3f cellPt = mInvCellSize * distMin;
const Vec3f coordF(
std::floor(cellPt.x),
std::floor(cellPt.y),
std::floor(cellPt.z));
const int px = int(coordF.x);
const int py = int(coordF.y);
const int pz = int(coordF.z);
const Vec3f fractCoord = cellPt - coordF;
const int pxo = px + (fractCoord.x < 0.5f ? -1 : +1);
const int pyo = py + (fractCoord.y < 0.5f ? -1 : +1);
const int pzo = pz + (fractCoord.z < 0.5f ? -1 : +1);
int found = 0;
for(int j=0; j<8; j++)
{
Vec2i activeRange;
switch(j)
{
case 0: activeRange = GetCellRange(GetCellIndex(Vec3i(px , py , pz ))); break;
case 1: activeRange = GetCellRange(GetCellIndex(Vec3i(px , py , pzo))); break;
case 2: activeRange = GetCellRange(GetCellIndex(Vec3i(px , pyo, pz ))); break;
case 3: activeRange = GetCellRange(GetCellIndex(Vec3i(px , pyo, pzo))); break;
case 4: activeRange = GetCellRange(GetCellIndex(Vec3i(pxo, py , pz ))); break;
case 5: activeRange = GetCellRange(GetCellIndex(Vec3i(pxo, py , pzo))); break;
case 6: activeRange = GetCellRange(GetCellIndex(Vec3i(pxo, pyo, pz ))); break;
case 7: activeRange = GetCellRange(GetCellIndex(Vec3i(pxo, pyo, pzo))); break;
}
for(; activeRange.x < activeRange.y; activeRange.x++)
{
const int particleIndex = mIndices[activeRange.x];
const tParticle &particle = aParticles[particleIndex];
const float distSqr =
(aQuery.GetPosition() - particle.GetPosition()).LenSqr();
if(distSqr <= mRadiusSqr)
aQuery.Process(particle);
}
}
}
private:
Vec2i GetCellRange(int aCellIndex) const
{
if(aCellIndex == 0) return Vec2i(0, mCellEnds[0]);
return Vec2i(mCellEnds[aCellIndex-1], mCellEnds[aCellIndex]);
}
int GetCellIndex(const Vec3i &aCoord) const
{
uint x = uint(aCoord.x);
uint y = uint(aCoord.y);
uint z = uint(aCoord.z);
return int(((x * 73856093) ^ (y * 19349663) ^
(z * 83492791)) % uint(mCellEnds.size()));
}
int GetCellIndex(const Vec3f &aPoint) const
{
const Vec3f distMin = aPoint - mBBoxMin;
const Vec3f coordF(
std::floor(mInvCellSize * distMin.x),
std::floor(mInvCellSize * distMin.y),
std::floor(mInvCellSize * distMin.z));
const Vec3i coordI = Vec3i(int(coordF.x), int(coordF.y), int(coordF.z));
return GetCellIndex(coordI);
}
private:
Vec3f mBBoxMin;
Vec3f mBBoxMax;
std::vector<int> mIndices;
std::vector<int> mCellEnds;
float mRadius;
float mRadiusSqr;
float mCellSize;
float mInvCellSize;
};
#endif //__HASHGRID_HXX__
================================================
FILE: src/html_writer.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __HTML_WRITER_HXX__
#define __HTML_WRITER_HXX__
// silence vsnprintf secure warning in MSVS
#pragma warning(push)
#pragma warning(disable : 4996)
#include <vector>
#include <cmath>
#include <fstream>
#include <string>
#include <cstdio>
#include <cstdarg>
class HtmlWriter
{
public:
enum BorderColor
{
kNone,
kRed,
kGreen
};
public:
HtmlWriter(const std::string& aFileName) :
mFileName(aFileName),
mHtml(mFileName.c_str())
{
// Most browsers will cap number of columns to real value,
// so having something significantly larger works ok
mAlgorithmCount = 100;
mThumbnailSize = 128;
}
~HtmlWriter()
{
mHtml.flush();
mHtml.close();
}
void Close()
{
mHtml << "</body>" << std::endl;
mHtml << "</html>" << std::endl;
}
/*
* The Javascript plugin is heavily modified version of plugin example:
* http://www.htmldrive.net/items/show/838/jQuery-different-Photo-comparing-Effect
*
* Original copyright reads:
* // Queness Before & After jQuery Plugin
* // Created by Kevin Liew from Queness.com
* // Permission is given to use this plugin in whatever way you want :)
*/
void WriteHeader()
{
mHtml << "<!--" << std::endl;
mHtml << "* The Javascript plugin is heavily modified version of plugin example:" << std::endl;
mHtml << "* http://www.htmldrive.net/items/show/838/jQuery-different-Photo-comparing-Effect" << std::endl;
mHtml << "*" << std::endl;
mHtml << "* Original copyright reads:" << std::endl;
mHtml << "* // Queness Before & After jQuery Plugin" << std::endl;
mHtml << "* // Created by Kevin Liew from Queness.com" << std::endl;
mHtml << "* // Permission is given to use this plugin in whatever way you want :)" << std::endl;
mHtml << "-->" << std::endl;
mHtml << "" << std::endl;
mHtml << "<!DOCTYPE html PUBLIC "
"\"-//W3C//DTD XHTML 1.0 Strict//EN\""
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
<< std::endl;
mHtml << "<html xmlns=\"http://www.w3.org/1999/xhtml\">" << std::endl;
mHtml << "<head>" << std::endl;
mHtml << "<title>Comparison of GI algorithms with Vertex Connection Merging</title>" << std::endl;
mHtml << "<meta http-equiv=\"Content-Language\" content=\"English\" />" << std::endl;
mHtml << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />" << std::endl;
mHtml << "<script type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js\"></script>" << std::endl;
mHtml << "<script type=\"text/javascript\">" << std::endl;
mHtml << "<!--" << std::endl;
mHtml << " (function($){" << std::endl;
mHtml << " $.fn.extend({" << std::endl;
mHtml << " //plugin name - qbeforeafter" << std::endl;
mHtml << " qbeforeafter: function(options) {" << std::endl;
mHtml << "" << std::endl;
mHtml << " return this.each(function() {" << std::endl;
mHtml << "" << std::endl;
mHtml << " var i = $(this);" << std::endl;
mHtml << " var img_layer_tl = i.children('img:eq(0)').attr('src');" << std::endl;
mHtml << " var img_layer_tr = i.children('img:eq(1)').attr('src');" << std::endl;
mHtml << " var img_layer_bl = i.children('img:eq(2)').attr('src');" << std::endl;
mHtml << " var img_layer_br = i.children('img:eq(3)').attr('src');" << std::endl;
mHtml << " var img_cap_tl = i.children('img:eq(0)').attr('alt');" << std::endl;
mHtml << " var img_cap_tr = i.children('img:eq(1)').attr('alt');" << std::endl;
mHtml << " var img_cap_bl = i.children('img:eq(2)').attr('alt');" << std::endl;
mHtml << " var img_cap_br = i.children('img:eq(3)').attr('alt');" << std::endl;
mHtml << "" << std::endl;
mHtml << " var img_style_tl = i.children('img:eq(0)').attr('style');" << std::endl;
mHtml << " var img_style_tr = i.children('img:eq(1)').attr('style');" << std::endl;
mHtml << " var img_style_bl = i.children('img:eq(2)').attr('style');" << std::endl;
mHtml << " var img_style_br = i.children('img:eq(3)').attr('style');" << std::endl;
mHtml << "" << std::endl;
mHtml << " var width = i.children('img:eq(0)').width();" << std::endl;
mHtml << " var height = i.children('img:eq(0)').height();" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('img').hide();" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.css({'overflow': 'hidden', 'position': 'relative'});" << std::endl;
mHtml << " i.append('<div class=\"ba-layer_tl\"></div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-layer_tr\"></div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-layer_bl\"></div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-layer_br\"></div>');" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.append('<div class=\"ba-border_tl\" style=\"' + img_style_tl + '\"></div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-border_tr\" style=\"' + img_style_tr + '\"></div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-border_bl\" style=\"' + img_style_bl + '\"></div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-border_br\" style=\"' + img_style_br + '\"></div>');" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.append('<div class=\"ba-caption_tl\">' + img_cap_tl + '</div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-caption_tr\">' + img_cap_tr + '</div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-caption_bl\">' + img_cap_bl + '</div>');" << std::endl;
mHtml << " i.append('<div class=\"ba-caption_br\">' + img_cap_br + '</div>');" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-layer_tl, .ba-layer_tr, .ba-layer_bl, .ba-layer_br').width(width);" << std::endl;
mHtml << " i.children('.ba-layer_tl, .ba-layer_tr, .ba-layer_bl, .ba-layer_br').height(height);" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-layer_tl').css('backgroundImage','url(' + img_layer_tl + ')');" << std::endl;
mHtml << " i.children('.ba-layer_tr').css('backgroundImage','url(' + img_layer_tr + ')');" << std::endl;
mHtml << " i.children('.ba-layer_bl').css('backgroundImage','url(' + img_layer_bl + ')');" << std::endl;
mHtml << " i.children('.ba-layer_br').css('backgroundImage','url(' + img_layer_br + ')');" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-layer_tl').width(width * 0.5);" << std::endl;
mHtml << " i.children('.ba-layer_tl').height(height * 0.5);" << std::endl;
mHtml << " i.children('.ba-layer_tr').height(height * 0.5);" << std::endl;
mHtml << " i.children('.ba-layer_bl').width(width * 0.5);" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-caption_tl').show();" << std::endl;
mHtml << " i.children('.ba-caption_tr').show();" << std::endl;
mHtml << " i.children('.ba-caption_bl').show();" << std::endl;
mHtml << " i.children('.ba-caption_br').show();" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-caption_tl').css({ bottom: height * 0.5, right: width * 0.5 });" << std::endl;
mHtml << " i.children('.ba-caption_tr').css({ bottom: height * 0.5, left: width * 0.5 });" << std::endl;
mHtml << " i.children('.ba-caption_bl').css({ top: height * 0.5, right: width * 0.5 });" << std::endl;
mHtml << " i.children('.ba-caption_br').css({ top: height * 0.5, left: width * 0.5 });" << std::endl;
mHtml << " // Set border" << std::endl;
mHtml << " bwidth = parseInt(i.children('.ba-border_tl').css(\"borderRightWidth\"), 10);" << std::endl;
mHtml << " i.children('.ba-border_tl').width (width * 0.5 - 1 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_tl').height(height * 0.5 - 1 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_tr').width (width * 0.5 - 4 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_tr').height(height * 0.5 - 1 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_bl').width (width * 0.5 - 1 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_bl').height(height * 0.5 - 4 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_br').width (width * 0.5 - 4 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_br').height(height * 0.5 - 4 - 2*(bwidth-1));" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-border_tl').css({ top: 0, left: 0 });" << std::endl;
mHtml << " i.children('.ba-border_tr').css({ top: 0, left: width * 0.5 + 2});" << std::endl;
mHtml << " i.children('.ba-border_bl').css({ top: height * 0.5 + 2, left: 0 });" << std::endl;
mHtml << " i.children('.ba-border_br').css({ top: height * 0.5 + 2, left: width * 0.5 + 2});" << std::endl;
mHtml << "" << std::endl;
mHtml << " }).mousemove(function (e) {" << std::endl;
mHtml << "" << std::endl;
mHtml << " var o = options;" << std::endl;
mHtml << " var i = $(this);" << std::endl;
mHtml << "" << std::endl;
mHtml << " right_border_width = parseInt(i.children('.ba-layer_tl').css(\"borderRightWidth\"), 10);" << std::endl;
mHtml << " bottom_border_width = parseInt(i.children('.ba-layer_tl').css(\"borderBottomWidth\"), 10);" << std::endl;
mHtml << " pos_imgX = i.position()['left'];" << std::endl;
mHtml << " pos_imgY = i.position()['top'];" << std::endl;
mHtml << " pos_mouseX = e.pageX - right_border_width * 0.5;" << std::endl;
mHtml << " pos_mouseY = e.pageY - bottom_border_width * 0.5;" << std::endl;
mHtml << " new_lwidth = pos_mouseX - pos_imgX; // left width" << std::endl;
mHtml << " new_theight = pos_mouseY - pos_imgY; // top height" << std::endl;
mHtml << " img_width = i.width();" << std::endl;
mHtml << " img_height = i.height();" << std::endl;
mHtml << " new_rwidth = img_width - new_lwidth; // right width" << std::endl;
mHtml << " new_bheight = img_height - new_theight; // bottom height" << std::endl;
mHtml << "" << std::endl;
mHtml << " img_cap_tl = i.children('img:eq(0)').attr('alt');" << std::endl;
mHtml << " img_cap_tr = i.children('img:eq(1)').attr('alt');" << std::endl;
mHtml << " img_cap_bl = i.children('img:eq(2)').attr('alt');" << std::endl;
mHtml << " img_cap_br = i.children('img:eq(3)').attr('alt');" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-layer_tl').width (new_lwidth );" << std::endl;
mHtml << " i.children('.ba-layer_tl').height(new_theight);" << std::endl;
mHtml << " i.children('.ba-layer_tr').height(new_theight);" << std::endl;
mHtml << " i.children('.ba-layer_bl').width (new_lwidth );" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-caption_tl').css({ bottom: new_bheight, right: new_rwidth });" << std::endl;
mHtml << " i.children('.ba-caption_tr').css({ bottom: new_bheight, left: new_lwidth });" << std::endl;
mHtml << " i.children('.ba-caption_bl').css({ top: new_theight, right: new_rwidth });" << std::endl;
mHtml << " i.children('.ba-caption_br').css({ top: new_theight, left: new_lwidth });" << std::endl;
mHtml << " // Set border" << std::endl;
mHtml << " bwidth = parseInt(i.children('.ba-border_tl').css(\"borderRightWidth\"), 10);" << std::endl;
mHtml << " i.children('.ba-border_tl').width (new_lwidth - 1 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_tl').height(new_theight - 1 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_tr').width (new_rwidth - 4 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_tr').height(new_theight - 1 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_bl').width (new_lwidth - 1 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_bl').height(new_bheight - 4 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_br').width (new_rwidth - 4 - 2*(bwidth-1));" << std::endl;
mHtml << " i.children('.ba-border_br').height(new_bheight - 4 - 2*(bwidth-1));" << std::endl;
mHtml << "" << std::endl;
mHtml << " i.children('.ba-border_tl').css({ top: 0, left: 0 });" << std::endl;
mHtml << " i.children('.ba-border_tr').css({ top: 0, left: new_lwidth + 2});" << std::endl;
mHtml << " i.children('.ba-border_bl').css({ top: new_theight + 2, left: 0 });" << std::endl;
mHtml << " i.children('.ba-border_br').css({ top: new_theight + 2, left: new_lwidth + 2});" << std::endl;
mHtml << " });" << std::endl;
mHtml << " }" << std::endl;
mHtml << " });" << std::endl;
mHtml << " })(jQuery);" << std::endl;
mHtml << "-->" << std::endl;
mHtml << "</script>" << std::endl;
mHtml << "" << std::endl;
mHtml << "<script type=\"text/javascript\">" << std::endl;
mHtml << "$(function () {" << std::endl;
mHtml << " $('.cross_compare').qbeforeafter({defaultgap:50, leftgap:0, rightgap:10, caption: true, reveal: 0.5});" << std::endl;
mHtml << "});" << std::endl;
mHtml << "</script>" << std::endl;
mHtml << "<style type=\"text/css\">" << std::endl;
mHtml << ".ba-layer_tl {position:absolute; top:0; left:0; z-index:3; border-right:3px solid #333; border-bottom:3px solid #333;}" << std::endl;
mHtml << ".ba-layer_tr {position:absolute; top:0; left:0; z-index:2; border-bottom:3px solid #333;}" << std::endl;
mHtml << ".ba-layer_bl {position:absolute; top:0; left:0; z-index:1; border-right:3px solid #333;}" << std::endl;
mHtml << ".ba-layer_br {position:absolute; top:0; left:0; z-index:0;}" << std::endl;
mHtml << "" << std::endl;
mHtml << ".ba-border_tl {position:absolute; top:0; left:0; z-index:4;}" << std::endl;
mHtml << ".ba-border_tr {position:absolute; top:0; left:0; z-index:4;}" << std::endl;
mHtml << ".ba-border_bl {position:absolute; top:0; left:0; z-index:4;}" << std::endl;
mHtml << ".ba-border_br {position:absolute; top:0; left:0; z-index:4;}" << std::endl;
mHtml << "" << std::endl;
mHtml << ".ba-caption_tl {position:absolute; bottom:10px; right:10px; z-index:120; color:#fff; text-align:center; padding:5px; font-size:12px; font-family:arial; display:none;}" << std::endl;
mHtml << ".ba-caption_tr {position:absolute; bottom:10px; left: 10px; z-index:120; color:#fff; text-align:center; padding:5px; font-size:12px; font-family:arial; display:none;}" << std::endl;
mHtml << ".ba-caption_bl {position:absolute; top:10px; right:10px; z-index:120; color:#fff; text-align:center; padding:5px; font-size:12px; font-family:arial; display:none;}" << std::endl;
mHtml << ".ba-caption_br {position:absolute; top:10px; left: 10px; z-index:120; color:#fff; text-align:center; padding:5px; font-size:12px; font-family:arial; display:none;}" << std::endl;
mHtml << "</style>" << std::endl;
mHtml << "</head>" << std::endl;
mHtml << "<body>" << std::endl;
}
void AddScene(const std::string &aSceneName)
{
mHtml << "<table";
if(mAlgorithmCount < 100)
mHtml << " width=\"" << mAlgorithmCount * (mThumbnailSize + 10) << "px\"";
mHtml << "><tr><td colspan=\"" << mAlgorithmCount << "\"><h2>" << aSceneName << "</h2></td></tr>" << std::endl;
mHtml << "<tr>" << std::endl;
}
void AddRendering(
const std::string &aMethodName,
const std::string &aFileName,
float aTime,
BorderColor aBorderColor = kNone,
const std::string &aOtherInfo = "")
{
// The image
mHtml << "<td valign=\"top\" align=\"center\">"
<< "<div style=\""
<< "width:"<< mThumbnailSize + 10 <<"px;line-height:90%;\">"
<< " <a href=\"" << aFileName << "\">";
#if 1
mHtml << "<img src=\"" << aFileName << "\" "
<< "width=\""<< mThumbnailSize <<"px\" ";
if(aBorderColor == kGreen)
mHtml << "style=\"border:5px solid #0c0\" ";
else if(aBorderColor == kRed)
mHtml << "style=\"border:5px solid #f00\" ";
else
mHtml << "style=\"border:5px solid #ccc\" ";
mHtml << " alt=\"" << aFileName << " (" << MakeMessage("%.2f", aTime) << " s)\" ";
mHtml << "height=\""<< mThumbnailSize <<"px\" />";
#else
mHtml << "<div style=\"background: url(" << aFileName
<< "); background-size: 128px;";
if(aBorderColor == kRed)
{
mHtml << "background-position: -5px -5px; border: 5px solid #f00; "
<< "width:"<< mThumbnailSize - 10 <<"px; "
<< "height:"<< mThumbnailSize - 10 <<"px;\"></div>";
}
else if(aBorderColor == kGreen)
{
mHtml << "background-position: -5px -5px; border: 5px solid #0c0; "
<< "width:"<< mThumbnailSize - 10 <<"px; "
<< "height:"<< mThumbnailSize - 10 <<"px;\"></div>";
}
else // None
{
mHtml << "width:"<< mThumbnailSize <<"px; "
<< "height:"<< mThumbnailSize <<"px;\"></div>";
}
#endif
mHtml << "</a>" << std::endl;
// The text
mHtml << "<br/><small>" << aMethodName
<< " (" << MakeMessage("%.2f", aTime) << " s)" << aOtherInfo
<< "</small></div></td>" << std::endl;
}
void AddFourWaySplit(
const std::string aMethodFiles[4],
const std::string aMethodNames[4],
const int aBorderColors[4],
const int aSize)
{
mHtml << "</tr><tr>" << std::endl;
mHtml << "<td colspan=\"" << mAlgorithmCount << "\" align=\"center\">" << std::endl;
mHtml << "<div class=\"cross_compare\" style=\"width:" << aSize
<< "px;height:" << aSize << "px;cursor:crosshair\">" << std::endl;
for(int i=0; i<4; i++)
{
mHtml << "<img src=\"" << aMethodFiles[i]
<< "\" alt=\"" << aMethodNames[i]
<< "\" width=\"" << aSize
<< "\" height=\"" << aSize << "\" ";
if(aBorderColors[i] == kGreen)
mHtml << "style=\"border:2px solid #0c0\"/>" << std::endl;
else if(aBorderColors[i] == kRed)
mHtml << "style=\"border:2px solid #f00\"/>" << std::endl;
else
mHtml << "style=\"border:2px solid #ccc\"/>" << std::endl;
}
mHtml << "</div>" << std::endl;
mHtml << "</td>" << std::endl;
mHtml << "</tr></table>" << std::endl;
}
// Taken from: http://www.tin.org/bin/man.cgi?section=3&topic=vsnprintf
std::string MakeMessage(const char *fmt, ...)
{
/* Guess we need no more than 100 bytes. */
int size = 100;
std::string str;
va_list ap;
while (1) {
str.resize(size);
/* Try to print in the allocated space. */
va_start(ap, fmt);
int n = vsnprintf((char*)str.c_str(), size, fmt, ap);
va_end(ap);
/* If that worked, return the string. */
if (n > -1 && n < size)
{
str.resize(n);
return str;
}
/* Else try again with more space. */
if (n > -1) /* glibc 2.1 */
size = n+1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
}
}
public:
int mAlgorithmCount;
int mThumbnailSize;
std::string mFileName;
std::ofstream mHtml;
};
#pragma warning(pop)
#endif //__HTML_WRITER_HXX__
================================================
FILE: src/lights.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __LIGHTS_HXX__
#define __LIGHTS_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
struct SceneSphere
{
// Center of the scene's bounding sphere
Vec3f mSceneCenter;
// Radius of the scene's bounding sphere
float mSceneRadius;
// 1.f / (mSceneRadius^2)
float mInvSceneRadiusSqr;
};
class AbstractLight
{
public:
virtual ~AbstractLight()
{}
/* \brief Illuminates a given point in the scene.
*
* Given a point and two random samples (e.g., for position on area lights),
* this method returns direction from point to light, distance,
* pdf of having chosen this direction (e.g., 1 / area).
* Optionally also returns pdf of emitting particle in this direction,
* and cosine from lights normal (helps with PDF of hitting the light,
* but set to 1 for point lights).
*
* Returns radiance.
*/
virtual Vec3f Illuminate(
const SceneSphere &aSceneSphere,
const Vec3f &aReceivingPosition,
const Vec2f &aRndTuple,
Vec3f &oDirectionToLight,
float &oDistance,
float &oDirectPdfW,
float *oEmissionPdfW = NULL,
float *oCosAtLight = NULL) const = 0;
/* \brief Emits particle from the light.
*
* Given two sets of random numbers (e.g., position and direction on area light),
* this method generates a position and direction for light particle, along
* with the pdf.
*
* Can also supply pdf (w.r.t. area) of choosing this position when calling
* Illuminate. Also provides cosine on the light (this is 1 for point lights etc.).
*
* Returns "energy" that particle carries
*/
virtual Vec3f Emit(
const SceneSphere &aSceneSphere,
const Vec2f &aDirRndTuple,
const Vec2f &aPosRndTuple,
Vec3f &oPosition,
Vec3f &oDirection,
float &oEmissionPdfW,
float *oDirectPdfA,
float *oCosThetaLight) const = 0;
/* \brief Returns radiance for ray randomly hitting the light
*
* Given ray direction and hitpoint, it returns radiance.
* Can also provide area pdf of sampling hitpoint in Illuminate,
* and of emitting particle along the ray (in opposite direction).
*/
virtual Vec3f GetRadiance(
const SceneSphere &aSceneSphere,
const Vec3f &aRayDirection,
const Vec3f &aHitPoint,
float *oDirectPdfA = NULL,
float *oEmissionPdfW = NULL) const = 0;
// Whether the light has a finite extent (area, point) or not (directional, env. map)
virtual bool IsFinite() const = 0;
// Whether the light has delta function (point, directional) or not (area)
virtual bool IsDelta() const = 0;
};
//////////////////////////////////////////////////////////////////////////
class AreaLight : public AbstractLight
{
public:
AreaLight(
const Vec3f &aP0,
const Vec3f &aP1,
const Vec3f &aP2)
{
p0 = aP0;
e1 = aP1 - aP0;
e2 = aP2 - aP0;
Vec3f normal = Cross(e1, e2);
float len = normal.Length();
mInvArea = 2.f / len;
mFrame.SetFromZ(normal);
}
virtual Vec3f Illuminate(
const SceneSphere &/*aSceneSphere*/,
const Vec3f &aReceivingPosition,
const Vec2f &aRndTuple,
Vec3f &oDirectionToLight,
float &oDistance,
float &oDirectPdfW,
float *oEmissionPdfW = NULL,
float *oCosAtLight = NULL) const
{
const Vec2f uv = SampleUniformTriangle(aRndTuple);
const Vec3f lightPoint = p0 + e1 * uv.x + e2 * uv.y;
oDirectionToLight = lightPoint - aReceivingPosition;
const float distSqr = oDirectionToLight.LenSqr();
oDistance = std::sqrt(distSqr);
oDirectionToLight = oDirectionToLight / oDistance;
const float cosNormalDir = Dot(mFrame.Normal(), -oDirectionToLight);
// too close to, or under, tangent
if(cosNormalDir < EPS_COSINE)
{
return Vec3f(0.f);
}
oDirectPdfW = mInvArea * distSqr / cosNormalDir;
if(oCosAtLight)
*oCosAtLight = cosNormalDir;
if(oEmissionPdfW)
*oEmissionPdfW = mInvArea * cosNormalDir * INV_PI_F;
return mIntensity;
}
virtual Vec3f Emit(
const SceneSphere &/*aSceneSphere*/,
const Vec2f &aDirRndTuple,
const Vec2f &aPosRndTuple,
Vec3f &oPosition,
Vec3f &oDirection,
float &oEmissionPdfW,
float *oDirectPdfA,
float *oCosThetaLight) const
{
const Vec2f uv = SampleUniformTriangle(aPosRndTuple);
oPosition = p0 + e1 * uv.x + e2 * uv.y;
Vec3f localDirOut = SampleCosHemisphereW(aDirRndTuple, &oEmissionPdfW);
oEmissionPdfW *= mInvArea;
// cannot really not emit the particle, so just bias it to the correct angle
localDirOut.z = std::max(localDirOut.z, EPS_COSINE);
oDirection = mFrame.ToWorld(localDirOut);
if(oDirectPdfA)
*oDirectPdfA = mInvArea;
if(oCosThetaLight)
*oCosThetaLight = localDirOut.z;
return mIntensity * localDirOut.z;
}
virtual Vec3f GetRadiance(
const SceneSphere &/*aSceneSphere*/,
const Vec3f &aRayDirection,
const Vec3f &aHitPoint,
float *oDirectPdfA = NULL,
float *oEmissionPdfW = NULL) const
{
const float cosOutL = std::max(0.f, Dot(mFrame.Normal(), -aRayDirection));
if(cosOutL == 0)
return Vec3f(0);
if(oDirectPdfA)
*oDirectPdfA = mInvArea;
if(oEmissionPdfW)
{
*oEmissionPdfW = CosHemispherePdfW(mFrame.Normal(), -aRayDirection);
*oEmissionPdfW *= mInvArea;
}
return mIntensity;
}
// Whether the light has a finite extent (area, point) or not (directional, env. map)
virtual bool IsFinite() const { return true; }
// Whether the light has delta function (point, directional) or not (area)
virtual bool IsDelta() const { return false; }
public:
Vec3f p0, e1, e2;
Frame mFrame;
Vec3f mIntensity;
float mInvArea;
};
//////////////////////////////////////////////////////////////////////////
class DirectionalLight : public AbstractLight
{
public:
DirectionalLight(const Vec3f& aDirection)
{
mFrame.SetFromZ(aDirection);
}
virtual Vec3f Illuminate(
const SceneSphere &aSceneSphere,
const Vec3f &/*aReceivingPosition*/,
const Vec2f &/*aRndTuple*/,
Vec3f &oDirectionToLight,
float &oDistance,
float &oDirectPdfW,
float *oEmissionPdfW = NULL,
float *oCosAtLight = NULL) const
{
oDirectionToLight = -mFrame.Normal();
oDistance = 1e36f;
oDirectPdfW = 1.f;
if(oCosAtLight)
*oCosAtLight = 1.f;
if(oEmissionPdfW)
*oEmissionPdfW = ConcentricDiscPdfA() * aSceneSphere.mInvSceneRadiusSqr;
return mIntensity;
}
virtual Vec3f Emit(
const SceneSphere &aSceneSphere,
const Vec2f &/*aDirRndTuple*/,
const Vec2f &aPosRndTuple,
Vec3f &oPosition,
Vec3f &oDirection,
float &oEmissionPdfW,
float *oDirectPdfA,
float *oCosThetaLight) const
{
const Vec2f xy = SampleConcentricDisc(aPosRndTuple);
oPosition = aSceneSphere.mSceneCenter +
aSceneSphere.mSceneRadius * (
-mFrame.Normal() + mFrame.Binormal() * xy.x + mFrame.Tangent() * xy.y);
oDirection = mFrame.Normal();
oEmissionPdfW = ConcentricDiscPdfA() * aSceneSphere.mInvSceneRadiusSqr;
if(oDirectPdfA)
*oDirectPdfA = 1.f;
// Not used for infinite or delta lights
if(oCosThetaLight)
*oCosThetaLight = 1.f;
return mIntensity;
}
virtual Vec3f GetRadiance(
const SceneSphere &/*aSceneSphere*/,
const Vec3f &/*aRayDirection*/,
const Vec3f &/*aHitPoint*/,
float *oDirectPdfA = NULL,
float *oEmissionPdfW = NULL) const
{
return Vec3f(0);
}
// Whether the light has a finite extent (area, point) or not (directional, env. map)
virtual bool IsFinite() const { return false; }
// Whether the light has delta function (point, directional) or not (area)
virtual bool IsDelta() const { return true; }
public:
Frame mFrame;
Vec3f mIntensity;
};
//////////////////////////////////////////////////////////////////////////
class PointLight : public AbstractLight
{
public:
PointLight(const Vec3f& aPosition)
{
mPosition = aPosition;
}
virtual Vec3f Illuminate(
const SceneSphere &/*aSceneSphere*/,
const Vec3f &aReceivingPosition,
const Vec2f &aRndTuple,
Vec3f &oDirectionToLight,
float &oDistance,
float &oDirectPdfW,
float *oEmissionPdfW = NULL,
float *oCosAtLight = NULL) const
{
oDirectionToLight = mPosition - aReceivingPosition;
const float distSqr = oDirectionToLight.LenSqr();
oDirectPdfW = distSqr;
oDistance = std::sqrt(distSqr);
oDirectionToLight = oDirectionToLight / oDistance;
if(oCosAtLight)
*oCosAtLight = 1.f;
if(oEmissionPdfW)
*oEmissionPdfW = UniformSpherePdfW();
return mIntensity;
}
virtual Vec3f Emit(
const SceneSphere &/*aSceneSphere*/,
const Vec2f &aDirRndTuple,
const Vec2f &/*aPosRndTuple*/,
Vec3f &oPosition,
Vec3f &oDirection,
float &oEmissionPdfW,
float *oDirectPdfA,
float *oCosThetaLight) const
{
oPosition = mPosition;
oDirection = SampleUniformSphereW(aDirRndTuple, &oEmissionPdfW);
if(oDirectPdfA)
*oDirectPdfA = 1.f;
// Not used for infinite or delta lights
if(oCosThetaLight)
*oCosThetaLight = 1.f;
return mIntensity;
}
virtual Vec3f GetRadiance(
const SceneSphere &/*aSceneSphere*/,
const Vec3f &/*aRayDirection*/,
const Vec3f &/*aHitPoint*/,
float *oDirectPdfA = NULL,
float *oEmissionPdfW = NULL) const
{
return Vec3f(0);
}
// Whether the light has a finite extent (area, point) or not (directional, env. map)
virtual bool IsFinite() const { return true; }
// Whether the light has delta function (point, directional) or not (area)
virtual bool IsDelta() const { return true; }
public:
Vec3f mPosition;
Vec3f mIntensity;
};
//////////////////////////////////////////////////////////////////////////
class BackgroundLight : public AbstractLight
{
public:
BackgroundLight()
{
mBackgroundColor = Vec3f(135, 206, 250) / Vec3f(255.f);
mScale = 1.f;
}
virtual Vec3f Illuminate(
const SceneSphere &aSceneSphere,
const Vec3f &aReceivingPosition,
const Vec2f &aRndTuple,
Vec3f &oDirectionToLight,
float &oDistance,
float &oDirectPdfW,
float *oEmissionPdfW = NULL,
float *oCosAtLight = NULL) const
{
// Replace these two lines with image sampling
oDirectionToLight = SampleUniformSphereW(aRndTuple, &oDirectPdfW);
//oDirectionToLight = Vec3f(0.16123600f, -0.98195398f, 0.098840252f);
Vec3f radiance = mBackgroundColor * mScale;
// This stays even with image sampling
oDistance = 1e36f;
if(oEmissionPdfW)
*oEmissionPdfW = oDirectPdfW * ConcentricDiscPdfA() *
aSceneSphere.mInvSceneRadiusSqr;
if(oCosAtLight)
*oCosAtLight = 1.f;
return radiance;
}
virtual Vec3f Emit(
const SceneSphere &aSceneSphere,
const Vec2f &aDirRndTuple,
const Vec2f &aPosRndTuple,
Vec3f &oPosition,
Vec3f &oDirection,
float &oEmissionPdfW,
float *oDirectPdfA,
float *oCosThetaLight) const
{
float directPdf;
// Replace these two lines with image sampling
oDirection = SampleUniformSphereW(aDirRndTuple, &directPdf);
//oDirection = -Vec3f(0.16123600f, -0.98195398f, 0.098840252f);
Vec3f radiance = mBackgroundColor * mScale;
// Stays even with image sampling
const Vec2f xy = SampleConcentricDisc(aPosRndTuple);
Frame frame;
frame.SetFromZ(oDirection);
oPosition = aSceneSphere.mSceneCenter + aSceneSphere.mSceneRadius * (
-oDirection + frame.Binormal() * xy.x + frame.Tangent() * xy.y);
//oPosition = Vec3f(-1.109054f, -2.15064538f, -1.087019148f);
oEmissionPdfW = directPdf * ConcentricDiscPdfA() *
aSceneSphere.mInvSceneRadiusSqr;
// For background we lie about Pdf being in area measure
if(oDirectPdfA)
*oDirectPdfA = directPdf;
// Not used for infinite or delta lights
if(oCosThetaLight)
*oCosThetaLight = 1.f;
return radiance;
}
virtual Vec3f GetRadiance(
const SceneSphere &aSceneSphere,
const Vec3f &/*aRayDirection*/,
const Vec3f &/*aHitPoint*/,
float *oDirectPdfA = NULL,
float *oEmissionPdfW = NULL) const
{
// Replace this with image lookup (proper pdf and such)
// use aRayDirection
float directPdf = UniformSpherePdfW();
Vec3f radiance = mBackgroundColor * mScale;
const float positionPdf = ConcentricDiscPdfA() *
aSceneSphere.mInvSceneRadiusSqr;
if(oDirectPdfA)
*oDirectPdfA = directPdf;
if(oEmissionPdfW)
*oEmissionPdfW = directPdf * positionPdf;
return radiance;
}
// Whether the light has a finite extent (area, point) or not (directional, env. map)
virtual bool IsFinite() const { return false; }
// Whether the light has delta function (point, directional) or not (area)
virtual bool IsDelta() const { return false; }
public:
Vec3f mBackgroundColor;
float mScale;
};
#endif //__LIGHTS_HXX__
================================================
FILE: src/materials.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __MATERIALS_HXX__
#define __MATERIALS_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
#include "frame.hxx"
#include "ray.hxx"
#include "scene.hxx"
#include "utils.hxx"
class Material
{
public:
Material()
{
Reset();
}
void Reset()
{
mDiffuseReflectance = Vec3f(0);
mPhongReflectance = Vec3f(0);
mPhongExponent = 1.f;
mMirrorReflectance = Vec3f(0);
mIOR = -1.f;
}
// diffuse is simply added to the others
Vec3f mDiffuseReflectance;
// Phong is simply added to the others
Vec3f mPhongReflectance;
float mPhongExponent;
// mirror can be either simply added, or mixed using Fresnel term
// this is governed by mIOR, if it is >= 0, fresnel is used, otherwise
// it is not
Vec3f mMirrorReflectance;
// When mIOR >= 0, we also transmit (just clear glass)
float mIOR;
};
#endif //__MATERIALS_HXX__
================================================
FILE: src/math.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __MATH_HXX__
#define __MATH_HXX__
#include <cmath>
// for portability issues
#define PI_F 3.14159265358979f
#define INV_PI_F (1.f / PI_F)
template<typename T>
T Sqr(const T& a) { return a*a; }
typedef unsigned uint;
//////////////////////////////////////////////////////////////////////////
// Math section
template<typename T>
class Vec2x
{
public:
Vec2x(){}
Vec2x(T a):x(a),y(a){}
Vec2x(T a, T b):x(a),y(b){}
const T& Get(int a) const { return reinterpret_cast<const T*>(this)[a]; }
T& Get(int a) { return reinterpret_cast<T*>(this)[a]; }
// unary minus
Vec2x<T> operator-() const
{ Vec2x<T> res; for(int i=0; i<2; i++) res.Get(i) = -Get(i); return res; }
// binary operations
friend Vec2x<T> operator+(const Vec2x& a, const Vec2x& b)
{ Vec2x<T> res; for(int i=0; i<2; i++) res.Get(i) = a.Get(i) + b.Get(i); return res; }
friend Vec2x<T> operator-(const Vec2x& a, const Vec2x& b)
{ Vec2x<T> res; for(int i=0; i<2; i++) res.Get(i) = a.Get(i) - b.Get(i); return res; }
friend Vec2x<T> operator*(const Vec2x& a, const Vec2x& b)
{ Vec2x<T> res; for(int i=0; i<2; i++) res.Get(i) = a.Get(i) * b.Get(i); return res; }
friend Vec2x<T> operator/(const Vec2x& a, const Vec2x& b)
{ Vec2x<T> res; for(int i=0; i<2; i++) res.Get(i) = a.Get(i) / b.Get(i); return res; }
Vec2x<T>& operator+=(const Vec2x& a)
{ for(int i=0; i<2; i++) Get(i) += a.Get(i); return *this;}
Vec2x<T>& operator-=(const Vec2x& a)
{ for(int i=0; i<2; i++) Get(i) -= a.Get(i); return *this;}
Vec2x<T>& operator*=(const Vec2x& a)
{ for(int i=0; i<2; i++) Get(i) *= a.Get(i); return *this;}
Vec2x<T>& operator/=(const Vec2x& a)
{ for(int i=0; i<2; i++) Get(i) /= a.Get(i); return *this;}
friend T Dot(const Vec2x& a, const Vec2x& b)
{ T res(0); for(int i=0; i<2; i++) res += a.Get(i) * b.Get(i); return res; }
public:
T x, y;
};
typedef Vec2x<float> Vec2f;
typedef Vec2x<int> Vec2i;
template<typename T>
class Vec3x
{
public:
Vec3x(){}
Vec3x(T a):x(a),y(a),z(a){}
Vec3x(T a, T b, T c):x(a),y(b),z(c){}
const T& Get(int a) const { return reinterpret_cast<const T*>(this)[a]; }
T& Get(int a) { return reinterpret_cast<T*>(this)[a]; }
Vec2x<T> GetXY() const { return Vec2x<T>(x, y); }
T Max() const { T res = Get(0); for(int i=1; i<3; i++) res = std::max(res, Get(i)); return res;}
bool IsZero() const
{
for(int i=0; i<3; i++)
if(Get(i) != 0)
return false;
return true;
}
// unary minus
Vec3x<T> operator-() const
{ Vec3x<T> res; for(int i=0; i<3; i++) res.Get(i) = -Get(i); return res; }
// binary operations
friend Vec3x<T> operator+(const Vec3x& a, const Vec3x& b)
{ Vec3x<T> res; for(int i=0; i<3; i++) res.Get(i) = a.Get(i) + b.Get(i); return res; }
friend Vec3x<T> operator-(const Vec3x& a, const Vec3x& b)
{ Vec3x<T> res; for(int i=0; i<3; i++) res.Get(i) = a.Get(i) - b.Get(i); return res; }
friend Vec3x<T> operator*(const Vec3x& a, const Vec3x& b)
{ Vec3x<T> res; for(int i=0; i<3; i++) res.Get(i) = a.Get(i) * b.Get(i); return res; }
friend Vec3x<T> operator/(const Vec3x& a, const Vec3x& b)
{ Vec3x<T> res; for(int i=0; i<3; i++) res.Get(i) = a.Get(i) / b.Get(i); return res; }
Vec3x<T>& operator+=(const Vec3x& a)
{ for(int i=0; i<3; i++) Get(i) += a.Get(i); return *this;}
Vec3x<T>& operator-=(const Vec3x& a)
{ for(int i=0; i<3; i++) Get(i) -= a.Get(i); return *this;}
Vec3x<T>& operator*=(const Vec3x& a)
{ for(int i=0; i<3; i++) Get(i) *= a.Get(i); return *this;}
Vec3x<T>& operator/=(const Vec3x& a)
{ for(int i=0; i<3; i++) Get(i) /= a.Get(i); return *this;}
friend T Dot(const Vec3x& a, const Vec3x& b)
{ T res(0); for(int i=0; i<3; i++) res += a.Get(i) * b.Get(i); return res; }
float LenSqr() const { return Dot(*this, *this); }
float Length() const { return std::sqrt(LenSqr()); }
public:
T x, y, z;
};
typedef Vec3x<float> Vec3f;
typedef Vec3x<int> Vec3i;
Vec3f Cross(
const Vec3f &a,
const Vec3f &b)
{
Vec3f res;
res.x = a.y * b.z - a.z * b.y;
res.y = a.z * b.x - a.x * b.z;
res.z = a.x * b.y - a.y * b.x;
return res;
}
Vec3f Normalize(const Vec3f& a)
{
const float lenSqr = Dot(a, a);
const float len = std::sqrt(lenSqr);
return a / len;
}
class Mat4f
{
public:
Mat4f(){}
Mat4f(float a){ for(int i=0; i<16; i++) GetPtr()[i] = a; }
const float* GetPtr() const { return reinterpret_cast<const float*>(this); }
float* GetPtr() { return reinterpret_cast<float*>(this); }
const float& Get(int r, int c) const { return GetPtr()[r + c*4]; }
float& Get(int r, int c) { return GetPtr()[r + c*4]; }
void SetRow(int r, float a, float b, float c, float d)
{
Get(r, 0) = a;
Get(r, 1) = b;
Get(r, 2) = c;
Get(r, 3) = d;
}
void SetRow(int r, const Vec3f &a, float b)
{
for(int i=0; i<3; i++)
Get(r, i) = a.Get(i);
Get(r, 3) = b;
}
Vec3f TransformVector(const Vec3f& aVec) const
{
Vec3f res(0);
for(int r=0; r<3; r++)
for(int c=0; c<3; c++)
res.Get(r) += aVec.Get(c) * Get(r, c);
return res;
}
Vec3f TransformPoint(const Vec3f& aVec) const
{
float w = Get(3,3);
for(int c=0; c<3; c++)
w += Get(3, c) * aVec.Get(c);
const float invW = 1.f / w;
Vec3f res(0);
for(int r=0; r<3; r++)
{
res.Get(r) = Get(r, 3);
for(int c=0; c<3; c++)
res.Get(r) += aVec.Get(c) * Get(r, c);
res.Get(r) *= invW;
}
return res;
}
static Mat4f Zero() { Mat4f res(0); return res; }
static Mat4f Identity()
{
Mat4f res(0);
for(int i=0; i<4; i++) res.Get(i,i) = 1.f;
return res;
}
static Mat4f Scale(const Vec3f& aScale)
{
Mat4f res = Mat4f::Identity();
for(int i=0; i<3; i++) res.Get(i,i) = aScale.Get(i);
res.Get(3,3) = 1;
return res;
}
static Mat4f Translate(const Vec3f& aScale)
{
Mat4f res = Mat4f::Identity();
for(int i=0; i<3; i++) res.Get(i,3) = aScale.Get(i);
res.Get(3,3) = 1;
return res;
}
static Mat4f Perspective(
float aFov,
float aNear,
float aFar)
{
// Camera points towards -z. 0 < near < far.
// Matrix maps z range [-near, -far] to [-1, 1], after homogeneous division.
float f = 1.f / (std::tan(aFov * PI_F / 360.0f));
float d = 1.f / (aNear - aFar);
Mat4f r;
r.m00 = f; r.m01 = 0.0f; r.m02 = 0.0f; r.m03 = 0.0f;
r.m10 = 0.0f; r.m11 = -f; r.m12 = 0.0f; r.m13 = 0.0f;
r.m20 = 0.0f; r.m21 = 0.0f; r.m22 = (aNear + aFar) * d; r.m23 = 2.0f * aNear * aFar * d;
r.m30 = 0.0f; r.m31 = 0.0f; r.m32 = -1.0f; r.m33 = 0.0f;
return r;
}
public:
// m_row_col; stored column major
float m00, m10, m20, m30;
float m01, m11, m21, m31;
float m02, m12, m22, m32;
float m03, m13, m23, m33;
};
Mat4f operator*(const Mat4f& left, const Mat4f& right)
{
Mat4f res(0);
for(int row=0; row<4; row++)
for(int col=0; col<4; col++)
for(int i=0; i<4; i++)
res.Get(row, col) += left.Get(row, i) * right.Get(i, col);
return res;
}
// Code for inversion taken from:
// http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix
Mat4f Invert(const Mat4f& aMatrix)
{
const float *m = aMatrix.GetPtr();
float inv[16], det;
int i;
inv[0] = m[5] * m[10] * m[15] -
m[5] * m[11] * m[14] -
m[9] * m[6] * m[15] +
m[9] * m[7] * m[14] +
m[13] * m[6] * m[11] -
m[13] * m[7] * m[10];
inv[4] = -m[4] * m[10] * m[15] +
m[4] * m[11] * m[14] +
m[8] * m[6] * m[15] -
m[8] * m[7] * m[14] -
m[12] * m[6] * m[11] +
m[12] * m[7] * m[10];
inv[8] = m[4] * m[9] * m[15] -
m[4] * m[11] * m[13] -
m[8] * m[5] * m[15] +
m[8] * m[7] * m[13] +
m[12] * m[5] * m[11] -
m[12] * m[7] * m[9];
inv[12] = -m[4] * m[9] * m[14] +
m[4] * m[10] * m[13] +
m[8] * m[5] * m[14] -
m[8] * m[6] * m[13] -
m[12] * m[5] * m[10] +
m[12] * m[6] * m[9];
inv[1] = -m[1] * m[10] * m[15] +
m[1] * m[11] * m[14] +
m[9] * m[2] * m[15] -
m[9] * m[3] * m[14] -
m[13] * m[2] * m[11] +
m[13] * m[3] * m[10];
inv[5] = m[0] * m[10] * m[15] -
m[0] * m[11] * m[14] -
m[8] * m[2] * m[15] +
m[8] * m[3] * m[14] +
m[12] * m[2] * m[11] -
m[12] * m[3] * m[10];
inv[9] = -m[0] * m[9] * m[15] +
m[0] * m[11] * m[13] +
m[8] * m[1] * m[15] -
m[8] * m[3] * m[13] -
m[12] * m[1] * m[11] +
m[12] * m[3] * m[9];
inv[13] = m[0] * m[9] * m[14] -
m[0] * m[10] * m[13] -
m[8] * m[1] * m[14] +
m[8] * m[2] * m[13] +
m[12] * m[1] * m[10] -
m[12] * m[2] * m[9];
inv[2] = m[1] * m[6] * m[15] -
m[1] * m[7] * m[14] -
m[5] * m[2] * m[15] +
m[5] * m[3] * m[14] +
m[13] * m[2] * m[7] -
m[13] * m[3] * m[6];
inv[6] = -m[0] * m[6] * m[15] +
m[0] * m[7] * m[14] +
m[4] * m[2] * m[15] -
m[4] * m[3] * m[14] -
m[12] * m[2] * m[7] +
m[12] * m[3] * m[6];
inv[10] = m[0] * m[5] * m[15] -
m[0] * m[7] * m[13] -
m[4] * m[1] * m[15] +
m[4] * m[3] * m[13] +
m[12] * m[1] * m[7] -
m[12] * m[3] * m[5];
inv[14] = -m[0] * m[5] * m[14] +
m[0] * m[6] * m[13] +
m[4] * m[1] * m[14] -
m[4] * m[2] * m[13] -
m[12] * m[1] * m[6] +
m[12] * m[2] * m[5];
inv[3] = -m[1] * m[6] * m[11] +
m[1] * m[7] * m[10] +
m[5] * m[2] * m[11] -
m[5] * m[3] * m[10] -
m[9] * m[2] * m[7] +
m[9] * m[3] * m[6];
inv[7] = m[0] * m[6] * m[11] -
m[0] * m[7] * m[10] -
m[4] * m[2] * m[11] +
m[4] * m[3] * m[10] +
m[8] * m[2] * m[7] -
m[8] * m[3] * m[6];
inv[11] = -m[0] * m[5] * m[11] +
m[0] * m[7] * m[9] +
m[4] * m[1] * m[11] -
m[4] * m[3] * m[9] -
m[8] * m[1] * m[7] +
m[8] * m[3] * m[5];
inv[15] = m[0] * m[5] * m[10] -
m[0] * m[6] * m[9] -
m[4] * m[1] * m[10] +
m[4] * m[2] * m[9] +
m[8] * m[1] * m[6] -
m[8] * m[2] * m[5];
det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
if (det == 0)
return Mat4f::Identity();
det = 1.f / det;
Mat4f res;
for (i = 0; i < 16; i++)
res.GetPtr()[i] = inv[i] * det;
return res;
}
#endif //__MATH_HXX__
================================================
FILE: src/pathtracer.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __PATHTRACER_HXX__
#define __PATHTRACER_HXX__
#include <vector>
#include <cmath>
#include "renderer.hxx"
#include "bsdf.hxx"
#include "rng.hxx"
class PathTracer : public AbstractRenderer
{
public:
PathTracer(
const Scene& aScene,
int aSeed = 1234
) :
AbstractRenderer(aScene), mRng(aSeed)
{}
virtual void RunIteration(int aIteration)
{
// We sample lights uniformly
const int lightCount = mScene.GetLightCount();
const float lightPickProb = 1.f / lightCount;
const int resX = int(mScene.mCamera.mResolution.x);
const int resY = int(mScene.mCamera.mResolution.y);
for(int pixID = 0; pixID < resX * resY; pixID++)
{
const int x = pixID % resX;
const int y = pixID / resX;
const Vec2f sample = Vec2f(float(x), float(y)) + mRng.GetVec2f();
Ray ray = mScene.mCamera.GenerateRay(sample);
Isect isect;
isect.dist = 1e36f;
Vec3f pathWeight(1.f);
Vec3f color(0.f);
uint pathLength = 1;
bool lastSpecular = true;
float lastPdfW = 1;
for(;; ++pathLength)
{
if(!mScene.Intersect(ray, isect))
{
if(pathLength < mMinPathLength)
break;
const BackgroundLight* background = mScene.GetBackground();
if(!background)
break;
// For background we cheat with the A/W suffixes,
// and GetRadiance actually returns W instead of A
float directPdfW;
Vec3f contrib = background->GetRadiance(mScene.mSceneSphere,
ray.dir, Vec3f(0), &directPdfW);
if(contrib.IsZero())
break;
float misWeight = 1.f;
if(pathLength > 1 && !lastSpecular)
{
misWeight = Mis2(lastPdfW, directPdfW * lightPickProb);
}
color += pathWeight * misWeight * contrib;
break;
}
Vec3f hitPoint = ray.org + ray.dir * isect.dist;
isect.dist += EPS_RAY;
BSDF<false> bsdf(ray, isect, mScene);
if(!bsdf.IsValid())
break;
// directly hit some light, lights do not reflect
if(isect.lightID >= 0)
{
if(pathLength < mMinPathLength)
break;
const AbstractLight *light = mScene.GetLightPtr(isect.lightID);
float directPdfA;
Vec3f contrib = light->GetRadiance(mScene.mSceneSphere,
ray.dir, hitPoint, &directPdfA);
if(contrib.IsZero())
break;
float misWeight = 1.f;
if(pathLength > 1 && !lastSpecular)
{
const float directPdfW = PdfAtoW(directPdfA, isect.dist,
bsdf.CosThetaFix());
misWeight = Mis2(lastPdfW, directPdfW * lightPickProb);
}
color += pathWeight * misWeight * contrib;
break;
}
if(pathLength >= mMaxPathLength)
break;
if(bsdf.ContinuationProb() == 0)
break;
// next event estimation
if(!bsdf.IsDelta() && pathLength + 1 >= mMinPathLength)
{
int lightID = int(mRng.GetFloat() * lightCount);
const AbstractLight *light = mScene.GetLightPtr(lightID);
Vec3f directionToLight;
float distance, directPdfW;
Vec3f radiance = light->Illuminate(mScene.mSceneSphere, hitPoint,
mRng.GetVec2f(), directionToLight, distance, directPdfW);
if(!radiance.IsZero())
{
float bsdfPdfW, cosThetaOut;
const Vec3f factor = bsdf.Evaluate(mScene,
directionToLight, cosThetaOut, &bsdfPdfW);
if(!factor.IsZero())
{
float weight = 1.f;
if(!light->IsDelta())
{
const float contProb = bsdf.ContinuationProb();
bsdfPdfW *= contProb;
weight = Mis2(directPdfW * lightPickProb, bsdfPdfW);
}
Vec3f contrib = (weight * cosThetaOut / (lightPickProb * directPdfW)) *
(radiance * factor);
if(!mScene.Occluded(hitPoint, directionToLight, distance))
{
color += pathWeight * contrib;
}
}
}
}
// continue random walk
{
Vec3f rndTriplet = mRng.GetVec3f();
float pdf, cosThetaOut;
uint sampledEvent;
Vec3f factor = bsdf.Sample(mScene, rndTriplet, ray.dir,
pdf, cosThetaOut, &sampledEvent);
if(factor.IsZero())
break;
// Russian roulette
const float contProb = bsdf.ContinuationProb();
lastSpecular = (sampledEvent & BSDF<true>::kSpecular) != 0;
lastPdfW = pdf * contProb;
if(contProb < 1.f)
{
if(mRng.GetFloat() > contProb)
{
break;
}
pdf *= contProb;
}
pathWeight *= factor * (cosThetaOut / pdf);
// We offset ray origin instead of setting tmin due to numeric
// issues in ray-sphere intersection. The isect.dist has to be
// extended by this EPS_RAY after hitpoint is determined
ray.org = hitPoint + EPS_RAY * ray.dir;
ray.tmin = 0.f;
isect.dist = 1e36f;
}
}
mFramebuffer.AddColor(sample, color);
}
mIterations++;
}
private:
// Mis power (1 for balance heuristic)
float Mis(float aPdf) const
{
return aPdf;
}
// Mis weight for 2 pdfs
float Mis2(
float aSamplePdf,
float aOtherPdf) const
{
return Mis(aSamplePdf) / (Mis(aSamplePdf) + Mis(aOtherPdf));
}
private:
Rng mRng;
};
#endif //__PATHTRACER_HXX__
================================================
FILE: src/ray.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __RAY_HXX__
#define __RAY_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
//////////////////////////////////////////////////////////////////////////
// Ray casting
struct Ray
{
Ray()
{}
Ray(const Vec3f& aOrg,
const Vec3f& aDir,
float aTMin
) :
org(aOrg),
dir(aDir),
tmin(aTMin)
{}
Vec3f org; //!< Ray origin
Vec3f dir; //!< Ray direction
float tmin; //!< Minimal distance to intersection
};
struct Isect
{
Isect()
{}
Isect(float aMaxDist):dist(aMaxDist)
{}
float dist; //!< Distance to closest intersection (serves as ray.tmax)
int matID; //!< ID of intersected material
int lightID; //!< ID of intersected light (if < 0, then none)
Vec3f normal; //!< Normal at the intersection
};
#endif //__RAY_HXX__
================================================
FILE: src/renderer.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __RENDERER_HXX__
#define __RENDERER_HXX__
#include <vector>
#include <cmath>
#include "scene.hxx"
#include "framebuffer.hxx"
class AbstractRenderer
{
public:
AbstractRenderer(const Scene& aScene) : mScene(aScene)
{
mMinPathLength = 0;
mMaxPathLength = 2;
mIterations = 0;
mFramebuffer.Setup(aScene.mCamera.mResolution);
}
virtual ~AbstractRenderer(){}
virtual void RunIteration(int aIteration) = 0;
void GetFramebuffer(Framebuffer& oFramebuffer)
{
oFramebuffer = mFramebuffer;
if(mIterations > 0)
oFramebuffer.Scale(1.f / mIterations);
}
//! Whether this renderer was used at all
bool WasUsed() const { return mIterations > 0; }
public:
uint mMaxPathLength;
uint mMinPathLength;
protected:
int mIterations;
Framebuffer mFramebuffer;
const Scene& mScene;
};
#endif //__RENDERER_HXX__
================================================
FILE: src/rng.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __RNG_HXX__
#define __RNG_HXX__
#include <vector>
#include <cmath>
#include "renderer.hxx"
#if defined(_MSC_VER)
# if (_MSC_VER < 1600)
# define LEGACY_RNG
# endif
#endif
#if !defined(LEGACY_RNG)
#include <random>
class Rng
{
public:
Rng(int aSeed = 1234):
mRng(aSeed)
{}
int GetInt()
{
return mDistInt(mRng);
}
uint GetUint()
{
return mDistUint(mRng);
}
float GetFloat()
{
return mDistFloat(mRng);
}
Vec2f GetVec2f()
{
float a = GetFloat();
float b = GetFloat();
return Vec2f(a, b);
}
Vec3f GetVec3f()
{
float a = GetFloat();
float b = GetFloat();
float c = GetFloat();
return Vec3f(a, b, c);
}
private:
std::mt19937_64 mRng;
std::uniform_int_distribution<int> mDistInt;
std::uniform_int_distribution<uint> mDistUint;
std::uniform_real_distribution<float> mDistFloat;
};
#else
template<unsigned int rounds>
class TeaImplTemplate
{
public:
void Reset(
uint aSeed0,
uint aSeed1)
{
mState0 = aSeed0;
mState1 = aSeed1;
}
uint GetImpl(void)
{
unsigned int sum=0;
const unsigned int delta=0x9e3779b9U;
for (unsigned int i=0; i<rounds; i++)
{
sum+=delta;
mState0+=((mState1<<4)+0xa341316cU) ^ (mState1+sum) ^ ((mState1>>5)+0xc8013ea4U);
mState1+=((mState0<<4)+0xad90777dU) ^ (mState0+sum) ^ ((mState0>>5)+0x7e95761eU);
}
return mState0;
}
private:
uint mState0, mState1;
};
typedef TeaImplTemplate<6> TeaImpl;
template<typename RandomImpl>
class RandomBase
{
public:
RandomBase(int aSeed = 1234)
{
mImpl.Reset(uint(aSeed), 5678);
}
uint GetUint()
{
return getImpl();
}
float GetFloat()
{
return (float(GetUint()) + 1.f) * (1.0f / 4294967297.0f);
}
Vec2f GetVec2f (void)
{
// cannot do return Vec2f(getF32(), getF32()) because the order is not ensured
float a = GetFloat();
float b = GetFloat();
return Vec2f(a, b);
}
Vec3f GetVec3f (void)
{
float a = GetFloat();
float b = GetFloat();
float c = GetFloat();
return Vec3f(a, b, c);
}
//////////////////////////////////////////////////////////////////////////
void StoreState(
uint *oState1,
uint *oState2)
{
mImpl.StoreState(oState1, oState2);
}
void LoadState(
uint aState1,
uint aState2,
uint aDimension)
{
mImpl.LoadState(aState1, aState2, aDimension);
}
protected:
uint getImpl(void)
{
return mImpl.GetImpl();
}
private:
RandomImpl mImpl;
};
typedef RandomBase<TeaImpl> Rng;
#endif
#endif //__RNG_HXX__
================================================
FILE: src/scene.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __SCENE_HXX__
#define __SCENE_HXX__
#include <vector>
#include <map>
#include <cmath>
#include "math.hxx"
#include "geometry.hxx"
#include "camera.hxx"
#include "materials.hxx"
#include "lights.hxx"
class Scene
{
public:
Scene() :
mGeometry(NULL),
mBackground(NULL)
{}
~Scene()
{
delete mGeometry;
for(size_t i=0; i<mLights.size(); i++)
delete mLights[i];
}
bool Intersect(
const Ray &aRay,
Isect &oResult) const
{
bool hit = mGeometry->Intersect(aRay, oResult);
if(hit)
{
oResult.lightID = -1;
std::map<int, int>::const_iterator it =
mMaterial2Light.find(oResult.matID);
if(it != mMaterial2Light.end())
oResult.lightID = it->second;
}
return hit;
}
bool Occluded(
const Vec3f &aPoint,
const Vec3f &aDir,
float aTMax) const
{
Ray ray;
ray.org = aPoint + aDir * EPS_RAY;
ray.dir = aDir;
ray.tmin = 0;
Isect isect;
isect.dist = aTMax - 2*EPS_RAY;
return mGeometry->IntersectP(ray, isect);
}
const Material& GetMaterial(const int aMaterialIdx) const
{
return mMaterials[aMaterialIdx];
}
int GetMaterialCount() const
{
return (int)mMaterials.size();
}
const AbstractLight* GetLightPtr(int aLightIdx) const
{
aLightIdx = std::min<int>(aLightIdx, mLights.size()-1);
return mLights[aLightIdx];
}
int GetLightCount() const
{
return (int)mLights.size();
}
const BackgroundLight* GetBackground() const
{
return mBackground;
}
//////////////////////////////////////////////////////////////////////////
// Loads a Cornell Box scene
enum BoxMask
{
kLightCeiling = 1,
kLightSun = 2,
kLightPoint = 4,
kLightBackground = 8,
kLargeMirrorSphere = 16,
kLargeGlassSphere = 32,
kSmallMirrorSphere = 64,
kSmallGlassSphere = 128,
kGlossyFloor = 256,
kBothSmallSpheres = (kSmallMirrorSphere | kSmallGlassSphere),
kBothLargeSpheres = (kLargeMirrorSphere | kLargeGlassSphere),
kDefault = (kLightCeiling | kBothSmallSpheres),
};
void LoadCornellBox(
const Vec2i &aResolution,
uint aBoxMask = kDefault)
{
mSceneName = GetSceneName(aBoxMask, &mSceneAcronym);
if((aBoxMask & kBothLargeSpheres) == kBothLargeSpheres)
{
printf("Cannot have both large balls, using mirror\n\n");
aBoxMask &= ~kLargeGlassSphere;
}
bool light_ceiling = (aBoxMask & kLightCeiling) != 0;
bool light_sun = (aBoxMask & kLightSun) != 0;
bool light_point = (aBoxMask & kLightPoint) != 0;
bool light_background = (aBoxMask & kLightBackground) != 0;
bool light_box = true;
// because it looks really weird with it
if(light_point)
light_box = false;
// Camera
mCamera.Setup(
Vec3f(-0.0439815f, -4.12529f, 0.222539f),
Vec3f(0.00688625f, 0.998505f, -0.0542161f),
Vec3f(3.73896e-4f, 0.0542148f, 0.998529f),
Vec2f(float(aResolution.x), float(aResolution.y)), 45);
// Materials
Material mat;
// 0) light1, will only emit
mMaterials.push_back(mat);
// 1) light2, will only emit
mMaterials.push_back(mat);
// 2) glossy white floor
mat.Reset();
mat.mDiffuseReflectance = Vec3f(0.1f);
mat.mPhongReflectance = Vec3f(0.7f);
mat.mPhongExponent = 90.f;
mMaterials.push_back(mat);
// 3) diffuse green left wall
mat.Reset();
mat.mDiffuseReflectance = Vec3f(0.156863f, 0.803922f, 0.172549f);
mMaterials.push_back(mat);
// 4) diffuse red right wall
mat.Reset();
mat.mDiffuseReflectance = Vec3f(0.803922f, 0.152941f, 0.152941f);
mMaterials.push_back(mat);
// 5) diffuse white back wall
mat.Reset();
mat.mDiffuseReflectance = Vec3f(0.803922f, 0.803922f, 0.803922f);
mMaterials.push_back(mat);
// 6) mirror ball
mat.Reset();
mat.mMirrorReflectance = Vec3f(1.f);
mMaterials.push_back(mat);
// 7) glass ball
mat.Reset();
mat.mMirrorReflectance = Vec3f(1.f);
mat.mIOR = 1.6f;
mMaterials.push_back(mat);
// 8) diffuse blue wall (back wall for glossy floor)
mat.Reset();
mat.mDiffuseReflectance = Vec3f(0.156863f, 0.172549f, 0.803922f);
mMaterials.push_back(mat);
delete mGeometry;
//////////////////////////////////////////////////////////////////////////
// Cornell box
Vec3f cb[8] = {
Vec3f(-1.27029f, 1.30455f, -1.28002f),
Vec3f( 1.28975f, 1.30455f, -1.28002f),
Vec3f( 1.28975f, 1.30455f, 1.28002f),
Vec3f(-1.27029f, 1.30455f, 1.28002f),
Vec3f(-1.27029f, -1.25549f, -1.28002f),
Vec3f( 1.28975f, -1.25549f, -1.28002f),
Vec3f( 1.28975f, -1.25549f, 1.28002f),
Vec3f(-1.27029f, -1.25549f, 1.28002f)
};
GeometryList *geometryList = new GeometryList;
mGeometry = geometryList;
if((aBoxMask & kGlossyFloor) != 0)
{
// Floor
geometryList->mGeometry.push_back(new Triangle(cb[0], cb[4], cb[5], 2));
geometryList->mGeometry.push_back(new Triangle(cb[5], cb[1], cb[0], 2));
// Back wall
geometryList->mGeometry.push_back(new Triangle(cb[0], cb[1], cb[2], 8));
geometryList->mGeometry.push_back(new Triangle(cb[2], cb[3], cb[0], 8));
}
else
{
// Floor
geometryList->mGeometry.push_back(new Triangle(cb[0], cb[4], cb[5], 5));
geometryList->mGeometry.push_back(new Triangle(cb[5], cb[1], cb[0], 5));
// Back wall
geometryList->mGeometry.push_back(new Triangle(cb[0], cb[1], cb[2], 5));
geometryList->mGeometry.push_back(new Triangle(cb[2], cb[3], cb[0], 5));
}
// Ceiling
if(light_ceiling && !light_box)
{
geometryList->mGeometry.push_back(new Triangle(cb[2], cb[6], cb[7], 0));
geometryList->mGeometry.push_back(new Triangle(cb[7], cb[3], cb[2], 1));
}
else
{
geometryList->mGeometry.push_back(new Triangle(cb[2], cb[6], cb[7], 5));
geometryList->mGeometry.push_back(new Triangle(cb[7], cb[3], cb[2], 5));
}
// Left wall
geometryList->mGeometry.push_back(new Triangle(cb[3], cb[7], cb[4], 3));
geometryList->mGeometry.push_back(new Triangle(cb[4], cb[0], cb[3], 3));
// Right wall
geometryList->mGeometry.push_back(new Triangle(cb[1], cb[5], cb[6], 4));
geometryList->mGeometry.push_back(new Triangle(cb[6], cb[2], cb[1], 4));
// Ball - central
float largeRadius = 0.8f;
Vec3f center = (cb[0] + cb[1] + cb[4] + cb[5]) * (1.f / 4.f) + Vec3f(0, 0, largeRadius);
if((aBoxMask & kLargeMirrorSphere) != 0)
geometryList->mGeometry.push_back(new Sphere(center, largeRadius, 6));
if((aBoxMask & kLargeGlassSphere) != 0)
geometryList->mGeometry.push_back(new Sphere(center, largeRadius, 7));
// Balls - left and right
float smallRadius = 0.5f;
Vec3f leftWallCenter = (cb[0] + cb[4]) * (1.f / 2.f) + Vec3f(0, 0, smallRadius);
Vec3f rightWallCenter = (cb[1] + cb[5]) * (1.f / 2.f) + Vec3f(0, 0, smallRadius);
float xlen = rightWallCenter.x - leftWallCenter.x;
Vec3f leftBallCenter = leftWallCenter + Vec3f(2.f * xlen / 7.f, 0, 0);
Vec3f rightBallCenter = rightWallCenter - Vec3f(2.f * xlen / 7.f, 0, 0);
if((aBoxMask & kSmallMirrorSphere) != 0)
geometryList->mGeometry.push_back(new Sphere(leftBallCenter, smallRadius, 6));
if((aBoxMask & kSmallGlassSphere) != 0)
geometryList->mGeometry.push_back(new Sphere(rightBallCenter, smallRadius, 7));
//////////////////////////////////////////////////////////////////////////
// Light box at the ceiling
Vec3f lb[8] = {
Vec3f(-0.25f, 0.25f, 1.26002f),
Vec3f( 0.25f, 0.25f, 1.26002f),
Vec3f( 0.25f, 0.25f, 1.28002f),
Vec3f(-0.25f, 0.25f, 1.28002f),
Vec3f(-0.25f, -0.25f, 1.26002f),
Vec3f( 0.25f, -0.25f, 1.26002f),
Vec3f( 0.25f, -0.25f, 1.28002f),
Vec3f(-0.25f, -0.25f, 1.28002f)
};
if(light_box)
{
// Back wall
geometryList->mGeometry.push_back(new Triangle(lb[0], lb[2], lb[1], 5));
geometryList->mGeometry.push_back(new Triangle(lb[2], lb[0], lb[3], 5));
// Left wall
geometryList->mGeometry.push_back(new Triangle(lb[3], lb[4], lb[7], 5));
geometryList->mGeometry.push_back(new Triangle(lb[4], lb[3], lb[0], 5));
// Right wall
geometryList->mGeometry.push_back(new Triangle(lb[1], lb[6], lb[5], 5));
geometryList->mGeometry.push_back(new Triangle(lb[6], lb[1], lb[2], 5));
// Front wall
geometryList->mGeometry.push_back(new Triangle(lb[4], lb[5], lb[6], 5));
geometryList->mGeometry.push_back(new Triangle(lb[6], lb[7], lb[4], 5));
if(light_ceiling)
{
// Floor
geometryList->mGeometry.push_back(new Triangle(lb[0], lb[5], lb[4], 0));
geometryList->mGeometry.push_back(new Triangle(lb[5], lb[0], lb[1], 1));
}
else
{
// Floor
geometryList->mGeometry.push_back(new Triangle(lb[0], lb[5], lb[4], 5));
geometryList->mGeometry.push_back(new Triangle(lb[5], lb[0], lb[1], 5));
}
}
//////////////////////////////////////////////////////////////////////////
// Lights
if(light_ceiling && !light_box)
{
// Without light box, whole ceiling is light
mLights.resize(2);
AreaLight *l = new AreaLight(cb[2], cb[6], cb[7]);
l->mIntensity = Vec3f(0.95492965f);
mLights[0] = l;
mMaterial2Light.insert(std::make_pair(0, 0));
l = new AreaLight(cb[7], cb[3], cb[2]);
l->mIntensity = Vec3f(0.95492965f);
mLights[1] = l;
mMaterial2Light.insert(std::make_pair(1, 1));
}
else if(light_ceiling && light_box)
{
// With light box
mLights.resize(2);
AreaLight *l = new AreaLight(lb[0], lb[5], lb[4]);
//l->mIntensity = Vec3f(0.95492965f);
l->mIntensity = Vec3f(25.03329895614464f);
mLights[0] = l;
mMaterial2Light.insert(std::make_pair(0, 0));
l = new AreaLight(lb[5], lb[0], lb[1]);
//l->mIntensity = Vec3f(0.95492965f);
l->mIntensity = Vec3f(25.03329895614464f);
mLights[1] = l;
mMaterial2Light.insert(std::make_pair(1, 1));
}
if(light_sun)
{
DirectionalLight *l = new DirectionalLight(Vec3f(-1.f, 1.5f, -1.f));
l->mIntensity = Vec3f(0.5f, 0.2f, 0.f) * 20.f;
mLights.push_back(l);
}
if(light_point)
{
PointLight *l = new PointLight(Vec3f(0.0, -0.5, 1.0));
l->mIntensity = Vec3f(70.f * (INV_PI_F * 0.25f));
mLights.push_back(l);
}
if(light_background)
{
BackgroundLight *l = new BackgroundLight;
l->mScale = 1.f;
mLights.push_back(l);
mBackground = l;
}
}
void BuildSceneSphere()
{
Vec3f bboxMin( 1e36f);
Vec3f bboxMax(-1e36f);
mGeometry->GrowBBox(bboxMin, bboxMax);
const float radius2 = (bboxMax - bboxMin).LenSqr();
mSceneSphere.mSceneCenter = (bboxMax + bboxMin) * 0.5f;
mSceneSphere.mSceneRadius = std::sqrt(radius2) * 0.5f;
mSceneSphere.mInvSceneRadiusSqr = 1.f / Sqr(mSceneSphere.mSceneRadius);
}
static std::string GetSceneName(
uint aBoxMask,
std::string *oAcronym = NULL)
{
std::string name;
std::string acronym;
// Floor type
if((aBoxMask & kGlossyFloor) == kGlossyFloor)
{
name += "glossy ";
acronym += "g";
}
// Box content
if((aBoxMask & kBothSmallSpheres) == kBothSmallSpheres)
{
name += "small spheres";
acronym += "bs";
}
else if((aBoxMask & kSmallMirrorSphere) == kSmallMirrorSphere)
{
name += "small mirror sphere";
acronym += "sm";
}
else if((aBoxMask & kSmallGlassSphere) == kSmallGlassSphere)
{
name += "small glass sphere";
acronym += "sg";
}
else if((aBoxMask & kLargeMirrorSphere) == kLargeMirrorSphere)
{
name += "large mirror sphere";
acronym += "lm";
}
else if((aBoxMask & kLargeGlassSphere) == kLargeGlassSphere)
{
name += "large glass sphere";
acronym += "lg";
}
else
{
name += "empty";
acronym += "e";
}
acronym += "_";
// Lighting
if((aBoxMask & kLightCeiling) == kLightCeiling)
{
name += " + ceiling (area)";
acronym += "c";
}
else if((aBoxMask & kLightSun) == kLightSun)
{
name += " + sun (directional)";
acronym += "s";
}
else if((aBoxMask & kLightPoint) == kLightPoint)
{
name += " + point";
acronym += "p";
}
else if((aBoxMask & kLightBackground) == kLightBackground)
{
name += " + background (env. lighting)";
acronym += "b";
}
if(oAcronym) *oAcronym = acronym;
return name;
}
public:
AbstractGeometry *mGeometry;
Camera mCamera;
std::vector<Material> mMaterials;
std::vector<AbstractLight*> mLights;
std::map<int, int> mMaterial2Light;
SceneSphere mSceneSphere;
BackgroundLight* mBackground;
std::string mSceneName;
std::string mSceneAcronym;
};
#endif //__SCENE_HXX__
================================================
FILE: src/smallvcm.cxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#include <vector>
#include <cmath>
#include <time.h>
#include <cstdlib>
#include "math.hxx"
#include "ray.hxx"
#include "geometry.hxx"
#include "camera.hxx"
#include "framebuffer.hxx"
#include "scene.hxx"
#include "eyelight.hxx"
#include "pathtracer.hxx"
#include "bsdf.hxx"
#include "vertexcm.hxx"
#include "html_writer.hxx"
#include "config.hxx"
#ifndef NO_OMP
#include <omp.h>
#endif
#include <string>
#include <set>
#include <sstream>
//////////////////////////////////////////////////////////////////////////
// The main rendering function, renders what is in aConfig
float render(
const Config &aConfig,
int *oUsedIterations = NULL)
{
// Set number of used threads
#ifndef NO_OMP
omp_set_num_threads(aConfig.mNumThreads);
#endif
// Create 1 renderer per thread
typedef AbstractRenderer* AbstractRendererPtr;
AbstractRendererPtr *renderers;
renderers = new AbstractRendererPtr[aConfig.mNumThreads];
for(int i=0; i<aConfig.mNumThreads; i++)
{
renderers[i] = CreateRenderer(aConfig, aConfig.mBaseSeed + i);
renderers[i]->mMaxPathLength = aConfig.mMaxPathLength;
renderers[i]->mMinPathLength = aConfig.mMinPathLength;
}
clock_t startT = clock();
int iter = 0;
// Rendering loop, when we have any time limit, use time-based loop,
// otherwise go with required iterations
if(aConfig.mMaxTime > 0)
{
// Time based loop
#pragma omp parallel
while(clock() < startT + aConfig.mMaxTime*CLOCKS_PER_SEC)
{
#ifndef NO_OMP
int threadId = omp_get_thread_num();
#else
int threadId = 0;
#endif
renderers[threadId]->RunIteration(iter);
#pragma omp atomic
iter++; // counts number of iterations
}
}
else
{
// Iterations based loop
#pragma omp parallel for
for(iter=0; iter < aConfig.mIterations; iter++)
{
#ifndef NO_OMP
int threadId = omp_get_thread_num();
#else
int threadId = 0;
#endif
renderers[threadId]->RunIteration(iter);
}
}
clock_t endT = clock();
if(oUsedIterations)
*oUsedIterations = iter+1;
// Accumulate from all renderers into a common framebuffer
int usedRenderers = 0;
// With very low number of iterations and high number of threads
// not all created renderers had to have been used.
// Those must not participate in accumulation.
for(int i=0; i<aConfig.mNumThreads; i++)
{
if(!renderers[i]->WasUsed())
continue;
if(usedRenderers == 0)
{
renderers[i]->GetFramebuffer(*aConfig.mFramebuffer);
}
else
{
Framebuffer tmp;
renderers[i]->GetFramebuffer(tmp);
aConfig.mFramebuffer->Add(tmp);
}
usedRenderers++;
}
// Scale framebuffer by the number of used renderers
aConfig.mFramebuffer->Scale(1.f / usedRenderers);
// Clean up renderers
for(int i=0; i<aConfig.mNumThreads; i++)
delete renderers[i];
delete [] renderers;
return float(endT - startT) / CLOCKS_PER_SEC;
}
//////////////////////////////////////////////////////////////////////////
// Generates index.html with all scene-algorithm combinations.
void FullReport(const Config &aConfig)
{
// Make a local copy of config
Config config = aConfig;
config.mFullReport = false;
// Setup framebuffer and threads
Framebuffer fbuffer;
config.mFramebuffer = &fbuffer;
// Setup html writer
HtmlWriter html_writer("index.html");
html_writer.WriteHeader();
html_writer.mAlgorithmCount = (int)Config::kAlgorithmMax;
html_writer.mThumbnailSize = 128;
int numIterations;
if(SizeOfArray(g_SceneConfigs) != 4)
{
printf("Report assumes we have only 4 scenes\n");
printf("Cannot continue with %d\n", SizeOfArray(g_SceneConfigs));
exit(2);
}
// Quite subjective evaluation whether given algorithm is
// for a given scene good, poor, or neutral.
std::set<int> goodAlgorithms[4], poorAlgorithms[4];
goodAlgorithms[0].insert(Config::kVertexConnectionMerging);
goodAlgorithms[0].insert(Config::kBidirectionalPhotonMapping);
poorAlgorithms[0].insert(Config::kBidirectionalPathTracing);
goodAlgorithms[1].insert(Config::kVertexConnectionMerging);
goodAlgorithms[1].insert(Config::kBidirectionalPhotonMapping);
poorAlgorithms[1].insert(Config::kBidirectionalPathTracing);
poorAlgorithms[1].insert(Config::kProgressivePhotonMapping);
goodAlgorithms[2].insert(Config::kVertexConnectionMerging);
goodAlgorithms[2].insert(Config::kBidirectionalPhotonMapping);
poorAlgorithms[2].insert(Config::kProgressivePhotonMapping);
goodAlgorithms[3].insert(Config::kVertexConnectionMerging);
goodAlgorithms[3].insert(Config::kBidirectionalPathTracing);
poorAlgorithms[3].insert(Config::kBidirectionalPhotonMapping);
poorAlgorithms[3].insert(Config::kProgressivePhotonMapping);
// Acronyms of algorithms in four-way split
std::string splitAcronyms[] = {"PPM", "BPM", "BPT", "VCM"};
// Filename and border color for images in four-way split
std::string splitFiles[4];
int borderColors[4];
clock_t startTime = clock();
for(int sceneID=0; sceneID<SizeOfArray(g_SceneConfigs); sceneID++)
{
Scene scene;
scene.LoadCornellBox(config.mResolution, g_SceneConfigs[sceneID]);
scene.BuildSceneSphere();
config.mScene = &scene;
html_writer.AddScene(scene.mSceneName);
printf("Scene: %s\n", scene.mSceneName.c_str());
for(uint algID = 0; algID < (uint)Config::kAlgorithmMax; algID++)
{
config.mAlgorithm = Config::Algorithm(algID);
printf("Running %s... ", config.GetName(config.mAlgorithm));
fflush(stdout);
float time = render(config, &numIterations);
printf("done in %.2f s\n", time);
std::string filename = DefaultFilename(g_SceneConfigs[sceneID],
*config.mScene, config.mAlgorithm);
fbuffer.SaveBMP(filename.c_str(), 2.2f);
// Add thumbnail of the method
HtmlWriter::BorderColor bcolor = HtmlWriter::kNone;
if(poorAlgorithms[sceneID].count(algID) > 0)
bcolor = HtmlWriter::kRed;
if(goodAlgorithms[sceneID].count(algID) > 0)
bcolor = HtmlWriter::kGreen;
html_writer.AddRendering(Config::GetName(config.mAlgorithm),
filename, time, bcolor,
html_writer.MakeMessage("<br/>Iterations: %d", numIterations));
if(algID >= (int)Config::kProgressivePhotonMapping)
{
const int idx = algID - Config::kProgressivePhotonMapping;
splitFiles[idx] = filename;
borderColors[idx] = bcolor;
}
}
html_writer.AddFourWaySplit(splitFiles, splitAcronyms,
borderColors, config.mResolution.x);
}
html_writer.Close();
clock_t endTime = clock();
printf("Whole run took %.2f s\n", float(endTime - startTime) / CLOCKS_PER_SEC);
}
//////////////////////////////////////////////////////////////////////////
// Main
int main(int argc, const char *argv[])
{
// Warns when not using C++11 Mersenne Twister
PrintRngWarning();
// Setups config based on command line
Config config;
ParseCommandline(argc, argv, config);
// If number of threads is invalid, set 1 thread per processor
if(config.mNumThreads <= 0)
#ifndef NO_OMP
config.mNumThreads = std::max(1, omp_get_num_procs());
#else
config.mNumThreads = 1;
#endif
if(config.mFullReport)
{
FullReport(config);
return 0;
}
// When some error has been encountered, exits
if(config.mScene == NULL)
return 1;
// Sets up framebuffer and number of threads
Framebuffer fbuffer;
config.mFramebuffer = &fbuffer;
// Prints what we are doing
printf("Scene: %s\n", config.mScene->mSceneName.c_str());
if(config.mMaxTime > 0)
printf("Target: %g seconds render time\n", config.mMaxTime);
else
printf("Target: %d iteration(s)\n", config.mIterations);
// Renders the image
printf("Running: %s... ", config.GetName(config.mAlgorithm));
fflush(stdout);
float time = render(config);
printf("done in %.2f s\n", time);
// Saves the image
std::string extension = config.mOutputName.substr(config.mOutputName.length() - 3, 3);
if(extension == "bmp")
fbuffer.SaveBMP(config.mOutputName.c_str(), 2.2f /*gamma*/);
else if(extension == "hdr")
fbuffer.SaveHDR(config.mOutputName.c_str());
else
printf("Used unknown extension %s\n", extension.c_str());
// Scene cleanup
delete config.mScene;
return 0;
}
================================================
FILE: src/utils.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __UTILS_HXX__
#define __UTILS_HXX__
#include <vector>
#include <cmath>
#include "math.hxx"
#define EPS_COSINE 1e-6f
#define EPS_RAY 1e-3f
// sRGB luminance
float Luminance(const Vec3f& aRGB)
{
return 0.212671f * aRGB.x +
0.715160f * aRGB.y +
0.072169f * aRGB.z;
}
float FresnelDielectric(
float aCosInc,
float mIOR)
{
if(mIOR < 0)
return 1.f;
float etaIncOverEtaTrans;
if(aCosInc < 0.f)
{
aCosInc = -aCosInc;
etaIncOverEtaTrans = mIOR;
}
else
{
etaIncOverEtaTrans = 1.f / mIOR;
}
const float sinTrans2 = Sqr(etaIncOverEtaTrans) * (1.f - Sqr(aCosInc));
const float cosTrans = std::sqrt(std::max(0.f, 1.f - sinTrans2));
const float term1 = etaIncOverEtaTrans * cosTrans;
const float rParallel =
(aCosInc - term1) / (aCosInc + term1);
const float term2 = etaIncOverEtaTrans * aCosInc;
const float rPerpendicular =
(term2 - cosTrans) / (term2 + cosTrans);
return 0.5f * (Sqr(rParallel) + Sqr(rPerpendicular));
}
// reflect vector through (0,0,1)
Vec3f ReflectLocal(const Vec3f& aVector)
{
return Vec3f(-aVector.x, -aVector.y, aVector.z);
}
//////////////////////////////////////////////////////////////////////////
// Cosine lobe hemisphere sampling
Vec3f SamplePowerCosHemisphereW(
const Vec2f &aSamples,
const float aPower,
float *oPdfW)
{
const float term1 = 2.f * PI_F * aSamples.x;
const float term2 = std::pow(aSamples.y, 1.f / (aPower + 1.f));
const float term3 = std::sqrt(1.f - term2 * term2);
if(oPdfW)
{
*oPdfW = (aPower + 1.f) * std::pow(term2, aPower) * (0.5f * INV_PI_F);
}
return Vec3f(
std::cos(term1) * term3,
std::sin(term1) * term3,
term2);
}
float PowerCosHemispherePdfW(
const Vec3f &aNormal,
const Vec3f &aDirection,
const float aPower)
{
const float cosTheta = std::max(0.f, Dot(aNormal, aDirection));
return (aPower + 1.f) * std::pow(cosTheta, aPower) * (INV_PI_F * 0.5f);
}
//////////////////////////////////////////////////////////////////////////
// Disc sampling
Vec2f SampleConcentricDisc(
const Vec2f &aSamples)
{
float phi, r;
float a = 2*aSamples.x - 1; /* (a,b) is now on [-1,1]^2 */
float b = 2*aSamples.y - 1;
if(a > -b) /* region 1 or 2 */
{
if(a > b) /* region 1, also |a| > |b| */
{
r = a;
phi = (PI_F/4.f) * (b/a);
}
else /* region 2, also |b| > |a| */
{
r = b;
phi = (PI_F/4.f) * (2.f - (a/b));
}
}
else /* region 3 or 4 */
{
if(a < b) /* region 3, also |a| >= |b|, a != 0 */
{
r = -a;
phi = (PI_F/4.f) * (4.f + (b/a));
}
else /* region 4, |b| >= |a|, but a==0 and b==0 could occur. */
{
r = -b;
if (b != 0)
phi = (PI_F/4.f) * (6.f - (a/b));
else
phi = 0;
}
}
Vec2f res;
res.x = r * std::cos(phi);
res.y = r * std::sin(phi);
return res;
}
float ConcentricDiscPdfA()
{
return INV_PI_F;
}
//////////////////////////////////////////////////////////////////////////
/// Sample direction in the upper hemisphere with cosine-proportional pdf
/** The returned PDF is with respect to solid angle measure */
Vec3f SampleCosHemisphereW(
const Vec2f &aSamples,
float *oPdfW)
{
const float term1 = 2.f * PI_F * aSamples.x;
const float term2 = std::sqrt(1.f - aSamples.y);
const Vec3f ret(
std::cos(term1) * term2,
std::sin(term1) * term2,
std::sqrt(aSamples.y));
if(oPdfW)
{
*oPdfW = ret.z * INV_PI_F;
}
return ret;
}
float CosHemispherePdfW(
const Vec3f &aNormal,
const Vec3f &aDirection)
{
return std::max(0.f, Dot(aNormal, aDirection)) * INV_PI_F;
}
// Sample Triangle
// returns barycentric coordinates
Vec2f SampleUniformTriangle(const Vec2f &aSamples)
{
const float term = std::sqrt(aSamples.x);
return Vec2f(1.f - term, aSamples.y * term);
}
//////////////////////////////////////////////////////////////////////////
// Sphere sampling
Vec3f SampleUniformSphereW(
const Vec2f &aSamples,
float *oPdfSA)
{
const float term1 = 2.f * PI_F * aSamples.x;
const float term2 = 2.f * std::sqrt(aSamples.y - aSamples.y * aSamples.y);
const Vec3f ret(
std::cos(term1) * term2,
std::sin(term1) * term2,
1.f - 2.f * aSamples.y);
if(oPdfSA)
{
//*oPdfSA = 1.f / (4.f * PI_F);
*oPdfSA = INV_PI_F * 0.25f;
}
return ret;
}
float UniformSpherePdfW()
{
//return (1.f / (4.f * PI_F));
return INV_PI_F * 0.25f;
}
//////////////////////////////////////////////////////////////////////////
// Utilities for converting PDF between Area (A) and Solid angle (W)
// WtoA = PdfW * cosine / distance_squared
// AtoW = PdfA * distance_squared / cosine
float PdfWtoA(
const float aPdfW,
const float aDist,
const float aCosThere)
{
return aPdfW * std::abs(aCosThere) / Sqr(aDist);
}
float PdfAtoW(
const float aPdfA,
const float aDist,
const float aCosThere)
{
return aPdfA * Sqr(aDist) / std::abs(aCosThere);
}
#endif //__UTILS_HXX__
================================================
FILE: src/vertexcm.hxx
================================================
/*
* Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* (The above is MIT License: http://en.wikipedia.org/wiki/MIT_License)
*/
#ifndef __VERTEXCM_HXX__
#define __VERTEXCM_HXX__
#include <vector>
#include <cmath>
#include <cassert>
#include "renderer.hxx"
#include "bsdf.hxx"
#include "rng.hxx"
#include "hashgrid.hxx"
////////////////////////////////////////////////////////////////////////////////
// A NOTE ON PATH MIS WEIGHT EVALUATION
////////////////////////////////////////////////////////////////////////////////
//
// We compute path MIS weights iteratively as we trace the light and eye
// sub-paths. We cache three floating points quantities at each sub-path vertex:
//
// dVCM dVC dVM
//
// These quantities represent partial weights associated with the sub-path. When
// we connect or merge one vertex to another, we use these quantities to quickly
// evaluate the MIS weight for the full path we have constructed. This scheme is
// presented in the technical report
//
// "Implementing Vertex Connection and Merging"
// http://www.iliyan.com/publications/ImplementingVCM
//
// The MIS code in the VertexCM class references the corresponding equations in
// the report in the form
//
// [tech. rep. (##)]
//
// where ## is the equation number.
//
class VertexCM : public AbstractRenderer
{
// The sole point of this structure is to make carrying around the ray baggage easier.
struct SubPathState
{
Vec3f mOrigin; // Path origin
Vec3f mDirection; // Where to go next
Vec3f mThroughput; // Path throughput
uint mPathLength : 30; // Number of path segments, including this
uint mIsFiniteLight : 1; // Just generate by finite light
uint mSpecularPath : 1; // All scattering events so far were specular
float dVCM; // MIS quantity used for vertex connection and merging
float dVC; // MIS quantity used for vertex connection
float dVM; // MIS quantity used for vertex merging
};
// Path vertex, used for merging and connection
template<bool tFromLight>
struct PathVertex
{
Vec3f mHitpoint; // Position of the vertex
Vec3f mThroughput; // Path throughput (including emission)
uint mPathLength; // Number of segments between source and vertex
// Stores all required local information, including incoming direction.
BSDF<tFromLight> mBsdf;
float dVCM; // MIS quantity used for vertex connection and merging
float dVC; // MIS quantity used for vertex connection
float dVM; // MIS quantity used for vertex merging
// Used by HashGrid
const Vec3f& GetPosition() const
{
return mHitpoint;
}
};
typedef PathVertex<false> CameraVertex;
typedef PathVertex<true> LightVertex;
typedef BSDF<false> CameraBSDF;
typedef BSDF<true> LightBSDF;
// Range query used for PPM, BPT, and VCM. When HashGrid finds a vertex
// within range -- Process() is called and vertex
// merging is performed. BSDF of the camera vertex is used.
class RangeQuery
{
public:
RangeQuery(
const VertexCM &aVertexCM,
const Vec3f &aCameraPosition,
const CameraBSDF &aCameraBsdf,
const SubPathState &aCameraState
) :
mVertexCM(aVertexCM),
mCameraPosition(aCameraPosition),
mCameraBsdf(aCameraBsdf),
mCameraState(aCameraState),
mContrib(0)
{}
const Vec3f& GetPosition() const { return mCameraPosition; }
const Vec3f& GetContrib() const { return mContrib; }
void Process(const LightVertex& aLightVertex)
{
// Reject if full path length below/above min/max path length
if((aLightVertex.mPathLength + mCameraState.mPathLength > mVertexCM.mMaxPathLength) ||
(aLightVertex.mPathLength + mCameraState.mPathLength < mVertexCM.mMinPathLength))
return;
// Retrieve light incoming direction in world coordinates
const Vec3f lightDirection = aLightVertex.mBsdf.WorldDirFix();
float cosCamera, cameraBsdfDirPdfW, cameraBsdfRevPdfW;
const Vec3f cameraBsdfFactor = mCameraBsdf.Evaluate(
mVertexCM.mScene, lightDirection, cosCamera, &cameraBsdfDirPdfW,
&cameraBsdfRevPdfW);
if(cameraBsdfFactor.IsZero())
return;
cameraBsdfDirPdfW *= mCameraBsdf.ContinuationProb();
// Even though this is pdf from camera BSDF, the continuation probability
// must come from light BSDF, because that would govern it if light path
// actually continued
cameraBsdfRevPdfW *= aLightVertex.mBsdf.ContinuationProb();
// Partial light sub-path MIS weight [tech. rep. (38)]
const float wLight = aLightVertex.dVCM * mVertexCM.mMisVcWeightFactor +
aLightVertex.dVM * mVertexCM.Mis(cameraBsdfDirPdfW);
// Partial eye sub-path MIS weight [tech. rep. (39)]
const float wCamera = mCameraState.dVCM * mVertexCM.mMisVcWeightFactor +
mCameraState.dVM * mVertexCM.Mis(cameraBsdfRevPdfW);
//
gitextract_o0afi678/
├── .gitignore
├── LICENSE
├── Makefile
├── Makefile.osx
├── README
├── SmallVCM.sln
├── SmallVCM.vcxproj
├── SmallVCM.vcxproj.filters
└── src/
├── bsdf.hxx
├── camera.hxx
├── config.hxx
├── eyelight.hxx
├── frame.hxx
├── framebuffer.hxx
├── geometry.hxx
├── hashgrid.hxx
├── html_writer.hxx
├── lights.hxx
├── materials.hxx
├── math.hxx
├── pathtracer.hxx
├── ray.hxx
├── renderer.hxx
├── rng.hxx
├── scene.hxx
├── smallvcm.cxx
├── utils.hxx
└── vertexcm.hxx
SYMBOL INDEX (253 symbols across 20 files)
FILE: src/bsdf.hxx
class BSDF (line 62) | class BSDF
type ComponentProbabilities (line 64) | struct ComponentProbabilities
type Events (line 72) | enum Events
method BSDF (line 85) | BSDF():mMaterialID(-1){}
method BSDF (line 87) | BSDF(
method Setup (line 95) | void Setup(
method Vec3f (line 128) | Vec3f Evaluate(
method Pdf (line 161) | float Pdf(
method Vec3f (line 191) | Vec3f Sample(
method IsValid (line 260) | bool IsValid() const { return mMaterialID >= 0; }
method IsDelta (line 261) | bool IsDelta() const { return mIsDelta; }
method ContinuationProb (line 262) | float ContinuationProb() const { return mContinuationProb; }
method CosThetaFix (line 263) | float CosThetaFix() const { return mLocalDirFix.z; }
method Vec3f (line 264) | Vec3f WorldDirFix() const { return mFrame.ToWorld(mLocalDirFix); }
method Vec3f (line 274) | Vec3f SampleDiffuse(
method Vec3f (line 290) | Vec3f SamplePhong(
method Vec3f (line 320) | Vec3f SampleReflect(
method Vec3f (line 335) | Vec3f SampleRefract(
method Vec3f (line 393) | Vec3f EvaluateDiffuse(
method Vec3f (line 414) | Vec3f EvaluatePhong(
method PdfDiffuse (line 456) | void PdfDiffuse(
method PdfPhong (line 474) | void PdfPhong(
method AlbedoDiffuse (line 508) | float AlbedoDiffuse(const Material& aMaterial) const
method AlbedoPhong (line 513) | float AlbedoPhong(const Material& aMaterial) const
method AlbedoReflect (line 518) | float AlbedoReflect(const Material& aMaterial) const
method AlbedoRefract (line 523) | float AlbedoRefract(const Material& aMaterial) const
method GetComponentProbabilities (line 528) | void GetComponentProbabilities(
FILE: src/camera.hxx
class Camera (line 33) | class Camera
method Setup (line 37) | void Setup(
method RasterToIndex (line 78) | int RasterToIndex(const Vec2f &aPixelCoords) const
method Vec2f (line 83) | Vec2f IndexToRaster(const int &aPixelIndex) const
method Vec3f (line 90) | Vec3f RasterToWorld(const Vec2f &aRasterXY) const
method Vec2f (line 95) | Vec2f WorldToRaster(const Vec3f &aWorldPos) const
method CheckRaster (line 102) | bool CheckRaster(const Vec2f &aRasterPos) const
method Ray (line 108) | Ray GenerateRay(const Vec2f &aRasterXY) const
FILE: src/config.hxx
type Config (line 52) | struct Config
type Algorithm (line 54) | enum Algorithm
function AbstractRenderer (line 112) | AbstractRenderer* CreateRenderer(
function DefaultFilename (line 153) | std::string DefaultFilename(
function SizeOfArray (line 178) | inline int SizeOfArray( const T(&)[ N ] )
function PrintRngWarning (line 183) | void PrintRngWarning()
function PrintHelp (line 195) | void PrintHelp(const char *argv[])
function ParseCommandline (line 225) | void ParseCommandline(int argc, const char *argv[], Config &oConfig)
FILE: src/eyelight.hxx
class EyeLight (line 36) | class EyeLight : public AbstractRenderer
method EyeLight (line 40) | EyeLight(
method RunIteration (line 47) | virtual void RunIteration(int aIteration)
FILE: src/frame.hxx
class Frame (line 32) | class Frame
method Frame (line 36) | Frame()
method Frame (line 43) | Frame(
method SetFromZ (line 53) | void SetFromZ(const Vec3f& z)
method Vec3f (line 61) | Vec3f ToWorld(const Vec3f& a) const
method Vec3f (line 66) | Vec3f ToLocal(const Vec3f& a) const
method Vec3f (line 71) | const Vec3f& Binormal() const { return mX; }
method Vec3f (line 72) | const Vec3f& Tangent () const { return mY; }
method Vec3f (line 73) | const Vec3f& Normal () const { return mZ; }
FILE: src/framebuffer.hxx
class Framebuffer (line 34) | class Framebuffer
method Framebuffer (line 38) | Framebuffer()
method AddColor (line 43) | void AddColor(
method Setup (line 61) | void Setup(const Vec2f& aResolution)
method Clear (line 70) | void Clear()
method Add (line 75) | void Add(const Framebuffer& aOther)
method Scale (line 81) | void Scale(float aScale)
method TotalLuminance (line 89) | float TotalLuminance()
method SavePPM (line 106) | void SavePPM(
method SavePFM (line 137) | void SavePFM(const char* aFilename)
type BmpHeader (line 150) | struct BmpHeader
method SaveBMP (line 170) | void SaveBMP(
method SaveHDR (line 219) | void SaveHDR(const char* aFilename)
FILE: src/geometry.hxx
class AbstractGeometry (line 36) | class AbstractGeometry
method IntersectP (line 46) | virtual bool IntersectP(const Ray& aRay, Isect& oResult) const
class GeometryList (line 55) | class GeometryList : public AbstractGeometry
method Intersect (line 65) | virtual bool Intersect(const Ray& aRay, Isect& oResult) const
method IntersectP (line 80) | virtual bool IntersectP(
method GrowBBox (line 93) | virtual void GrowBBox(
class Triangle (line 106) | class Triangle : public AbstractGeometry
method Triangle (line 110) | Triangle(){}
method Triangle (line 112) | Triangle(
method Intersect (line 125) | virtual bool Intersect(
method GrowBBox (line 158) | virtual void GrowBBox(
class Sphere (line 179) | class Sphere : public AbstractGeometry
method Sphere (line 183) | Sphere(){}
method Sphere (line 185) | Sphere(
method Intersect (line 198) | virtual bool Intersect(
method GrowBBox (line 239) | virtual void GrowBBox(
FILE: src/hashgrid.hxx
class HashGrid (line 32) | class HashGrid
method Reserve (line 35) | void Reserve(int aNumCells)
method Build (line 41) | void Build(
method Process (line 110) | void Process(
method Vec2i (line 173) | Vec2i GetCellRange(int aCellIndex) const
method GetCellIndex (line 179) | int GetCellIndex(const Vec3i &aCoord) const
method GetCellIndex (line 189) | int GetCellIndex(const Vec3f &aPoint) const
FILE: src/html_writer.hxx
class HtmlWriter (line 39) | class HtmlWriter
type BorderColor (line 43) | enum BorderColor
method HtmlWriter (line 52) | HtmlWriter(const std::string& aFileName) :
method Close (line 68) | void Close()
method WriteHeader (line 83) | void WriteHeader()
method AddScene (line 267) | void AddScene(const std::string &aSceneName)
method AddRendering (line 278) | void AddRendering(
method AddFourWaySplit (line 330) | void AddFourWaySplit(
method MakeMessage (line 359) | std::string MakeMessage(const char *fmt, ...)
FILE: src/lights.hxx
type SceneSphere (line 32) | struct SceneSphere
class AbstractLight (line 42) | class AbstractLight
class AreaLight (line 112) | class AreaLight : public AbstractLight
method AreaLight (line 116) | AreaLight(
method Vec3f (line 131) | virtual Vec3f Illuminate(
method Vec3f (line 168) | virtual Vec3f Emit(
method Vec3f (line 198) | virtual Vec3f GetRadiance(
method IsFinite (line 222) | virtual bool IsFinite() const { return true; }
method IsDelta (line 225) | virtual bool IsDelta() const { return false; }
class DirectionalLight (line 236) | class DirectionalLight : public AbstractLight
method DirectionalLight (line 239) | DirectionalLight(const Vec3f& aDirection)
method Vec3f (line 244) | virtual Vec3f Illuminate(
method Vec3f (line 267) | virtual Vec3f Emit(
method Vec3f (line 296) | virtual Vec3f GetRadiance(
method IsFinite (line 307) | virtual bool IsFinite() const { return false; }
method IsDelta (line 310) | virtual bool IsDelta() const { return true; }
class PointLight (line 320) | class PointLight : public AbstractLight
method PointLight (line 324) | PointLight(const Vec3f& aPosition)
method Vec3f (line 329) | virtual Vec3f Illuminate(
method Vec3f (line 354) | virtual Vec3f Emit(
method Vec3f (line 377) | virtual Vec3f GetRadiance(
method IsFinite (line 388) | virtual bool IsFinite() const { return true; }
method IsDelta (line 391) | virtual bool IsDelta() const { return true; }
class BackgroundLight (line 401) | class BackgroundLight : public AbstractLight
method BackgroundLight (line 404) | BackgroundLight()
method Vec3f (line 410) | virtual Vec3f Illuminate(
method Vec3f (line 438) | virtual Vec3f Emit(
method Vec3f (line 480) | virtual Vec3f GetRadiance(
method IsFinite (line 505) | virtual bool IsFinite() const { return false; }
method IsDelta (line 508) | virtual bool IsDelta() const { return false; }
FILE: src/materials.hxx
class Material (line 36) | class Material
method Material (line 39) | Material()
method Reset (line 44) | void Reset()
FILE: src/math.hxx
function T (line 34) | T Sqr(const T& a) { return a*a; }
class Vec2x (line 41) | class Vec2x
method Vec2x (line 45) | Vec2x(){}
method Vec2x (line 46) | Vec2x(T a):x(a),y(a){}
method Vec2x (line 47) | Vec2x(T a, T b):x(a),y(b){}
method T (line 49) | const T& Get(int a) const { return reinterpret_cast<const T*>(this)[a]; }
method T (line 50) | T& Get(int a) { return reinterpret_cast<T*>(this)[a]; }
method T (line 75) | T Dot(const Vec2x& a, const Vec2x& b)
class Vec3x (line 87) | class Vec3x
method Vec3x (line 91) | Vec3x(){}
method Vec3x (line 92) | Vec3x(T a):x(a),y(a),z(a){}
method Vec3x (line 93) | Vec3x(T a, T b, T c):x(a),y(b),z(c){}
method T (line 95) | const T& Get(int a) const { return reinterpret_cast<const T*>(this)[a]; }
method T (line 96) | T& Get(int a) { return reinterpret_cast<T*>(this)[a]; }
method GetXY (line 97) | Vec2x<T> GetXY() const { return Vec2x<T>(x, y); }
method T (line 98) | T Max() const { T res = Get(0); for(int i=1; i<3; i++) res...
method IsZero (line 100) | bool IsZero() const
method T (line 131) | T Dot(const Vec3x& a, const Vec3x& b)
method LenSqr (line 134) | float LenSqr() const { return Dot(*this, *this); }
method Length (line 135) | float Length() const { return std::sqrt(LenSqr()); }
function Vec3f (line 145) | Vec3f Cross(
function Vec3f (line 156) | Vec3f Normalize(const Vec3f& a)
class Mat4f (line 163) | class Mat4f
method Mat4f (line 167) | Mat4f(){}
method Mat4f (line 168) | Mat4f(float a){ for(int i=0; i<16; i++) GetPtr()[i] = a; }
method SetRow (line 176) | void SetRow(int r, float a, float b, float c, float d)
method SetRow (line 184) | void SetRow(int r, const Vec3f &a, float b)
method Vec3f (line 192) | Vec3f TransformVector(const Vec3f& aVec) const
method Vec3f (line 202) | Vec3f TransformPoint(const Vec3f& aVec) const
method Mat4f (line 225) | static Mat4f Zero() { Mat4f res(0); return res; }
method Mat4f (line 227) | static Mat4f Identity()
method Mat4f (line 234) | static Mat4f Scale(const Vec3f& aScale)
method Mat4f (line 242) | static Mat4f Translate(const Vec3f& aScale)
method Mat4f (line 250) | static Mat4f Perspective(
function Mat4f (line 277) | Mat4f operator*(const Mat4f& left, const Mat4f& right)
method Mat4f (line 167) | Mat4f(){}
method Mat4f (line 168) | Mat4f(float a){ for(int i=0; i<16; i++) GetPtr()[i] = a; }
method SetRow (line 176) | void SetRow(int r, float a, float b, float c, float d)
method SetRow (line 184) | void SetRow(int r, const Vec3f &a, float b)
method Vec3f (line 192) | Vec3f TransformVector(const Vec3f& aVec) const
method Vec3f (line 202) | Vec3f TransformPoint(const Vec3f& aVec) const
method Mat4f (line 225) | static Mat4f Zero() { Mat4f res(0); return res; }
method Mat4f (line 227) | static Mat4f Identity()
method Mat4f (line 234) | static Mat4f Scale(const Vec3f& aScale)
method Mat4f (line 242) | static Mat4f Translate(const Vec3f& aScale)
method Mat4f (line 250) | static Mat4f Perspective(
function Mat4f (line 290) | Mat4f Invert(const Mat4f& aMatrix)
method Mat4f (line 167) | Mat4f(){}
method Mat4f (line 168) | Mat4f(float a){ for(int i=0; i<16; i++) GetPtr()[i] = a; }
method SetRow (line 176) | void SetRow(int r, float a, float b, float c, float d)
method SetRow (line 184) | void SetRow(int r, const Vec3f &a, float b)
method Vec3f (line 192) | Vec3f TransformVector(const Vec3f& aVec) const
method Vec3f (line 202) | Vec3f TransformPoint(const Vec3f& aVec) const
method Mat4f (line 225) | static Mat4f Zero() { Mat4f res(0); return res; }
method Mat4f (line 227) | static Mat4f Identity()
method Mat4f (line 234) | static Mat4f Scale(const Vec3f& aScale)
method Mat4f (line 242) | static Mat4f Translate(const Vec3f& aScale)
method Mat4f (line 250) | static Mat4f Perspective(
FILE: src/pathtracer.hxx
class PathTracer (line 34) | class PathTracer : public AbstractRenderer
method PathTracer (line 38) | PathTracer(
method RunIteration (line 45) | virtual void RunIteration(int aIteration)
method Mis (line 220) | float Mis(float aPdf) const
method Mis2 (line 226) | float Mis2(
FILE: src/ray.hxx
type Ray (line 34) | struct Ray
method Ray (line 36) | Ray()
method Ray (line 39) | Ray(const Vec3f& aOrg,
type Isect (line 53) | struct Isect
method Isect (line 55) | Isect()
method Isect (line 58) | Isect(float aMaxDist):dist(aMaxDist)
FILE: src/renderer.hxx
class AbstractRenderer (line 33) | class AbstractRenderer
method AbstractRenderer (line 37) | AbstractRenderer(const Scene& aScene) : mScene(aScene)
method GetFramebuffer (line 49) | void GetFramebuffer(Framebuffer& oFramebuffer)
method WasUsed (line 58) | bool WasUsed() const { return mIterations > 0; }
FILE: src/rng.hxx
class Rng (line 41) | class Rng
method Rng (line 44) | Rng(int aSeed = 1234):
method GetInt (line 48) | int GetInt()
method uint (line 53) | uint GetUint()
method GetFloat (line 58) | float GetFloat()
method Vec2f (line 63) | Vec2f GetVec2f()
method Vec3f (line 71) | Vec3f GetVec3f()
class TeaImplTemplate (line 91) | class TeaImplTemplate
method Reset (line 94) | void Reset(
method uint (line 102) | uint GetImpl(void)
class RandomBase (line 125) | class RandomBase
method RandomBase (line 129) | RandomBase(int aSeed = 1234)
method uint (line 134) | uint GetUint()
method GetFloat (line 139) | float GetFloat()
method Vec2f (line 144) | Vec2f GetVec2f (void)
method Vec3f (line 152) | Vec3f GetVec3f (void)
method StoreState (line 161) | void StoreState(
method LoadState (line 168) | void LoadState(
method uint (line 178) | uint getImpl(void)
FILE: src/scene.hxx
class Scene (line 37) | class Scene
method Scene (line 40) | Scene() :
method Intersect (line 53) | bool Intersect(
method Occluded (line 72) | bool Occluded(
method Material (line 87) | const Material& GetMaterial(const int aMaterialIdx) const
method GetMaterialCount (line 92) | int GetMaterialCount() const
method AbstractLight (line 98) | const AbstractLight* GetLightPtr(int aLightIdx) const
method GetLightCount (line 104) | int GetLightCount() const
method BackgroundLight (line 109) | const BackgroundLight* GetBackground() const
type BoxMask (line 116) | enum BoxMask
method LoadCornellBox (line 132) | void LoadCornellBox(
method BuildSceneSphere (line 387) | void BuildSceneSphere()
method GetSceneName (line 400) | static std::string GetSceneName(
FILE: src/smallvcm.cxx
function render (line 52) | float render(
function FullReport (line 156) | void FullReport(const Config &aConfig)
function main (line 268) | int main(int argc, const char *argv[])
FILE: src/utils.hxx
function Luminance (line 36) | float Luminance(const Vec3f& aRGB)
function FresnelDielectric (line 43) | float FresnelDielectric(
function Vec3f (line 77) | Vec3f ReflectLocal(const Vec3f& aVector)
function Vec3f (line 85) | Vec3f SamplePowerCosHemisphereW(
function PowerCosHemispherePdfW (line 105) | float PowerCosHemispherePdfW(
function Vec2f (line 119) | Vec2f SampleConcentricDisc(
function ConcentricDiscPdfA (line 164) | float ConcentricDiscPdfA()
function Vec3f (line 173) | Vec3f SampleCosHemisphereW(
function CosHemispherePdfW (line 193) | float CosHemispherePdfW(
function Vec2f (line 202) | Vec2f SampleUniformTriangle(const Vec2f &aSamples)
function Vec3f (line 212) | Vec3f SampleUniformSphereW(
function UniformSpherePdfW (line 233) | float UniformSpherePdfW()
function PdfWtoA (line 245) | float PdfWtoA(
function PdfAtoW (line 253) | float PdfAtoW(
FILE: src/vertexcm.hxx
class VertexCM (line 61) | class VertexCM : public AbstractRenderer
type SubPathState (line 64) | struct SubPathState
type PathVertex (line 80) | struct PathVertex
method Vec3f (line 94) | const Vec3f& GetPosition() const
class RangeQuery (line 109) | class RangeQuery
method RangeQuery (line 113) | RangeQuery(
method Vec3f (line 126) | const Vec3f& GetPosition() const { return mCameraPosition; }
method Vec3f (line 128) | const Vec3f& GetContrib() const { return mContrib; }
method Process (line 130) | void Process(const LightVertex& aLightVertex)
type AlgorithmType (line 182) | enum AlgorithmType
method VertexCM (line 208) | VertexCM(
method RunIteration (line 284) | virtual void RunIteration(int aIteration)
method Mis (line 553) | float Mis(float aPdf) const
method Vec2f (line 564) | Vec2f GenerateCameraSample(
method Vec3f (line 617) | Vec3f GetLightRadiance(
method Vec3f (line 663) | Vec3f DirectIllumination(
method Vec3f (line 743) | Vec3f ConnectVertices(
method GenerateLightSample (line 816) | void GenerateLightSample(SubPathState &oLightState)
method ConnectToCamera (line 862) | void ConnectToCamera(
method SampleScattering (line 938) | bool SampleScattering(
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (241K chars).
[
{
"path": ".gitignore",
"chars": 92,
"preview": "*.bmp\n*.ppm\n*.pfm\n*.hdr\n*.suo\n*.sdf\n*.opensdf\n*.html\nSmallVCM.vcxproj.user\n/build/*\n/ipch/*\n"
},
{
"path": "LICENSE",
"chars": 1305,
"preview": "Notes:\nBecause I have been told this really ought to have a license, \nI picked the MIT License (http://en.wikipedia.org/"
},
{
"path": "Makefile",
"chars": 293,
"preview": "# This is under MIT licence\n# Also, I am not at all proud of this makefile, feel free to make better\n\nall: \n\tg++ -o smal"
},
{
"path": "Makefile.osx",
"chars": 307,
"preview": "# This is under MIT licence\n# Also, I am not at all proud of this makefile, feel free to make better\n\nall:\n\tc++ -o small"
},
{
"path": "README",
"chars": 11939,
"preview": "================================================================================\n SmallVCM\n============================="
},
{
"path": "SmallVCM.sln",
"chars": 860,
"preview": "\nMicrosoft Visual Studio Solution File, Format Version 11.00\n# Visual Studio 2010\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A"
},
{
"path": "SmallVCM.vcxproj",
"chars": 5180,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microso"
},
{
"path": "SmallVCM.vcxproj.filters",
"chars": 2515,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuil"
},
{
"path": "src/bsdf.hxx",
"chars": 19386,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/camera.hxx",
"chars": 4271,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/config.hxx",
"chars": 12731,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/eyelight.hxx",
"chars": 2676,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/frame.hxx",
"chars": 2255,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/framebuffer.hxx",
"chars": 8294,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/geometry.hxx",
"chars": 6982,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/hashgrid.hxx",
"chars": 7042,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/html_writer.hxx",
"chars": 23458,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/lights.hxx",
"chars": 16416,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/materials.hxx",
"chars": 2164,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/math.hxx",
"chars": 12377,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/pathtracer.hxx",
"chars": 8376,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/ray.hxx",
"chars": 2071,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/renderer.hxx",
"chars": 2173,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/rng.hxx",
"chars": 4109,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/scene.hxx",
"chars": 16133,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/smallvcm.cxx",
"chars": 10160,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/utils.hxx",
"chars": 6609,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
},
{
"path": "src/vertexcm.hxx",
"chars": 41637,
"preview": "/*\n * Copyright (C) 2012, Tomas Davidovic (http://www.davidovic.cz)\n *\n * Permission is hereby granted, free of charge, "
}
]
About this extraction
This page contains the full source code of the SmallVCM/SmallVCM GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (226.4 KB), approximately 60.2k tokens, and a symbol index with 253 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.