You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

301 lines
13 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
  5. * Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file cpostshader_ssao.cpp
  26. * @brief Implements a post shader screen space ambient occlusion on software
  27. */
  28. #include "cpostshader_ssao.h"
  29. #include "../3d_fastmath.h"
  30. POST_SHADER_SSAO::POST_SHADER_SSAO( const CAMERA& aCamera ) :
  31. POST_SHADER( aCamera ),
  32. m_shadedBuffer( nullptr ),
  33. m_isUsingShadows( false )
  34. {
  35. }
  36. // There are different sources for this shader on the web
  37. //https://github.com/scanberg/hbao/blob/master/resources/shaders/ssao_frag.glsl
  38. //http://www.gamedev.net/topic/556187-the-best-ssao-ive-seen/
  39. //http://www.gamedev.net/topic/556187-the-best-ssao-ive-seen/?view=findpost&p=4632208
  40. float POST_SHADER_SSAO::aoFF( const SFVEC2I& aShaderPos, const SFVEC3F& ddiff,
  41. const SFVEC3F& cnorm, const float aShadowAtSamplePos,
  42. const float aShadowAtCenterPos, int c1, int c2 ) const
  43. {
  44. const float shadowGain = 0.60f;
  45. const float aoGain = 1.0f;
  46. const float shadow_factor_at_sample = ( 1.0f - aShadowAtSamplePos ) * shadowGain;
  47. const float shadow_factor_at_center = ( 1.0f - aShadowAtCenterPos ) * shadowGain;
  48. float return_value = shadow_factor_at_center;
  49. const float rd = glm::length( ddiff );
  50. // This limits the zero of the function (see below)
  51. if( rd < 2.0f )
  52. {
  53. if( rd > FLT_EPSILON )
  54. {
  55. const SFVEC3F vv = glm::normalize( ddiff );
  56. // Calculate an attenuation distance factor, this was get the best
  57. // results by experimentation
  58. // Changing this factor will change how much shadow in relation to the
  59. // distance of the hit it will be in shadow
  60. // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIwLjgteCowLjYiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjAsImVxIjoiMS8oeCp4KjAuNSsxKSIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0yLjU5Mjk0NTkyNTA5ODA0MSIsIjQuNTUzODc5NjU1NDQ1OTIzIiwiLTEuNzY3MDMwOTAzMjgxNjgxOCIsIjIuNjMxMDE1NjA3ODIyMjk3Il0sInNpemUiOls2NDksMzk5XX1d
  61. const float attDistFactor = 1.0f / ( rd * rd * 8.0f + 1.0f );
  62. const SFVEC2I vr = aShaderPos + SFVEC2I( c1, c2 );
  63. float sampledNormalFactor = glm::max( glm::dot( GetNormalAt( vr ), cnorm ), 0.0f );
  64. sampledNormalFactor = glm::max( 1.0f - sampledNormalFactor *
  65. sampledNormalFactor, 0.0f );
  66. const float shadowAttDistFactor = glm::max( glm::min( rd * 5.0f - 0.25f, 1.0f ), 0.0f );
  67. float shadowAttFactor = glm::min( sampledNormalFactor + shadowAttDistFactor, 1.0f );
  68. const float shadowFactor = glm::mix( shadow_factor_at_sample, shadow_factor_at_center,
  69. shadowAttFactor );
  70. // This is a dot product threshold factor.
  71. // it defines after which angle we consider that the point starts to occlude.
  72. // if the value is high, it will discard low angles point
  73. const float aDotThreshold = 0.15f;
  74. // This is the dot product between the center pixel normal (the one that is being
  75. // shaded) and the vector from the center to the sampled point
  76. const float localNormalFactor = glm::dot( cnorm, vv );
  77. const float localNormalFactorWithThreshold =
  78. ( glm::max( localNormalFactor, aDotThreshold ) - aDotThreshold) /
  79. ( 1.0f - aDotThreshold );
  80. const float aoFactor = localNormalFactorWithThreshold * aoGain * attDistFactor;
  81. return_value = glm::min( aoFactor + shadowFactor, 1.0f );
  82. }
  83. }
  84. return return_value;
  85. }
  86. float POST_SHADER_SSAO::giFF( const SFVEC2I& aShaderPos, const SFVEC3F& ddiff,
  87. const SFVEC3F& cnorm, const float aShadow, int c1, int c2 ) const
  88. {
  89. if( ( ddiff.x > FLT_EPSILON ) || ( ddiff.y > FLT_EPSILON ) || ( ddiff.z > FLT_EPSILON ) )
  90. {
  91. const SFVEC3F vv = glm::normalize( ddiff );
  92. const float rd = glm::length( ddiff );
  93. const SFVEC2I vr = aShaderPos + SFVEC2I( c1, c2 );
  94. const float attDistFactor = 1.0f / ( rd * rd + 1.0f );
  95. return ( glm::clamp( glm::dot( GetNormalAt( vr ), -vv), 0.0f, 1.0f ) *
  96. glm::clamp( glm::dot( cnorm, vv ), 0.0f, 1.0f ) * attDistFactor ) *
  97. ( 0.03f + aShadow ) * 3.0f;
  98. }
  99. return 0.0f;
  100. }
  101. SFVEC3F POST_SHADER_SSAO::Shade( const SFVEC2I& aShaderPos ) const
  102. {
  103. float cdepth = GetDepthAt( aShaderPos );
  104. if( cdepth > FLT_EPSILON )
  105. {
  106. cdepth = ( 30.0f / ( cdepth * 2.0f + 1.0f ) );
  107. // read current normal, position and color.
  108. const SFVEC3F n = GetNormalAt( aShaderPos );
  109. const SFVEC3F p = GetPositionAt( aShaderPos );
  110. const float shadowAt0 = GetShadowFactorAt( aShaderPos );
  111. // initialize variables:
  112. float ao = 0.0f;
  113. SFVEC3F gi = SFVEC3F( 0.0f );
  114. #define ROUNDS 3
  115. for( unsigned int i = 0; i < ROUNDS; ++i )
  116. {
  117. static const int limit[ROUNDS] = { 0x01, 0x03, 0x03 };
  118. const int pw = Fast_rand() & limit[i];
  119. const int ph = Fast_rand() & limit[i];
  120. const int npw = (int) ( ( pw + i ) * cdepth ) + ( i + 1 );
  121. const int nph = (int) ( ( ph + i ) * cdepth ) + ( i + 1 );
  122. const SFVEC3F ddiff = GetPositionAt( aShaderPos + SFVEC2I( npw, nph ) ) - p;
  123. const SFVEC3F ddiff2 = GetPositionAt( aShaderPos + SFVEC2I( npw, -nph ) ) - p;
  124. const SFVEC3F ddiff3 = GetPositionAt( aShaderPos + SFVEC2I( -npw, nph ) ) - p;
  125. const SFVEC3F ddiff4 = GetPositionAt( aShaderPos + SFVEC2I( -npw, -nph ) ) - p;
  126. const SFVEC3F ddiff5 = GetPositionAt( aShaderPos + SFVEC2I( pw, nph ) ) - p;
  127. const SFVEC3F ddiff6 = GetPositionAt( aShaderPos + SFVEC2I( pw, -nph ) ) - p;
  128. const SFVEC3F ddiff7 = GetPositionAt( aShaderPos + SFVEC2I( npw, ph ) ) - p;
  129. const SFVEC3F ddiff8 = GetPositionAt( aShaderPos + SFVEC2I(-npw, ph ) ) - p;
  130. const float shadowAt1 = GetShadowFactorAt( aShaderPos + SFVEC2I( +npw, nph ) );
  131. const float shadowAt2 = GetShadowFactorAt( aShaderPos + SFVEC2I( +npw, -nph ) );
  132. const float shadowAt3 = GetShadowFactorAt( aShaderPos + SFVEC2I( -npw, nph ) );
  133. const float shadowAt4 = GetShadowFactorAt( aShaderPos + SFVEC2I( -npw, -nph ) );
  134. const float shadowAt5 = GetShadowFactorAt( aShaderPos + SFVEC2I( +pw, nph ) );
  135. const float shadowAt6 = GetShadowFactorAt( aShaderPos + SFVEC2I( pw, -nph ) );
  136. const float shadowAt7 = GetShadowFactorAt( aShaderPos + SFVEC2I( npw, ph ) );
  137. const float shadowAt8 = GetShadowFactorAt( aShaderPos + SFVEC2I( -npw, ph ) );
  138. ao += aoFF( aShaderPos, ddiff , n, shadowAt1, shadowAt0, npw, nph );
  139. ao += aoFF( aShaderPos, ddiff2, n, shadowAt2, shadowAt0, npw, -nph );
  140. ao += aoFF( aShaderPos, ddiff3, n, shadowAt3, shadowAt0, -npw, nph );
  141. ao += aoFF( aShaderPos, ddiff4, n, shadowAt4, shadowAt0, -npw, -nph );
  142. ao += aoFF( aShaderPos, ddiff5, n, shadowAt5, shadowAt0, pw, nph );
  143. ao += aoFF( aShaderPos, ddiff6, n, shadowAt6, shadowAt0, pw, -nph );
  144. ao += aoFF( aShaderPos, ddiff7, n, shadowAt7, shadowAt0, npw, ph );
  145. ao += aoFF( aShaderPos, ddiff8, n, shadowAt8, shadowAt0, -npw, ph );
  146. gi += giFF( aShaderPos, ddiff , n, shadowAt1, npw, nph) *
  147. giColorCurve( GetColorAt( aShaderPos + SFVEC2I( npw, nph ) ) );
  148. gi += giFF( aShaderPos, ddiff2, n, shadowAt2, npw, -nph) *
  149. giColorCurve( GetColorAt( aShaderPos + SFVEC2I( npw,-nph ) ) );
  150. gi += giFF( aShaderPos, ddiff3, n, shadowAt3, -npw, nph) *
  151. giColorCurve( GetColorAt( aShaderPos + SFVEC2I( -npw, nph ) ) );
  152. gi += giFF( aShaderPos, ddiff4, n, shadowAt4, -npw, -nph) *
  153. giColorCurve( GetColorAt( aShaderPos + SFVEC2I( -npw,-nph ) ) );
  154. gi += giFF( aShaderPos, ddiff5, n, shadowAt5 , pw, nph) *
  155. giColorCurve( GetColorAt( aShaderPos + SFVEC2I( pw, nph ) ) );
  156. gi += giFF( aShaderPos, ddiff6, n, shadowAt6, pw,-nph) *
  157. giColorCurve( GetColorAt( aShaderPos + SFVEC2I( pw,-nph ) ) );
  158. gi += giFF( aShaderPos, ddiff7, n, shadowAt7, npw, ph) *
  159. giColorCurve( GetColorAt( aShaderPos + SFVEC2I( npw, ph ) ) );
  160. gi += giFF( aShaderPos, ddiff8, n, shadowAt8, -npw, ph) *
  161. giColorCurve( GetColorAt( aShaderPos + SFVEC2I( -npw, ph ) ) );
  162. }
  163. // If it received direct light, it shouldn't consider much AO
  164. // shadowAt0 1.0 when no shadow
  165. const float reduceAOwhenNoShadow = m_isUsingShadows ? ( 1.0f - shadowAt0 * 0.3f ) : 1.0f;
  166. ao = reduceAOwhenNoShadow * ( ao / ( ROUNDS * 8.0f ) );
  167. ao = ( 1.0f - 1.0f / ( ao * ao * 5.0f + 1.0f ) ) * 1.2f;
  168. gi = ( gi / ( ROUNDS * 8.0f ) );
  169. float giL = glm::min( glm::length( gi ) * 4.0f, 1.0f );
  170. giL = ( 1.0f - 1.0f / ( giL * 4.0f + 1.0f ) ) * 1.5f;
  171. return glm::mix( SFVEC3F( ao ), -gi, giL );
  172. }
  173. else
  174. {
  175. return SFVEC3F( 0.0f );
  176. }
  177. }
  178. SFVEC3F POST_SHADER_SSAO::ApplyShadeColor( const SFVEC2I& aShaderPos, const SFVEC3F& aInputColor,
  179. const SFVEC3F& aShadeColor ) const
  180. {
  181. SFVEC3F outColor;
  182. const SFVEC3F subtracted = aInputColor - aShadeColor;
  183. const SFVEC3F mixed = glm::mix( aInputColor, aInputColor * 0.50f - aShadeColor * 0.05f,
  184. glm::min( aShadeColor, 1.0f ) );
  185. outColor.r = ( aShadeColor.r < 0.0f ) ? subtracted.r : mixed.r;
  186. outColor.g = ( aShadeColor.g < 0.0f ) ? subtracted.g : mixed.g;
  187. outColor.b = ( aShadeColor.b < 0.0f ) ? subtracted.b : mixed.b;
  188. return outColor;
  189. }
  190. SFVEC3F POST_SHADER_SSAO::giColorCurve( const SFVEC3F& aColor ) const
  191. {
  192. const SFVEC3F vec1 = SFVEC3F( 1.0f );
  193. // This option actually apply a gamma since we are using linear color space
  194. // and the result shader will be applied after convert back to sRGB
  195. // http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLjAtKDEuMC8oeCo5LjArMS4wKSkreCowLjEiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjEwMDAsIndpbmRvdyI6WyItMC4wNjIxODQ2MTUzODQ2MTU1MDUiLCIxLjE0Mjk4NDYxNTM4NDYxNDYiLCItMC4xMjcwOTk5OTk5OTk5OTk3NyIsIjEuMTMyNiJdfV0-
  196. return vec1 - ( vec1 / (aColor * SFVEC3F(9.0f) + vec1) ) + aColor * SFVEC3F(0.10f);
  197. }
  198. SFVEC3F POST_SHADER_SSAO::Blur( const SFVEC2I& aShaderPos ) const
  199. {
  200. const float dCenter = GetDepthAt( aShaderPos );
  201. SFVEC3F shadedOut = SFVEC3F( 0.0f );
  202. float totalWeight = 1.0f;
  203. for( int y = -3; y < 3; y++ )
  204. {
  205. for( int x = -3; x < 3; x++ )
  206. {
  207. const unsigned int idx = GetIndex( SFVEC2I( aShaderPos.x + x, aShaderPos.y + y ) );
  208. const SFVEC3F s = m_shadedBuffer[idx];
  209. if( !( ( x == 0 ) && ( y == 0 ) ) )
  210. {
  211. const float d = GetDepthAt( SFVEC2I( aShaderPos.x + x, aShaderPos.y + y ) );
  212. // Increasing the value will get more sharpness effect.
  213. const float depthAtt = ( dCenter - d ) * dCenter * 25.0f;
  214. const float depthAttSqr = depthAtt * depthAtt;
  215. float weight = ( 1.0f / ( depthAttSqr + 1.0f ) ) - 0.02f * depthAttSqr;
  216. weight = glm::max( weight, 0.0f );
  217. shadedOut += s * weight;
  218. totalWeight += weight;
  219. }
  220. else
  221. {
  222. shadedOut += s;
  223. }
  224. }
  225. }
  226. return shadedOut / totalWeight;
  227. }