├── BRDF ├── AnisotropicMicrofacetBRDF.frag ├── MathConstants.frag └── NumericConstants.frag ├── LICENSE.txt ├── README.md ├── anisotropic_layered.frag ├── figs ├── example.png └── teaser.png └── sphere.tbscene /BRDF/AnisotropicMicrofacetBRDF.frag: -------------------------------------------------------------------------------- 1 | #ifndef ANISOTROPIC_MICROFACET_BRDF_frag 2 | #define ANISOTROPIC_MICROFACET_BRDF_frag 3 | 4 | #include "NumericConstants.frag" 5 | #include "MathConstants.frag" 6 | 7 | 8 | // Microfacet BRDF using the GGX NDF with the Smith height-correlated masking and shadowing function 9 | // [Eric Eitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs" (2014)] 10 | 11 | // Axis-aligned Anisotropic NDF 12 | float GGX( in vec3 halfvector, in vec2 roughness ) 13 | { 14 | const vec3 stretchedHalfvector = vec3( halfvector.x / roughness.x, halfvector.y / roughness.y, halfvector.z ); 15 | const float stretchedSquaredLength = dot( stretchedHalfvector, stretchedHalfvector ); 16 | 17 | return 1.0 / ( M_PI * ( roughness.x * roughness.y ) * ( stretchedSquaredLength * stretchedSquaredLength ) ); 18 | } 19 | 20 | 21 | // Axis-aligned Anisotropic BRDF 22 | float AnisotropicMicrofacetBRDF( in vec3 incomingDir, in vec3 outgoingDir, in vec2 roughness ) 23 | { 24 | const vec3 halfvector = normalize( incomingDir + outgoingDir ); 25 | const float zi = abs( incomingDir.z ); 26 | const float zo = abs( outgoingDir.z ); 27 | const float stretchedIncomingLength = length( vec3( incomingDir.x * roughness.x, incomingDir.y * roughness.y, incomingDir.z ) ); 28 | const float stretchedOutgoingLength = length( vec3( outgoingDir.x * roughness.x, outgoingDir.y * roughness.y, outgoingDir.z ) ); 29 | 30 | return min( GGX( halfvector, roughness ) / ( 2.0 * ( zo * stretchedIncomingLength + zi * stretchedOutgoingLength ) ), FLT_MAX ); 31 | } 32 | 33 | #endif -------------------------------------------------------------------------------- /BRDF/MathConstants.frag: -------------------------------------------------------------------------------- 1 | #ifndef MATH_CONSTANTS_frag 2 | #define MATH_CONSTANTS_frag 3 | 4 | #define M_PI ( 3.1415926535897932384626433832795 ) 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /BRDF/NumericConstants.frag: -------------------------------------------------------------------------------- 1 | #ifndef NUMERIC_CONSTANTS_frag 2 | #define NUMERIC_CONSTANTS_frag 3 | 4 | #define FLT_MANT_BITS ( 23 ) 5 | #define FLT_MIN ( 1.175494351e-38f ) 6 | #define FLT_MAX ( 3.402823466e+38f ) 7 | #define FLT_MAX_EXP ( 128 ) 8 | #define FLT_MIN_EXP ( -125 ) 9 | #define FLT_EPSILON ( 1.192092896e-07f ) 10 | 11 | #define HLF_MANT_BITS ( 10 ) 12 | #define HLF_MIN ( 1.0 / ( 1 << 14 ) ) 13 | #define HLF_MAX ( 65504.0 ) 14 | 15 | #define RGBE_MAX_EXP ( 16 ) 16 | #define RGBE_EXP_OFFSET ( 15 ) 17 | #define RGBE_MANT_BITS ( 9 ) 18 | #define RGBE_EXP_BITS ( 5 ) 19 | #define RGBE_MANT_RANGE ( 1 << RGBE_MANT_BITS ) 20 | #define RGBE_EXP_RANGE ( 1 << RGBE_EXP_BITS ) 21 | #define RGBE_MIN_NORMALIZED ( 1.0 / ( 1 << RGBE_EXP_OFFSET ) ) 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tomoya Yamaguchi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Real-time Rendering of Layered Materials with Anisotropic Normal Distributions 2 | === 3 | 4 | # Overview 5 | 6 | This is an official implementation of the paper "Real-time Rendering of Layered Materials with Anisotropic Normal Distributions" (SIGGRAPH ASIA 2019 Tech. brief). 7 | ![](./figs/teaser.png) 8 | 9 | # Description 10 | - BRDF & anisotropic_layered.frag: source code 11 | - figs: sample images 12 | - sphere.tbscene: sample scene 13 | 14 | # Build & Run 15 | You will 16 | Use [Marmoset Toolbag 3](https://marmoset.co/toolbag/) to build and run the program. 17 | Please install [Marmoset Toolbag 3](https://marmoset.co/toolbag/) to your computer beforehand. 18 | 19 | For **Windows**, 20 | 1. Move 'BRDF' and 'anisotropic_layered.frag' to 'Toolbag 3/data/shader/mat/custom' directory 21 | 2. Launch Marmoset Toolbag 3 22 | 3. Open the sample scene (sphere.tbscene) 23 | 24 | # Example 25 | 26 | We included a simple scene (sphere.tbscene) . 27 | ![](./figs/example.png) 28 | 29 | # License 30 | 31 | This software is released under the MIT License, see LICENSE.txt. -------------------------------------------------------------------------------- /anisotropic_layered.frag: -------------------------------------------------------------------------------- 1 | //we only want to alter these passes 2 | #if defined(MATERIAL_PASS_LIGHT) || defined(MATERIAL_PASS_VOXELIZATION) 3 | 4 | #include "../state.frag" 5 | #include "../other/lightParams.frag" 6 | #include "BRDF/AnisotropicMicrofacetBRDF.frag" 7 | #include "../other/customExtras.sh" 8 | 9 | #define NB_LAYERS 2 10 | 11 | //Input parameters 12 | uniform vec3 uDielectric_eta; //name "dielectric_eta" min 0.0001 max 2.0 default 1.49, 1.49, 1.49 13 | uniform vec3 uConductor_eta; //name "conductor_eta" min 0.0001 max 2.0 default 1.0, 1.0, 1.0 14 | uniform vec3 uConductor_kappa; //name "conductor_kappa" min 0.0 max 10.0 default 1.0, 0.0, 0.0 15 | uniform vec2 uDielectric_alpha; // name "dielectric_alpha" min 0.01 max 1.0 default 0.001 0.1 16 | uniform vec2 uConductor_alpha; // name "conductor_alpha" min 0.01 max 1.0 default 0.001 0.1 17 | uniform int uMaterial_set; // name "0:Layered 1:Dielectric, 2:Conductor" min 0 max 2 default 0 18 | uniform int uSample_num; // name "sumple_num" min 1 max 4096 default 1024 19 | uniform float uDielectric_rotate; // name "Rotation_Dielectric" min -90.00 max 90.00 default 45.0 20 | uniform float uConductor_rotate; // name "Rotation_Conductor" min -90.00 max 90.00 default -75.0 21 | 22 | //math 23 | //----begin---- 24 | float average (vec3 v) {return (v.x + v.y + v.z) / 3.0;} 25 | bool isZero (vec3 v) {return (v.x==0.0 && v.y==0.0 && v.z==0.0) ? true : false;} 26 | vec2 CalculateEigenValues( in mat2 m ) 27 | { 28 | const float avg = ( m._11 + m._22 ) / 2.0; // Average eigenvalue. 29 | const float det = max( determinant( m ), 0.0 ); // The determinant must be within [0, square of the average eigenvalue]. 30 | const float eigenvalueMax = avg + sqrt( max( avg * avg - det, 0.0 ) ); 31 | const float eigenvalueMin = min( det / eigenvalueMax, avg ); // To avoid the numerical error, we compute the minimum eigenvalue using the maximum eigenvalue. 32 | 33 | return vec2( eigenvalueMax, eigenvalueMin ); 34 | } 35 | 36 | // The input variable eigenvalue is assumed to be the maximum eigenvalue for the numerical stability. 37 | // If it is not the maximum, the resulting eigenvector can have a large precision error. 38 | // This implementation assumes m._12 = m._21. 39 | vec2 CalculateEigenVectorMax( in mat2 m, in float eigenvalue ) 40 | { 41 | return normalize( m._11 < m._22 ? vec2( m._12, eigenvalue - m._11 ) : vec2( eigenvalue - m._22, m._12 ) ); 42 | } 43 | 44 | // The input variable eigenvalue is assumed to be the minimum eigenvalue for the numerical stability. 45 | // If it is not the minimum, the resulting eigenvector can have a large precision error. 46 | // This implementation assumes m._12 = m._21. 47 | vec2 CalculateEigenVectorMin( in mat2 m, in float eigenvalue ) 48 | { 49 | return normalize( m._11 > m._22 ? vec2( m._12, eigenvalue - m._11 ) : vec2( eigenvalue - m._22, m._12 ) ); 50 | } 51 | 52 | float GetRandomNumber(in vec2 v) 53 | { 54 | return frac(sin(dot(v.xy, vec2(12.9898, 78.233))) * 43758.5453); 55 | } 56 | 57 | static vec2 randState; 58 | float rand() 59 | { 60 | randState.x = GetRandomNumber(randState); 61 | randState.y = GetRandomNumber(randState); 62 | return randState.x; 63 | } 64 | 65 | vec3 Stretch( in vec3 direction, in vec2 roughness ) 66 | { 67 | return vec3( direction.x * roughness.x, direction.y * roughness.y, direction.z ); 68 | } 69 | 70 | mat2 inverse(in mat2 m) 71 | { 72 | return mat2(m._22, -m._12, -m._21, m._11) / determinant(m); 73 | } 74 | //----end---- 75 | 76 | /* Roughness to linear space conversions*/ 77 | //----begin---- 78 | #define USE_BEST_FIT 79 | float roughnessToVariance(float a) 80 | { 81 | #ifdef USE_BEST_FIT 82 | a = clamp(a, 0.0, 0.9999); 83 | float a3 = pow(a, 1.1); 84 | return a3 / (1.0 - a3); 85 | #else 86 | return a / (1.0-a); 87 | #endif 88 | } 89 | float varianceToRoughness(float v) 90 | { 91 | #ifdef USE_BEST_FIT 92 | return pow(v / (1.0 + v), 1.0/1.1); 93 | #else 94 | return v / (1.0+v); 95 | #endif 96 | } 97 | vec2 roughnessToVariance(vec2 v) 98 | { 99 | #ifdef USE_BEST_FIT 100 | vec2 vout = vec2(clamp(v.x, 0.0, 0.9999), clamp(v.y, 0.0, 0.9999)); 101 | vec2 v3 = vec2(pow(vout.x, 1.1), pow(vout.y, 1.1)); 102 | return v3 / (vec2(1.0, 1.0) - v3); 103 | #else 104 | return v / (vec2(1.0, 1.0) - v); 105 | #endif 106 | } 107 | vec2 varianceToRoughness(vec2 v) 108 | { 109 | #ifdef USE_BEST_FIT 110 | return vec2(pow(v.x / (1.0 + v.x), 1.0/1.1), pow(v.y / (1.0 + v.y), 1.0/1.1)); 111 | #else 112 | return v / (vec3(1.0, 1.0) + v); 113 | #endif 114 | } 115 | //----end---- 116 | 117 | vec3 fresnelConductorExact(float cosThetaI, vec3 eta, vec3 k) { 118 | /*From Mitsuba(https://github.com/mitsuba-renderer/mitsuba/blob/1fd0f671dfcb77f813c0d6a36f2aa4e480b5ca8e/src/libcore/util.cpp) */ 119 | float cosThetaI2 = cosThetaI*cosThetaI, 120 | sinThetaI2 = 1-cosThetaI2, 121 | sinThetaI4 = sinThetaI2*sinThetaI2; 122 | 123 | vec3 temp1 = eta*eta - k*k - sinThetaI2, 124 | a2pb2 = sqrt(max(temp1*temp1 + k*k*eta*eta*4, 0.0)), 125 | a = sqrt(max((a2pb2 + temp1) * 0.5f, 0.0)); 126 | 127 | vec3 term1 = a2pb2 + vec3(cosThetaI2, cosThetaI2, cosThetaI2), 128 | term2 = a*(2*cosThetaI); 129 | 130 | vec3 Rs2 = (term1 - term2) / (term1 + term2); 131 | 132 | vec3 term3 = a2pb2*cosThetaI2 + vec3(sinThetaI4, sinThetaI4, sinThetaI4), 133 | term4 = term2*sinThetaI2; 134 | 135 | vec3 Rp2 = Rs2 * (term3 - term4) / (term3 + term4); 136 | 137 | return 0.5 * (Rp2 + Rs2); 138 | } 139 | 140 | vec3 fresnelDielectricExt(float cosThetaI_, float eta) 141 | { 142 | /*From Mitsuba(https://github.com/mitsuba-renderer/mitsuba/blob/1fd0f671dfcb77f813c0d6a36f2aa4e480b5ca8e/src/libcore/util.cpp) */ 143 | float cosThetaT_; 144 | if (eta == 1.0) { 145 | cosThetaT_ = -cosThetaI_; 146 | return 0.0f; 147 | } 148 | 149 | /* Using Snell's law, calculate the squared sine of the 150 | angle between the normal and the transmitted ray */ 151 | float scale = (cosThetaI_ > 0) ? 1.0/eta : eta, 152 | cosThetaTSqr = 1 - (1-cosThetaI_*cosThetaI_) * (scale*scale); 153 | 154 | /* Check for total internal reflection */ 155 | if (cosThetaTSqr <= 0.0) { 156 | cosThetaT_ = 0.0; 157 | return 1.0; 158 | } 159 | 160 | /* Find the absolute cosines of the incident/transmitted rays */ 161 | float cosThetaI = abs(cosThetaI_); 162 | float cosThetaT = sqrt(cosThetaTSqr); 163 | 164 | float Rs = (cosThetaI - eta * cosThetaT) 165 | / (cosThetaI + eta * cosThetaT); 166 | float Rp = (eta * cosThetaI - cosThetaT) 167 | / (eta * cosThetaI + cosThetaT); 168 | 169 | cosThetaT_ = (cosThetaI_ > 0) ? -cosThetaT : cosThetaT; 170 | 171 | /* No polarization -- return the unpolarized reflectance */ 172 | return 0.5 * (Rs * Rs + Rp * Rp); 173 | } 174 | 175 | /* Common Eval Fresnel function. Permits to switch between FGD and non-FGD 176 | * evaluations of the Fresnel term. 177 | */ 178 | void evalFresnel(in float ct, in vec3 eta, in vec3 kappa, 179 | out vec3 Rij, out vec3 Tij) 180 | { 181 | Rij = (isZero(kappa)) ? fresnelDielectricExt(ct, eta[0]) * vec3(1.0, 1.0, 1.0) : fresnelConductorExact(ct, eta, kappa); 182 | Tij = (isZero(kappa)) ? vec3(1.0, 1.0, 1.0) - Rij : vec3(0.0, 0.0, 0.0); 183 | } 184 | 185 | // Evaluation of the NDF. 186 | //----begin---- 187 | // For perfect specular surfaces, it returns zero. 188 | float EvaluateNDF( in vec3 halfvector, in vec2 roughness ) 189 | { 190 | vec3 H = vec3( halfvector.x / roughness.x, halfvector.y / roughness.y, halfvector.z ); 191 | float squaredLength = dot(H, H); 192 | return ( halfvector.z > 0.0f ) ? ( 1.0 / ( max( M_PI * roughness.x * roughness.y, FLT_MIN ) * ( squaredLength * squaredLength ) ) ) : 0.0; 193 | } 194 | 195 | float EvaluatePDFOverNDF( in vec3 incomingDir, in vec2 roughness ) 196 | { 197 | float zi = abs( incomingDir.z ); 198 | float incomingLength = length( Stretch( incomingDir, roughness ) ); 199 | // The Heaviside functions are omitted in this implementation. 200 | // This is not a problem for the specular microfacet BRDF. 201 | return 0.5 / ( zi + incomingLength ); 202 | } 203 | 204 | // PDF of outgoing directions using VNDFs 205 | float EvaluatePDF( in vec3 incomingDir, in vec3 halfvector, in vec2 roughness ) 206 | { 207 | return EvaluateNDF( halfvector, roughness ) * EvaluatePDFOverNDF( incomingDir, roughness ); 208 | } 209 | 210 | // VNDF importance sampling for the Smith microsurface model. 211 | // [Heitz 2018 "Sampling the GGX Distribution of Visible Normals"]. 212 | vec3 SampleMicrofacetNormal( in vec3 direction, in vec2 randomNumbers, in vec2 roughness ) 213 | { 214 | // assert( isfinite( direction.x ) && isfinite( direction.y ) && isfinite( direction.z ) ); 215 | // assert( isfinite( randomNumbers.x ) && isfinite( randomNumbers.y ) ); 216 | 217 | // Stretch and normalize the view direction 218 | const vec3 stretchedDir = normalize( Stretch( direction, roughness ) ); 219 | 220 | // Sample a point on the half disk. 221 | const float radius = sqrt( randomNumbers.x ); 222 | const float phi = 2*M_PI * randomNumbers.y; 223 | const float x = radius * cos( phi ); 224 | const float t = radius * sin( phi ); 225 | const float s = 0.5 * ( 1.0 + stretchedDir.z ); 226 | const float y = lerp( sqrt( 1.0 - x * x ), t, s ); 227 | 228 | // Build an orthonormal basis. 229 | const vec3 unnormalizedBasisX = { -stretchedDir.y, stretchedDir.x, 0.0 }; 230 | const float basisXLength = length( unnormalizedBasisX ); 231 | const vec3 basisX = basisXLength != 0.0 ? unnormalizedBasisX / basisXLength : vec3( 1.0, 0.0, 0.0 ); 232 | const vec3 basisY = cross( stretchedDir, basisX ); 233 | 234 | // Compute the microfacet normal in the stretched space. 235 | // z must be equal ot greater than 0, so it is clamed by 0 to improve the numerical stability. 236 | const float z = sqrt( max( 1.0 - x * x - y * y, 0.0 ) ); 237 | const vec3 pos = vec3( x, y, z); 238 | //const vec3 normal = vec3( dot(pos, basisX), dot(pos, basisY), dot(pos, stretchedDir)); 239 | const vec3 normal = mul( pos, mat3(basisX, basisY, stretchedDir)); 240 | 241 | // Unstretch and normalize the sampled microfacet normal. 242 | const vec3 result = normalize( Stretch( normal, roughness ) ); 243 | 244 | //assert( isfinite( result.x ) && isfinite( result.y ) && isfinite( result.z ) ); 245 | 246 | return result; 247 | } 248 | //----end---- 249 | 250 | //Computing Adding double 251 | //Blcour_2018 (Efficient Rendering of Layered Materials using an Atomic Decomposition with Statistical Operators) 252 | void computeAddingDoubling(in float _cti, in vec3 m_etas[NB_LAYERS+1], in vec3 m_kappas[NB_LAYERS+1], in vec2 m_alphas[NB_LAYERS], in mat2 m_rotate[NB_LAYERS], 253 | out vec3 coeffs[NB_LAYERS], out mat2 variance_mat[NB_LAYERS]) 254 | { 255 | //variables 256 | float cti = _cti; 257 | vec3 R0i = vec3(0.0, 0.0, 0.0), Ri0 = vec3(0.0, 0.0, 0.0), T0i = vec3(1.0, 1.0, 1.0), Ti0 = vec3(1.0, 1.0, 1.0); 258 | float s_r0i = 0.0, s_ri0=0.0, s_t0i=0.0, s_ti0=0.0; 259 | mat2 s_r0i_ = 0.0, s_ri0_=0.0, s_t0i_=0.0, s_ti0_=0.0; 260 | float j0i=1.0, ji0=1.0; 261 | 262 | //Iterate over the layers 263 | for(int i=0; i((1.0f-alpha)*(sqrt(1.0f-alpha) + alpha), 0.0f, 1.0f); 284 | //stt = scale*stt + (1.0f-scale)*sti; 285 | ctt = sqrt(1.0f - stt*stt); 286 | } else { 287 | ctt = -1.0f; 288 | } 289 | 290 | /* Ray is not block by conducting interface or total reflection */ 291 | const bool has_transmissive = ctt > 0.0f && isZero(kappa); 292 | 293 | /* Evaluate interface variance term */ 294 | vec2 s_r12 = roughnessToVariance(alpha); 295 | s_r12_ = mul(mul(rotate, mat2(s_r12.x, 0.0,0.0, s_r12.y)) , transpose(rotate)); 296 | vec2 s_r21 = s_r12; 297 | s_r21_ = s_r12_; 298 | 299 | /* For dielectric interfaces, evaluate the transmissive roughnesses */ 300 | if(has_transmissive) { 301 | const float _ctt = 1.0; // The scaling factor overblurs the BSDF at grazing 302 | const float _cti = 1.0; // angles (we cannot account for the deformation of 303 | // the lobe for those configurations. 304 | 305 | vec2 s_t12 = roughnessToVariance(alpha * 0.5 * abs(_ctt*n12 - _cti)/(_ctt*n12)); 306 | s_t12_ = mul(mul(rotate, mat2(s_t12.x, 0.0,0.0, s_t12.y)), transpose(rotate)); 307 | vec2 s_t21 = roughnessToVariance(alpha * 0.5 * abs(_cti/n12 - _ctt)/(_cti/n12)); 308 | s_t21_ = mul(mul(rotate, mat2(s_t21.x, 0.0,0.0, s_t21.y)), transpose(rotate)); 309 | j12 = (ctt/cti) * n12; // Scale due to the interface 310 | j21 = (cti/ctt) / n12; 311 | } 312 | 313 | /* Evaluate r12, r21, t12, t21 */ 314 | evalFresnel(cti, eta, kappa, R12, T12); 315 | if(has_transmissive) { 316 | R21 = R12; 317 | T21 = T12 /* (n12*n12) */; // We don't need the IOR scaling since we are 318 | T12 = T12 /* (n12*n12) */; // computing reflectance only here. 319 | } else { 320 | R21 = 0.0; 321 | T21 = 0.0; 322 | T12 = 0.0; 323 | } 324 | 325 | /* Multiple scattering forms */ 326 | const vec3 denom = (1.0 - Ri0*R12); 327 | const vec3 m_R0i = (average(denom) <= 0.0)? 0.0 : (T0i*R12*Ti0) / denom; 328 | const vec3 m_Ri0 = (average(denom) <= 0.0)? 0.0 : (T21*Ri0*T12) / denom; 329 | const vec3 m_Rr = (average(denom) <= 0.0)? 0.0 : (Ri0*R12) / denom; 330 | 331 | /* Evaluate the adding operator on the energy */ 332 | const vec3 e_R0i = R0i + m_R0i; 333 | const vec3 e_T0i = (T0i*T12) / denom; 334 | const vec3 e_Ri0 = R21 + m_Ri0; 335 | const vec3 e_Ti0 = (T21*Ti0) / denom; 336 | 337 | /* Scalar forms for the spectral quantities */ 338 | 339 | const float r0i = average(R0i); 340 | const float e_r0i = average(e_R0i); 341 | const float e_ri0 = average(e_Ri0); 342 | const float m_r0i = average(m_R0i); 343 | const float m_ri0 = average(m_Ri0); 344 | const float m_rr = average(m_Rr); 345 | const float r21 = average(R21); 346 | 347 | /* Evaluate the adding operator on the normalized variance */ 348 | mat2 _s_r0i_ = (r0i*s_r0i_ + m_r0i*(s_ti0_ + j0i*(s_t0i_ + s_r12_ + m_rr*(s_r12_+s_ri0_)))) ;// e_r0i; 349 | mat2 _s_t0i_ = j12*s_t0i_ + s_t12_ + j12*(s_r12_ + s_ri0_)*m_rr; 350 | mat2 _s_ri0_ = (r21*s_r21_ + m_ri0*(s_t12_ + j12*(s_t21_ + s_ri0_ + m_rr*(s_r12_+s_ri0_)))) ;// e_ri0; 351 | mat2 _s_ti0_ = ji0*s_t21_ + s_ti0_ + ji0*(s_r12_ + s_ri0_)*m_rr; 352 | _s_r0i_ = (e_r0i > 0.0) ? _s_r0i_/e_r0i : 0.0; 353 | _s_ri0_ = (e_ri0 > 0.0) ? _s_ri0_/e_ri0 : 0.0; 354 | 355 | /* Store the coefficient and variance */ 356 | if(m_r0i > 0.0) { 357 | coeffs[i] = m_R0i; 358 | variance_mat[i] = s_ti0_ + j0i*(s_t0i_ + s_r12_ + m_rr*(s_r12_+s_ri0_)); 359 | } else { 360 | coeffs[i] = 0.0; 361 | variance_mat[i] = 0.0; 362 | } 363 | 364 | /* Update energy */ 365 | R0i = e_R0i; 366 | T0i = e_T0i; 367 | Ri0 = e_Ri0; 368 | Ti0 = e_Ti0; 369 | 370 | /* Update mean */ 371 | cti = ctt; 372 | 373 | /* Update variance */ 374 | s_r0i_ = _s_r0i_; 375 | s_t0i_ = _s_t0i_; 376 | s_ri0_ = _s_ri0_; 377 | s_ti0_ = _s_ti0_; 378 | 379 | /* Update jacobian */ 380 | j0i *= j12; 381 | ji0 *= j21; 382 | 383 | /* Escape if a conductor is present */ 384 | if(average(kappa) > 0.0) { 385 | return; 386 | } 387 | } 388 | } 389 | 390 | //Entry point 391 | //For env light 392 | void Anisotropic_layered_env( inout FragmentState s) 393 | { 394 | //material parrameters 395 | //1st:air, 2nd:dielectric, 3rd:conductor 396 | //or 397 | //1st:dielectric, 2nd:conductor 398 | vec3 m_etas[NB_LAYERS+1] = {vec3(1.0, 1.0, 1.0), uDielectric_eta, uConductor_eta}; 399 | vec3 m_kappas[NB_LAYERS+1] = {vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0), uConductor_kappa}; 400 | vec2 m_alphas[NB_LAYERS] = {uDielectric_alpha , uConductor_alpha}; 401 | float m_thetas[NB_LAYERS] = { 2.0*M_PI * (uDielectric_rotate/360.0), 2.0*M_PI * (uConductor_rotate/360.0) }; 402 | mat2 m_rotate[NB_LAYERS] = { mat2( cos(m_thetas[0]), -sin(m_thetas[0]), sin(m_thetas[0]), cos(m_thetas[0]) ), 403 | mat2( cos(m_thetas[1]), -sin(m_thetas[1]), sin(m_thetas[1]), cos(m_thetas[1]) ) }; 404 | //We use two kinds of coordinates. 405 | //The one is "Basis Coordinate" (basisX, basiY, basisZ) 406 | //The other is "Local Coordinate" (u, v, basisz) 407 | 408 | //Basis Cordinate 409 | vec3 basisX = s.vertexTangent; 410 | vec3 basisY = cross( basisX, s.normal ); 411 | vec3 basisZ = s.normal; 412 | vec3 E_base = normalize(vec3(dot(s.vertexEye, basisX), dot(s.vertexEye, basisY), dot(s.vertexEye, basisZ))); 413 | 414 | if(uMaterial_set == 0) 415 | { 416 | //Layered Material 417 | 418 | //evaluate the adding method to get coeffs and variances 419 | vec3 coeffs[NB_LAYERS] = {vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0)}; 420 | mat2 variance_mat_base[NB_LAYERS] = {mat2(0.0,0.0,0.0,0.0), mat2(0.0,0.0,0.0,0.0)}; 421 | 422 | computeAddingDoubling(E_base.z, m_etas, m_kappas, m_alphas, m_rotate, 423 | coeffs, variance_mat_base); 424 | 425 | vec2 eigenVec_max[NB_LAYERS]; 426 | vec2 eigenVec_min[NB_LAYERS]; 427 | vec2 alphas[NB_LAYERS]; 428 | vec3 us[NB_LAYERS]; 429 | vec3 vs[NB_LAYERS]; 430 | vec3 E_locals[NB_LAYERS]; 431 | //calculate eigen values and vecs 432 | for(int i=0; i