PolyVox  0.2.1
Open source voxel management library
AmbientOcclusionCalculator.inl
Go to the documentation of this file.
1 /*******************************************************************************
2 Copyright (c) 2005-2009 David Williams
3 
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7 
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11 
12  1. The origin of this software must not be misrepresented; you must not
13  claim that you wrote the original software. If you use this software
14  in a product, an acknowledgment in the product documentation would be
15  appreciated but is not required.
16 
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19 
20  3. This notice may not be removed or altered from any source
21  distribution.
22 *******************************************************************************/
23 
24 namespace PolyVox
25 {
34  template<typename VolumeType, typename IsVoxelTransparentCallback>
35  void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, IsVoxelTransparentCallback isVoxelTransparentCallback)
36  {
37  typename VolumeType::Sampler m_sampVolume(volInput);
38 
39  uint16_t uRandomUnitVectorIndex = 0;
40  uint16_t uRandomVectorIndex = 0;
41  uint16_t uIndexIncreament;
42 
43  //Make sure that the size of the volume is an exact multiple of the size of the array.
44  assert(volInput->getWidth() % arrayResult->getDimension(0) == 0);
45  assert(volInput->getHeight() % arrayResult->getDimension(1) == 0);
46  assert(volInput->getDepth() % arrayResult->getDimension(2) == 0);
47 
48  //Our initial indices. It doesn't matter exactly what we set here, but the code below makes
49  //sure they are different for different regions which helps reduce tiling patterns in the results.
50  uRandomUnitVectorIndex += region.getLowerCorner().getX() + region.getLowerCorner().getY() + region.getLowerCorner().getZ();
51  uRandomVectorIndex += region.getLowerCorner().getX() + region.getLowerCorner().getY() + region.getLowerCorner().getZ();
52 
53  //This value helps us jump around in the array a bit more, so the
54  //nth 'random' value isn't always followed by the n+1th 'random' value.
55  uIndexIncreament = 1;
56 
57  const int iRatioX = volInput->getWidth() / arrayResult->getDimension(0);
58  const int iRatioY = volInput->getHeight() / arrayResult->getDimension(1);
59  const int iRatioZ = volInput->getDepth() / arrayResult->getDimension(2);
60 
61  const float fRatioX = iRatioX;
62  const float fRatioY = iRatioY;
63  const float fRatioZ = iRatioZ;
64  const Vector3DFloat v3dRatio(fRatioX, fRatioY, fRatioZ);
65 
66  const float fHalfRatioX = fRatioX * 0.5f;
67  const float fHalfRatioY = fRatioY * 0.5f;
68  const float fHalfRatioZ = fRatioZ * 0.5f;
69  const Vector3DFloat v3dHalfRatio(fHalfRatioX, fHalfRatioY, fHalfRatioZ);
70 
71  const Vector3DFloat v3dOffset(0.5f,0.5f,0.5f);
72 
73  //This loop iterates over the bottom-lower-left voxel in each of the cells in the output array
74  for(uint16_t z = region.getLowerCorner().getZ(); z <= region.getUpperCorner().getZ(); z += iRatioZ)
75  {
76  for(uint16_t y = region.getLowerCorner().getY(); y <= region.getUpperCorner().getY(); y += iRatioY)
77  {
78  for(uint16_t x = region.getLowerCorner().getX(); x <= region.getUpperCorner().getX(); x += iRatioX)
79  {
80  //Compute a start position corresponding to
81  //the centre of the cell in the output array.
82  Vector3DFloat v3dStart(x, y, z);
83  v3dStart -= v3dOffset;
84  v3dStart += v3dHalfRatio;
85 
86  //Keep track of how many rays did not hit anything
87  uint8_t uVisibleDirections = 0;
88 
89  for(int ct = 0; ct < uNoOfSamplesPerOutputElement; ct++)
90  {
91  //We take a random vector with components going from -1 to 1 and scale it to go from -halfRatio to +halfRatio.
92  //This jitter value moves our sample point from the centre of the array cell to somewhere else in the array cell
93  Vector3DFloat v3dJitter = randomVectors[(uRandomVectorIndex += (++uIndexIncreament)) % 1019]; //Prime number helps avoid repetition on successive loops.
94  v3dJitter *= v3dHalfRatio;
95  const Vector3DFloat v3dRayStart = v3dStart + v3dJitter;
96 
97  Vector3DFloat v3dRayDirection = randomUnitVectors[(uRandomUnitVectorIndex += (++uIndexIncreament)) % 1021]; //Different prime number.
98  v3dRayDirection *= fRayLength;
99 
100  AmbientOcclusionCalculatorRaycastCallback<IsVoxelTransparentCallback> ambientOcclusionCalculatorRaycastCallback(isVoxelTransparentCallback);
101  RaycastResult result = raycastWithDirection(volInput, v3dRayStart, v3dRayDirection, ambientOcclusionCalculatorRaycastCallback);
102 
103  if(result == RaycastResults::Completed)
104  {
105  ++uVisibleDirections;
106  }
107  }
108 
109  float fVisibility;
110  if(uNoOfSamplesPerOutputElement == 0)
111  {
112  //The user might request zero samples (I've done this in the past while debugging - I don't want to
113  //wait for ambient occlusion but I do want as valid result for rendering). Avoid the divide by zero.
114  fVisibility = 1.0f;
115  }
116  else
117  {
118  fVisibility = static_cast<float>(uVisibleDirections) / static_cast<float>(uNoOfSamplesPerOutputElement);
119  assert((fVisibility >= 0.0f) && (fVisibility <= 1.0f));
120  }
121 
122  (*arrayResult)[z / iRatioZ][y / iRatioY][x / iRatioX] = static_cast<uint8_t>(255.0f * fVisibility);
123  }
124  }
125  }
126  }
127 }