KiCad PCB EDA Suite
Loading...
Searching...
No Matches
post_shader_ssao.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015-2016 Mario Luzeiro <[email protected]>
5 * Copyright The 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, see <https://www.gnu.org/licenses/>.
19 */
20
25
26#include "post_shader_ssao.h"
27#include "../3d_fastmath.h"
28
29
31 POST_SHADER( aCamera ),
32 m_shadedBuffer( nullptr ),
33 m_isUsingShadows( false )
34{
35}
36
37// There are different sources for this shader on the web
38//https://github.com/scanberg/hbao/blob/master/resources/shaders/ssao_frag.glsl
39
40//http://www.gamedev.net/topic/556187-the-best-ssao-ive-seen/
41//http://www.gamedev.net/topic/556187-the-best-ssao-ive-seen/?view=findpost&p=4632208
42
43float POST_SHADER_SSAO::aoFF( const SFVEC2I& aShaderPos, const SFVEC3F& ddiff,
44 const SFVEC3F& cnorm, const float aShadowAtSamplePos,
45 const float aShadowAtCenterPos, int c1, int c2 ) const
46{
47 const float shadowGain = 0.60f;
48 const float aoGain = 1.0f;
49
50 const float shadow_factor_at_sample = ( 1.0f - aShadowAtSamplePos ) * shadowGain;
51 const float shadow_factor_at_center = ( 1.0f - aShadowAtCenterPos ) * shadowGain;
52
53 float return_value = shadow_factor_at_center;
54
55 const float rd = glm::length( ddiff );
56
57 // This limits the zero of the function (see below)
58 if( rd < 2.0f )
59 {
60 if( rd > FLT_EPSILON )
61 {
62 const SFVEC3F vv = glm::normalize( ddiff );
63
64 // Calculate an attenuation distance factor, this was get the best
65 // results by experimentation
66 // Changing this factor will change how much shadow in relation to the
67 // distance of the hit it will be in shadow
68
69 // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIwLjgteCowLjYiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjAsImVxIjoiMS8oeCp4KjAuNSsxKSIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIi0yLjU5Mjk0NTkyNTA5ODA0MSIsIjQuNTUzODc5NjU1NDQ1OTIzIiwiLTEuNzY3MDMwOTAzMjgxNjgxOCIsIjIuNjMxMDE1NjA3ODIyMjk3Il0sInNpemUiOls2NDksMzk5XX1d
70 const float attDistFactor = 1.0f / ( rd * rd * 8.0f + 1.0f );
71
72 const SFVEC2I vr = aShaderPos + SFVEC2I( c1, c2 );
73
74 float sampledNormalFactor = glm::max( glm::dot( GetNormalAt( vr ), cnorm ), 0.0f );
75
76 sampledNormalFactor = glm::max( 1.0f - sampledNormalFactor *
77 sampledNormalFactor, 0.0f );
78
79 const float shadowAttDistFactor = glm::max( glm::min( rd * 5.0f - 0.25f, 1.0f ), 0.0f );
80
81 float shadowAttFactor = glm::min( sampledNormalFactor + shadowAttDistFactor, 1.0f );
82
83 const float shadowFactor = glm::mix( shadow_factor_at_sample, shadow_factor_at_center,
84 shadowAttFactor );
85
86 // This is a dot product threshold factor.
87 // it defines after which angle we consider that the point starts to occlude.
88 // if the value is high, it will discard low angles point
89 const float aDotThreshold = 0.15f;
90
91 // This is the dot product between the center pixel normal (the one that is being
92 // shaded) and the vector from the center to the sampled point
93 const float localNormalFactor = glm::dot( cnorm, vv );
94
95 const float localNormalFactorWithThreshold =
96 ( glm::max( localNormalFactor, aDotThreshold ) - aDotThreshold ) /
97 ( 1.0f - aDotThreshold );
98
99 const float aoFactor = localNormalFactorWithThreshold * aoGain * attDistFactor;
100
101 return_value = glm::min( aoFactor + shadowFactor, 1.0f );
102 }
103 }
104
105 return return_value;
106}
107
108
109float POST_SHADER_SSAO::giFF( const SFVEC2I& aShaderPos, const SFVEC3F& ddiff,
110 const SFVEC3F& cnorm, const float aShadow, int c1, int c2 ) const
111{
112 if( ( ddiff.x > FLT_EPSILON ) || ( ddiff.y > FLT_EPSILON ) || ( ddiff.z > FLT_EPSILON ) )
113 {
114 const SFVEC3F vv = glm::normalize( ddiff );
115 const float rd = glm::length( ddiff );
116 const SFVEC2I vr = aShaderPos + SFVEC2I( c1, c2 );
117
118 const float attDistFactor = 1.0f / ( rd * rd + 1.0f );
119
120 return ( glm::clamp( glm::dot( GetNormalAt( vr ), -vv), 0.0f, 1.0f ) *
121 glm::clamp( glm::dot( cnorm, vv ), 0.0f, 1.0f ) * attDistFactor ) *
122 ( 0.03f + aShadow ) * 3.0f;
123 }
124
125 return 0.0f;
126}
127
128
129SFVEC3F POST_SHADER_SSAO::Shade( const SFVEC2I& aShaderPos ) const
130{
131 float cdepth = GetDepthAt( aShaderPos );
132
133 if( cdepth > FLT_EPSILON )
134 {
135 cdepth = ( 30.0f / ( cdepth * 2.0f + 1.0f ) );
136
137 // read current normal, position and color.
138 const SFVEC3F n = GetNormalAt( aShaderPos );
139 const SFVEC3F p = GetPositionAt( aShaderPos );
140
141 const float shadowAt0 = GetShadowFactorAt( aShaderPos );
142
143 // initialize variables:
144 float ao = 0.0f;
145 SFVEC3F gi = SFVEC3F( 0.0f );
146
147#define ROUNDS 3
148 for( unsigned int i = 0; i < ROUNDS; ++i )
149 {
150 static const int limit[ROUNDS] = { 0x01, 0x03, 0x03 };
151
152 const int pw = Fast_rand() & limit[i];
153 const int ph = Fast_rand() & limit[i];
154
155 const int npw = (int) ( ( pw + i ) * cdepth ) + ( i + 1 );
156 const int nph = (int) ( ( ph + i ) * cdepth ) + ( i + 1 );
157
158 const SFVEC3F ddiff = GetPositionAt( aShaderPos + SFVEC2I( npw, nph ) ) - p;
159 const SFVEC3F ddiff2 = GetPositionAt( aShaderPos + SFVEC2I( npw, -nph ) ) - p;
160 const SFVEC3F ddiff3 = GetPositionAt( aShaderPos + SFVEC2I( -npw, nph ) ) - p;
161 const SFVEC3F ddiff4 = GetPositionAt( aShaderPos + SFVEC2I( -npw, -nph ) ) - p;
162 const SFVEC3F ddiff5 = GetPositionAt( aShaderPos + SFVEC2I( pw, nph ) ) - p;
163 const SFVEC3F ddiff6 = GetPositionAt( aShaderPos + SFVEC2I( pw, -nph ) ) - p;
164 const SFVEC3F ddiff7 = GetPositionAt( aShaderPos + SFVEC2I( npw, ph ) ) - p;
165 const SFVEC3F ddiff8 = GetPositionAt( aShaderPos + SFVEC2I(-npw, ph ) ) - p;
166
167 const float shadowAt1 = GetShadowFactorAt( aShaderPos + SFVEC2I( +npw, nph ) );
168 const float shadowAt2 = GetShadowFactorAt( aShaderPos + SFVEC2I( +npw, -nph ) );
169 const float shadowAt3 = GetShadowFactorAt( aShaderPos + SFVEC2I( -npw, nph ) );
170 const float shadowAt4 = GetShadowFactorAt( aShaderPos + SFVEC2I( -npw, -nph ) );
171 const float shadowAt5 = GetShadowFactorAt( aShaderPos + SFVEC2I( +pw, nph ) );
172 const float shadowAt6 = GetShadowFactorAt( aShaderPos + SFVEC2I( pw, -nph ) );
173 const float shadowAt7 = GetShadowFactorAt( aShaderPos + SFVEC2I( npw, ph ) );
174 const float shadowAt8 = GetShadowFactorAt( aShaderPos + SFVEC2I( -npw, ph ) );
175
176 ao += aoFF( aShaderPos, ddiff , n, shadowAt1, shadowAt0, npw, nph );
177 ao += aoFF( aShaderPos, ddiff2, n, shadowAt2, shadowAt0, npw, -nph );
178 ao += aoFF( aShaderPos, ddiff3, n, shadowAt3, shadowAt0, -npw, nph );
179 ao += aoFF( aShaderPos, ddiff4, n, shadowAt4, shadowAt0, -npw, -nph );
180 ao += aoFF( aShaderPos, ddiff5, n, shadowAt5, shadowAt0, pw, nph );
181 ao += aoFF( aShaderPos, ddiff6, n, shadowAt6, shadowAt0, pw, -nph );
182 ao += aoFF( aShaderPos, ddiff7, n, shadowAt7, shadowAt0, npw, ph );
183 ao += aoFF( aShaderPos, ddiff8, n, shadowAt8, shadowAt0, -npw, ph );
184
185 gi += giFF( aShaderPos, ddiff , n, shadowAt1, npw, nph) *
186 giColorCurveShade( GetColorAt( aShaderPos + SFVEC2I( npw, nph ) ) );
187 gi += giFF( aShaderPos, ddiff2, n, shadowAt2, npw, -nph) *
188 giColorCurveShade( GetColorAt( aShaderPos + SFVEC2I( npw,-nph ) ) );
189 gi += giFF( aShaderPos, ddiff3, n, shadowAt3, -npw, nph) *
190 giColorCurveShade( GetColorAt( aShaderPos + SFVEC2I( -npw, nph ) ) );
191 gi += giFF( aShaderPos, ddiff4, n, shadowAt4, -npw, -nph) *
192 giColorCurveShade( GetColorAt( aShaderPos + SFVEC2I( -npw,-nph ) ) );
193 gi += giFF( aShaderPos, ddiff5, n, shadowAt5 , pw, nph) *
194 giColorCurveShade( GetColorAt( aShaderPos + SFVEC2I( pw, nph ) ) );
195 gi += giFF( aShaderPos, ddiff6, n, shadowAt6, pw,-nph) *
196 giColorCurveShade( GetColorAt( aShaderPos + SFVEC2I( pw,-nph ) ) );
197 gi += giFF( aShaderPos, ddiff7, n, shadowAt7, npw, ph) *
198 giColorCurveShade( GetColorAt( aShaderPos + SFVEC2I( npw, ph ) ) );
199 gi += giFF( aShaderPos, ddiff8, n, shadowAt8, -npw, ph) *
200 giColorCurveShade( GetColorAt( aShaderPos + SFVEC2I( -npw, ph ) ) );
201 }
202
203 // If it received direct light, it shouldn't consider much AO
204 // shadowAt0 1.0 when no shadow
205 const float reduceAOwhenNoShadow = m_isUsingShadows ? ( 1.0f - shadowAt0 * 0.3f ) : 1.0f;
206
207 ao = reduceAOwhenNoShadow * ( ao / ( ROUNDS * 8.0f ) );
208
209 ao = ( 1.0f - 1.0f / ( ao * ao * 5.0f + 1.0f ) ) * 1.2f;
210
211 gi = ( gi / ( ROUNDS * 8.0f ) );
212
213 float giL = glm::min( glm::length( gi ) * 4.0f, 1.0f );
214
215 giL = ( 1.0f - 1.0f / ( giL * 4.0f + 1.0f ) ) * 1.5f;
216
217 return glm::mix( SFVEC3F( ao ), -gi, giL );
218 }
219 else
220 {
221 return SFVEC3F( 0.0f );
222 }
223}
224
225
226SFVEC4F POST_SHADER_SSAO::ApplyShadeColor( const SFVEC2I& aShaderPos, const SFVEC4F& aInputColor,
227 const SFVEC3F& aShadeColor ) const
228{
229 SFVEC4F outColor;
230 SFVEC3F inColor( aInputColor );
231
232 const SFVEC3F subtracted = inColor - aShadeColor;
233 const SFVEC3F mixed = glm::mix( inColor, inColor * 0.50f - aShadeColor * 0.05f,
234 glm::min( aShadeColor, 1.0f ) );
235
236 outColor.r = ( aShadeColor.r < 0.0f ) ? subtracted.r : mixed.r;
237 outColor.g = ( aShadeColor.g < 0.0f ) ? subtracted.g : mixed.g;
238 outColor.b = ( aShadeColor.b < 0.0f ) ? subtracted.b : mixed.b;
239 outColor.a = std::max( aInputColor.a, ( aShadeColor.r + aShadeColor.g + aShadeColor.b ) / 3 );
240
241 return outColor;
242}
243
244
246{
247 const SFVEC3F vec1 = SFVEC3F( 1.0f );
248
249 // This option actually apply a gamma since we are using linear color space
250 // and the result shader will be applied after convert back to sRGB
251
252 // http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiIxLjAtKDEuMC8oeCo5LjArMS4wKSkreCowLjEiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjEwMDAsIndpbmRvdyI6WyItMC4wNjIxODQ2MTUzODQ2MTU1MDUiLCIxLjE0Mjk4NDYxNTM4NDYxNDYiLCItMC4xMjcwOTk5OTk5OTk5OTk3NyIsIjEuMTMyNiJdfV0-
253 return vec1 - ( vec1 / (aColor * SFVEC3F(9.0f) + vec1) ) + aColor * SFVEC3F(0.10f);
254}
255
256
258{
259 return SFVEC4F( giColorCurve( SFVEC3F( aColor ) ), aColor.a );
260}
261
262
264{
265 return giColorCurve( SFVEC3F( aColor ) );
266}
267
268
269SFVEC3F POST_SHADER_SSAO::Blur( const SFVEC2I& aShaderPos ) const
270{
271 const float dCenter = GetDepthAt( aShaderPos );
272
273 SFVEC3F shadedOut = SFVEC3F( 0.0f );
274
275 float totalWeight = 1.0f;
276
277 for( int y = -3; y < 3; y++ )
278 {
279 for( int x = -3; x < 3; x++ )
280 {
281
282 const unsigned int idx = GetIndex( SFVEC2I( aShaderPos.x + x, aShaderPos.y + y ) );
283
284 const SFVEC3F s = m_shadedBuffer[idx];
285
286 if( !( ( x == 0 ) && ( y == 0 ) ) )
287 {
288
289 const float d = GetDepthAt( SFVEC2I( aShaderPos.x + x, aShaderPos.y + y ) );
290
291 // Increasing the value will get more sharpness effect.
292 const float depthAtt = ( dCenter - d ) * dCenter * 25.0f;
293
294 const float depthAttSqr = depthAtt * depthAtt;
295
296 float weight = ( 1.0f / ( depthAttSqr + 1.0f ) ) - 0.02f * depthAttSqr;
297
298 weight = glm::max( weight, 0.0f );
299
300 shadedOut += s * weight;
301 totalWeight += weight;
302 }
303 else
304 {
305 shadedOut += s;
306 }
307 }
308 }
309
310 return shadedOut / totalWeight;
311}
int Fast_rand(void)
Defines math related functions.
A class used to derive camera objects from.
Definition camera.h:99
float aoFF(const SFVEC2I &aShaderPos, const SFVEC3F &ddiff, const SFVEC3F &cnorm, const float aShadowAtSamplePos, const float aShadowAtCenterPos, int c1, int c2) const
SFVEC3F giColorCurveShade(const SFVEC4F &aColor) const
float giFF(const SFVEC2I &aShaderPos, const SFVEC3F &ddiff, const SFVEC3F &cnorm, const float aShadow, int c1, int c2) const
SFVEC4F ApplyShadeColor(const SFVEC2I &aShaderPos, const SFVEC4F &aInputColor, const SFVEC3F &aShadeColor) const override
Apply the final color process using a previous stage color.
SFVEC3F Shade(const SFVEC2I &aShaderPos) const override
POST_SHADER_SSAO(const CAMERA &aCamera)
SFVEC3F giColorCurve(const SFVEC3F &aColor) const
Apply a curve transformation to the original color.
SFVEC3F Blur(const SFVEC2I &aShaderPos) const
unsigned int GetIndex(const SFVEC2F &aPos) const
Definition post_shader.h:62
POST_SHADER(const CAMERA &aCamera)
const SFVEC4F & GetColorAt(const SFVEC2F &aPos) const
const SFVEC3F & GetNormalAt(const SFVEC2F &aPos) const
const SFVEC3F & GetPositionAt(const SFVEC2F &aPos) const
const float & GetShadowFactorAt(const SFVEC2I &aPos) const
float GetDepthAt(const SFVEC2F &aPos) const
#define ROUNDS
Implements a post shader screen space ambient occlusion on software.
glm::ivec2 SFVEC2I
Definition xv3d_types.h:35
glm::vec3 SFVEC3F
Definition xv3d_types.h:40
glm::vec4 SFVEC4F
Definition xv3d_types.h:42