Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/gpu/shaders')
-rw-r--r--source/blender/gpu/shaders/gpu_shader_display_sh_frag.glsl58
-rw-r--r--source/blender/gpu/shaders/gpu_shader_display_sh_vert.glsl13
-rw-r--r--source/blender/gpu/shaders/gpu_shader_downsample_maxz_frag.glsl74
-rw-r--r--source/blender/gpu/shaders/gpu_shader_downsample_maxz_vert.glsl6
-rw-r--r--source/blender/gpu/shaders/gpu_shader_fx_colormanage_frag.glsl99
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material.glsl1241
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_ambient_occlusion.glsl160
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_anisotropic.glsl772
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_diffuse.glsl263
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_glass.glsl345
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_glossy.glsl710
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_refraction.glsl490
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_toon.glsl273
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_translucent.glsl55
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_transparent.glsl30
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_bsdf_velvet.glsl156
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_new_shading.glsl1207
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material_utils.glsl1113
-rw-r--r--source/blender/gpu/shaders/gpu_shader_probe_sh_compute_frag.glsl108
-rw-r--r--source/blender/gpu/shaders/gpu_shader_probe_sh_compute_vert.glsl5
20 files changed, 5949 insertions, 1229 deletions
diff --git a/source/blender/gpu/shaders/gpu_shader_display_sh_frag.glsl b/source/blender/gpu/shaders/gpu_shader_display_sh_frag.glsl
new file mode 100644
index 00000000000..c578e25e482
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_display_sh_frag.glsl
@@ -0,0 +1,58 @@
+uniform vec3 sh0;
+uniform vec3 sh1;
+uniform vec3 sh2;
+uniform vec3 sh3;
+uniform vec3 sh4;
+uniform vec3 sh5;
+uniform vec3 sh6;
+uniform vec3 sh7;
+uniform vec3 sh8;
+
+
+varying vec3 varposition;
+varying vec3 varnormal;
+
+float linearrgb_to_srgb(float c)
+{
+ if(c < 0.0031308)
+ return (c < 0.0) ? 0.0: c * 12.92;
+ else
+ return 1.055 * pow(c, 1.0/2.4) - 0.055;
+}
+
+void linearrgb_to_srgb(vec4 col_from, out vec4 col_to)
+{
+ col_to.r = linearrgb_to_srgb(col_from.r);
+ col_to.g = linearrgb_to_srgb(col_from.g);
+ col_to.b = linearrgb_to_srgb(col_from.b);
+ col_to.a = col_from.a;
+}
+
+void main()
+{
+ vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(varposition, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
+ vec4 co_homogenous = (gl_ProjectionMatrixInverse * v);
+
+ vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
+ vec3 worldvec = normalize( (gl_ModelViewMatrixInverse * co).xyz );
+
+ /* Second order Spherical Harmonics */
+ /* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */
+ vec3 sh = vec3(0.0);
+
+ sh += 0.282095 * sh0;
+
+ sh += -0.488603 * worldvec.z * sh1;
+ sh += 0.488603 * worldvec.y * sh2;
+ sh += -0.488603 * worldvec.x * sh3;
+
+ sh += 1.092548 * worldvec.x * worldvec.z * sh4;
+ sh += -1.092548 * worldvec.z * worldvec.y * sh5;
+ sh += 0.315392 * (3.0 * worldvec.y * worldvec.y - 1.0) * sh6;
+ sh += -1.092548 * worldvec.x * worldvec.y * sh7;
+ sh += 0.546274 * (worldvec.x * worldvec.x - worldvec.z * worldvec.z) * sh8;
+
+ vec4 shcolor = vec4(sh, 1.0);
+ linearrgb_to_srgb(shcolor, shcolor);
+ gl_FragColor = shcolor;
+} \ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_display_sh_vert.glsl b/source/blender/gpu/shaders/gpu_shader_display_sh_vert.glsl
new file mode 100644
index 00000000000..02f627f0f21
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_display_sh_vert.glsl
@@ -0,0 +1,13 @@
+
+varying vec3 varposition;
+varying vec3 varnormal;
+
+void main()
+{
+ /* position does not need to be transformed, we already have it */
+ gl_Position = gl_Vertex;
+
+ varposition = gl_Vertex.xyz;
+
+ varnormal = normalize(-varposition);
+} \ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_downsample_maxz_frag.glsl b/source/blender/gpu/shaders/gpu_shader_downsample_maxz_frag.glsl
new file mode 100644
index 00000000000..3279023670c
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_downsample_maxz_frag.glsl
@@ -0,0 +1,74 @@
+/* From http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/ */
+#extension GL_EXT_gpu_shader4 : enable
+
+uniform sampler2D lowermip;
+uniform ivec2 lowermipsize;
+
+float minmax(float a, float b, float c, float d)
+{
+#ifdef MIN
+ return min(min(a, b), min(c, d));
+#else
+ return max(max(a, b), max(c, d));
+#endif
+}
+
+float minmax(float a, float b, float c)
+{
+#ifdef MIN
+ return min(min(a, b), c);
+#else
+ return max(max(a, b), c);
+#endif
+}
+
+float minmax(float a, float b)
+{
+#ifdef MIN
+ return min(a, b);
+#else
+ return max(a, b);
+#endif
+}
+
+float texelFetchLowermip(ivec2 texelPos)
+{
+#if __VERSION__ < 130
+ return texture2DLod(lowermip, (texelPos * lowermipsize + 0.5) / lowermipsize, 0.0).r;
+#else
+ return texelFetch(lowermip, texelPos, 0).r;
+#endif
+}
+
+void main()
+{
+ vec4 texels;
+ ivec2 texelPos = ivec2(gl_FragCoord.xy) * 2;
+ texels.x = texelFetchLowermip(texelPos);
+ texels.y = texelFetchLowermip(texelPos + ivec2(1, 0));
+ texels.z = texelFetchLowermip(texelPos + ivec2(1, 1));
+ texels.w = texelFetchLowermip(texelPos + ivec2(0, 1));
+
+ float minmaxz = minmax(texels.x, texels.y, texels.z, texels.w);
+ vec3 extra;
+ /* if we are reducing an odd-width texture then fetch the edge texels */
+ if (((lowermipsize.x & 1) != 0) && (int(gl_FragCoord.x) == lowermipsize.x-3)) {
+ /* if both edges are odd, fetch the top-left corner texel */
+ if (((lowermipsize.y & 1) != 0) && (int(gl_FragCoord.y) == lowermipsize.y-3)) {
+ extra.z = texelFetchLowermip(texelPos + ivec2(-1, -1));
+ minmaxz = minmax(minmaxz, extra.z);
+ }
+ extra.x = texelFetchLowermip(texelPos + ivec2(0, -1));
+ extra.y = texelFetchLowermip(texelPos + ivec2(1, -1));
+ minmaxz = minmax(minmaxz, extra.x, extra.y);
+ }
+ /* if we are reducing an odd-height texture then fetch the edge texels */
+ else if (((lowermipsize.y & 1) != 0) && (int(gl_FragCoord.y) == lowermipsize.y-3)) {
+ extra.x = texelFetchLowermip(texelPos + ivec2(0, -1));
+ extra.y = texelFetchLowermip(texelPos + ivec2(1, -1));
+ minmaxz = minmax(minmaxz, extra.x, extra.y);
+ }
+
+ gl_FragDepth = minmaxz;
+ gl_FragColor = vec4(1.0);
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_downsample_maxz_vert.glsl b/source/blender/gpu/shaders/gpu_shader_downsample_maxz_vert.glsl
new file mode 100644
index 00000000000..203d5322cee
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_downsample_maxz_vert.glsl
@@ -0,0 +1,6 @@
+
+void main()
+{
+ gl_Position = gl_Vertex;
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_fx_colormanage_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_colormanage_frag.glsl
new file mode 100644
index 00000000000..0d06e808891
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_fx_colormanage_frag.glsl
@@ -0,0 +1,99 @@
+// color buffer
+uniform sampler2D colorbuffer;
+uniform sampler3D lut3d_texture;
+
+uniform float exposure;
+uniform float gamma;
+
+uniform float lut_size = 16.0;
+
+uniform float displayspace_is_srgb;
+uniform float is_offscreen;
+
+// coordinates on framebuffer in normalized (0.0-1.0) uv space
+varying vec4 uvcoordsvar;
+
+float srgb_to_linearrgb(float c)
+{
+ if(c < 0.04045)
+ return (c < 0.0) ? 0.0: c * (1.0 / 12.92);
+ else
+ return pow((c + 0.055)*(1.0/1.055), 2.4);
+}
+
+float linearrgb_to_srgb(float c)
+{
+ if(c < 0.0031308)
+ return (c < 0.0) ? 0.0: c * 12.92;
+ else
+ return 1.055 * pow(c, 1.0/2.4) - 0.055;
+}
+
+void srgb_to_linearrgb(vec4 col_from, out vec4 col_to)
+{
+ col_to.r = srgb_to_linearrgb(col_from.r);
+ col_to.g = srgb_to_linearrgb(col_from.g);
+ col_to.b = srgb_to_linearrgb(col_from.b);
+ col_to.a = col_from.a;
+}
+
+void linearrgb_to_srgb(vec4 col_from, out vec4 col_to)
+{
+ col_to.r = linearrgb_to_srgb(col_from.r);
+ col_to.g = linearrgb_to_srgb(col_from.g);
+ col_to.b = linearrgb_to_srgb(col_from.b);
+ col_to.a = col_from.a;
+}
+
+float computeLuminance(vec3 color)
+{
+ return max(dot(vec3(0.30, 0.59, 0.11), color), 1e-16);
+}
+
+vec3 computeExposedColor(vec3 color, float exposure)
+{
+ return exp2(exposure) * color;
+}
+
+// Reinhard operator
+vec3 tonemapReinhard(vec3 color, float saturation)
+{
+ float lum = computeLuminance(color);
+ float toneMappedLuminance = lum / (lum + 1.0);
+ return toneMappedLuminance * (color / lum);
+}
+
+vec3 applyGamma(vec3 color, float gamma)
+{
+ gamma = max(gamma, 1e-8);
+ return vec3( pow(color.r, 1/gamma), pow(color.g, 1/gamma), pow(color.b, 1/gamma) );
+}
+
+void main()
+{
+ //float depth = texture2D(depthbuffer, uvcoordsvar.xy).r;
+ vec4 scene_col = texture2D(colorbuffer, uvcoordsvar.xy);
+
+ if (is_offscreen == 1.0) {
+ // early out for Ofs. Color management is done later by OCIO
+ gl_FragColor = scene_col;
+ return;
+ }
+
+ if (displayspace_is_srgb == 1.0)
+ srgb_to_linearrgb(scene_col, scene_col);
+
+ /* Esposure */
+ scene_col = vec4( computeExposedColor(scene_col.rgb, exposure), scene_col.a);
+
+ /* LUT 3D in linear space */
+ /* TODO find a way to apply it in gamma space to gain accuracy in the black areas */
+ //linearrgb_to_srgb(scene_col, scene_col);
+ scene_col = vec4( texture3D(lut3d_texture, scene_col.rgb * ((lut_size + 1.0f) / lut_size) + (0.5f / lut_size) ).rgb, scene_col.a);
+ //srgb_to_linearrgb(scene_col, scene_col);
+
+ /* Gamma */
+ scene_col = vec4( applyGamma(scene_col.rgb, gamma), scene_col.a);
+
+ gl_FragColor = vec4(scene_col.rgb, scene_col.a);
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl
index 9914c4bb362..07810579b22 100644
--- a/source/blender/gpu/shaders/gpu_shader_material.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_material.glsl
@@ -9,6 +9,11 @@ float convert_rgba_to_float(vec4 color)
#endif
}
+void convert_vec3_to_vec4(vec3 invec, float val, out vec4 outvec)
+{
+ outvec = vec4(invec, val);
+}
+
float exp_blender(float f)
{
return pow(2.71828182846, f);
@@ -157,7 +162,13 @@ void color_to_blender_normal_new_shading(vec3 color, out vec3 normal)
}
#define M_PI 3.14159265358979323846
+#define M_PI_2 1.57079632679489661923
+#define M_2PI 6.28318530717958647
+#define M_PI2 9.86960440108935861
#define M_1_PI 0.31830988618379069
+#define M_1_PI2 0.10132118364233777
+#define M_2_PI 0.63661977236758134
+#define M_1_2PI 0.15915494309189533
/*********** SHADER NODES ***************/
@@ -267,7 +278,7 @@ void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist)
{
outdepth = abs(co.z);
outdist = length(co);
- outview = normalize(co);
+ outview = normalize(co*vec3(1.0,1.0,-1.0));
}
void lamp(
@@ -2366,1234 +2377,6 @@ void shade_alpha_obcolor(vec4 col, vec4 obcol, out vec4 outcol)
outcol = vec4(col.rgb, col.a * obcol.a);
}
-/*********** NEW SHADER UTILITIES **************/
-
-float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)
-{
- /* compute fresnel reflectance without explicitly computing
- * the refracted direction */
- float c = abs(dot(Incoming, Normal));
- float g = eta * eta - 1.0 + c * c;
- float result;
-
- if (g > 0.0) {
- g = sqrt(g);
- float A = (g - c) / (g + c);
- float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
- result = 0.5 * A * A * (1.0 + B * B);
- }
- else {
- result = 1.0; /* TIR (no refracted component) */
- }
-
- return result;
-}
-
-float hypot(float x, float y)
-{
- return sqrt(x * x + y * y);
-}
-
-void generated_from_orco(vec3 orco, out vec3 generated)
-{
- generated = orco * 0.5 + vec3(0.5);
-}
-
-int floor_to_int(float x)
-{
- return int(floor(x));
-}
-
-int quick_floor(float x)
-{
- return int(x) - ((x < 0) ? 1 : 0);
-}
-
-#ifdef BIT_OPERATIONS
-float integer_noise(int n)
-{
- int nn;
- n = (n + 1013) & 0x7fffffff;
- n = (n >> 13) ^ n;
- nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
- return 0.5 * (float(nn) / 1073741824.0);
-}
-
-uint hash(uint kx, uint ky, uint kz)
-{
-#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
-#define final(a, b, c) \
-{ \
- c ^= b; c -= rot(b, 14); \
- a ^= c; a -= rot(c, 11); \
- b ^= a; b -= rot(a, 25); \
- c ^= b; c -= rot(b, 16); \
- a ^= c; a -= rot(c, 4); \
- b ^= a; b -= rot(a, 14); \
- c ^= b; c -= rot(b, 24); \
-}
- // now hash the data!
- uint a, b, c, len = 3u;
- a = b = c = 0xdeadbeefu + (len << 2u) + 13u;
-
- c += kz;
- b += ky;
- a += kx;
- final (a, b, c);
-
- return c;
-#undef rot
-#undef final
-}
-
-uint hash(int kx, int ky, int kz)
-{
- return hash(uint(kx), uint(ky), uint(kz));
-}
-
-float bits_to_01(uint bits)
-{
- float x = float(bits) * (1.0 / float(0xffffffffu));
- return x;
-}
-
-float cellnoise(vec3 p)
-{
- int ix = quick_floor(p.x);
- int iy = quick_floor(p.y);
- int iz = quick_floor(p.z);
-
- return bits_to_01(hash(uint(ix), uint(iy), uint(iz)));
-}
-
-vec3 cellnoise_color(vec3 p)
-{
- float r = cellnoise(p);
- float g = cellnoise(vec3(p.y, p.x, p.z));
- float b = cellnoise(vec3(p.y, p.z, p.x));
-
- return vec3(r, g, b);
-}
-#endif // BIT_OPERATIONS
-
-float floorfrac(float x, out int i)
-{
- i = floor_to_int(x);
- return x - i;
-}
-
-/*********** NEW SHADER NODES ***************/
-
-#define NUM_LIGHTS 3
-
-/* bsdfs */
-
-void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out vec4 result)
-{
- /* ambient light */
- vec3 L = vec3(0.2);
-
- /* directional lights */
- for (int i = 0; i < NUM_LIGHTS; i++) {
- vec3 light_position = gl_LightSource[i].position.xyz;
- vec3 light_diffuse = gl_LightSource[i].diffuse.rgb;
-
- float bsdf = max(dot(N, light_position), 0.0);
- L += light_diffuse * bsdf;
- }
-
- result = vec4(L * color.rgb, 1.0);
-}
-
-void node_bsdf_glossy(vec4 color, float roughness, vec3 N, out vec4 result)
-{
- /* ambient light */
- vec3 L = vec3(0.2);
-
- /* directional lights */
- for (int i = 0; i < NUM_LIGHTS; i++) {
- vec3 light_position = gl_LightSource[i].position.xyz;
- vec3 H = gl_LightSource[i].halfVector.xyz;
- vec3 light_diffuse = gl_LightSource[i].diffuse.rgb;
- vec3 light_specular = gl_LightSource[i].specular.rgb;
-
- /* we mix in some diffuse so low roughness still shows up */
- float bsdf = 0.5 * pow(max(dot(N, H), 0.0), 1.0 / roughness);
- bsdf += 0.5 * max(dot(N, light_position), 0.0);
- L += light_specular * bsdf;
- }
-
- result = vec4(L * color.rgb, 1.0);
-}
-
-void node_bsdf_anisotropic(
- vec4 color, float roughness, float anisotropy, float rotation, vec3 N, vec3 T,
- out vec4 result)
-{
- node_bsdf_diffuse(color, 0.0, N, result);
-}
-
-void node_bsdf_glass(vec4 color, float roughness, float ior, vec3 N, out vec4 result)
-{
- node_bsdf_diffuse(color, 0.0, N, result);
-}
-
-void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out vec4 result)
-{
- node_bsdf_diffuse(color, 0.0, N, result);
-}
-
-void node_bsdf_translucent(vec4 color, vec3 N, out vec4 result)
-{
- node_bsdf_diffuse(color, 0.0, N, result);
-}
-
-void node_bsdf_transparent(vec4 color, out vec4 result)
-{
- /* this isn't right */
- result.r = color.r;
- result.g = color.g;
- result.b = color.b;
- result.a = 0.0;
-}
-
-void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out vec4 result)
-{
- node_bsdf_diffuse(color, 0.0, N, result);
-}
-
-void node_subsurface_scattering(
- vec4 color, float scale, vec3 radius, float sharpen, float texture_blur, vec3 N,
- out vec4 result)
-{
- node_bsdf_diffuse(color, 0.0, N, result);
-}
-
-void node_bsdf_hair(vec4 color, float offset, float roughnessu, float roughnessv, vec3 tangent, out vec4 result)
-{
- result = color;
-}
-
-void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out vec4 result)
-{
- node_bsdf_diffuse(color, 0.0, N, result);
-}
-
-void node_ambient_occlusion(vec4 color, out vec4 result)
-{
- result = color;
-}
-
-/* emission */
-
-void node_emission(vec4 color, float strength, vec3 N, out vec4 result)
-{
- result = color * strength;
-}
-
-/* background */
-
-void background_transform_to_world(vec3 viewvec, out vec3 worldvec)
-{
- vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogenous = (gl_ProjectionMatrixInverse * v);
-
- vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
- worldvec = (gl_ModelViewMatrixInverse * co).xyz;
-}
-
-void node_background(vec4 color, float strength, vec3 N, out vec4 result)
-{
- result = color * strength;
-}
-
-/* closures */
-
-void node_mix_shader(float fac, vec4 shader1, vec4 shader2, out vec4 shader)
-{
- shader = mix(shader1, shader2, fac);
-}
-
-void node_add_shader(vec4 shader1, vec4 shader2, out vec4 shader)
-{
- shader = shader1 + shader2;
-}
-
-/* fresnel */
-
-void node_fresnel(float ior, vec3 N, vec3 I, out float result)
-{
- /* handle perspective/orthographic */
- vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
-
- float eta = max(ior, 0.00001);
- result = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? eta : 1.0 / eta);
-}
-
-/* layer_weight */
-
-void node_layer_weight(float blend, vec3 N, vec3 I, out float fresnel, out float facing)
-{
- /* fresnel */
- float eta = max(1.0 - blend, 0.00001);
- vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
-
- fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? 1.0 / eta : eta);
-
- /* facing */
- facing = abs(dot(I_view, N));
- if (blend != 0.5) {
- blend = clamp(blend, 0.0, 0.99999);
- blend = (blend < 0.5) ? 2.0 * blend : 0.5 / (1.0 - blend);
- facing = pow(facing, blend);
- }
- facing = 1.0 - facing;
-}
-
-/* gamma */
-
-void node_gamma(vec4 col, float gamma, out vec4 outcol)
-{
- outcol = col;
-
- if (col.r > 0.0)
- outcol.r = compatible_pow(col.r, gamma);
- if (col.g > 0.0)
- outcol.g = compatible_pow(col.g, gamma);
- if (col.b > 0.0)
- outcol.b = compatible_pow(col.b, gamma);
-}
-
-/* geometry */
-
-void node_attribute(vec3 attr, out vec4 outcol, out vec3 outvec, out float outf)
-{
- outcol = vec4(attr, 1.0);
- outvec = attr;
- outf = (attr.x + attr.y + attr.z) / 3.0;
-}
-
-void node_uvmap(vec3 attr_uv, out vec3 outvec)
-{
- outvec = attr_uv;
-}
-
-void node_geometry(
- vec3 I, vec3 N, mat4 toworld,
- out vec3 position, out vec3 normal, out vec3 tangent,
- out vec3 true_normal, out vec3 incoming, out vec3 parametric,
- out float backfacing, out float pointiness)
-{
- position = (toworld * vec4(I, 1.0)).xyz;
- normal = (toworld * vec4(N, 0.0)).xyz;
- tangent = vec3(0.0);
- true_normal = normal;
-
- /* handle perspective/orthographic */
- vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
- incoming = -(toworld * vec4(I_view, 0.0)).xyz;
-
- parametric = vec3(0.0);
- backfacing = (gl_FrontFacing) ? 0.0 : 1.0;
- pointiness = 0.0;
-}
-
-void node_tex_coord(
- vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac,
- vec3 attr_orco, vec3 attr_uv,
- out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object,
- out vec3 camera, out vec3 window, out vec3 reflection)
-{
- generated = attr_orco * 0.5 + vec3(0.5);
- normal = normalize((obinvmat * (viewinvmat * vec4(N, 0.0))).xyz);
- uv = attr_uv;
- object = (obinvmat * (viewinvmat * vec4(I, 1.0))).xyz;
- camera = vec3(I.xy, -I.z);
- vec4 projvec = gl_ProjectionMatrix * vec4(I, 1.0);
- window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0);
-
- vec3 shade_I;
- shade_view(I, shade_I);
- vec3 view_reflection = reflect(shade_I, normalize(N));
- reflection = (viewinvmat * vec4(view_reflection, 0.0)).xyz;
-}
-
-void node_tex_coord_background(
- vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac,
- vec3 attr_orco, vec3 attr_uv,
- out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object,
- out vec3 camera, out vec3 window, out vec3 reflection)
-{
- vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogenous = (gl_ProjectionMatrixInverse * v);
-
- vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
-
- co = normalize(co);
- vec3 coords = (gl_ModelViewMatrixInverse * co).xyz;
-
- generated = coords;
- normal = -coords;
- uv = vec3(attr_uv.xy, 0.0);
- object = coords;
-
- camera = vec3(co.xy, -co.z);
- window = (gl_ProjectionMatrix[3][3] == 0.0) ?
- vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0) :
- vec3(vec2(0.5) * camerafac.xy + camerafac.zw, 0.0);
-
- reflection = -coords;
-}
-
-/* textures */
-
-float calc_gradient(vec3 p, int gradient_type)
-{
- float x, y, z;
- x = p.x;
- y = p.y;
- z = p.z;
- if (gradient_type == 0) { /* linear */
- return x;
- }
- else if (gradient_type == 1) { /* quadratic */
- float r = max(x, 0.0);
- return r * r;
- }
- else if (gradient_type == 2) { /* easing */
- float r = min(max(x, 0.0), 1.0);
- float t = r * r;
- return (3.0 * t - 2.0 * t * r);
- }
- else if (gradient_type == 3) { /* diagonal */
- return (x + y) * 0.5;
- }
- else if (gradient_type == 4) { /* radial */
- return atan(y, x) / (M_PI * 2) + 0.5;
- }
- else {
- float r = max(1.0 - sqrt(x * x + y * y + z * z), 0.0);
- if (gradient_type == 5) { /* quadratic sphere */
- return r * r;
- }
- else if (gradient_type == 6) { /* sphere */
- return r;
- }
- }
- return 0.0;
-}
-
-void node_tex_gradient(vec3 co, float gradient_type, out vec4 color, out float fac)
-{
- float f = calc_gradient(co, int(gradient_type));
- f = clamp(f, 0.0, 1.0);
-
- color = vec4(f, f, f, 1.0);
- fac = f;
-}
-
-void node_tex_checker(vec3 co, vec4 color1, vec4 color2, float scale, out vec4 color, out float fac)
-{
- vec3 p = co * scale;
-
- /* Prevent precision issues on unit coordinates. */
- p.x = (p.x + 0.000001) * 0.999999;
- p.y = (p.y + 0.000001) * 0.999999;
- p.z = (p.z + 0.000001) * 0.999999;
-
- int xi = int(abs(floor(p.x)));
- int yi = int(abs(floor(p.y)));
- int zi = int(abs(floor(p.z)));
-
- bool check = ((mod(xi, 2) == mod(yi, 2)) == bool(mod(zi, 2)));
-
- color = check ? color1 : color2;
- fac = check ? 1.0 : 0.0;
-}
-
-#ifdef BIT_OPERATIONS
-vec2 calc_brick_texture(vec3 p, float mortar_size, float bias,
- float brick_width, float row_height,
- float offset_amount, int offset_frequency,
- float squash_amount, int squash_frequency)
-{
- int bricknum, rownum;
- float offset = 0.0;
- float x, y;
-
- rownum = floor_to_int(p.y / row_height);
-
- if (offset_frequency != 0 && squash_frequency != 0) {
- brick_width *= (rownum % squash_frequency != 0) ? 1.0 : squash_amount; /* squash */
- offset = (rownum % offset_frequency != 0) ? 0.0 : (brick_width * offset_amount); /* offset */
- }
-
- bricknum = floor_to_int((p.x + offset) / brick_width);
-
- x = (p.x + offset) - brick_width * bricknum;
- y = p.y - row_height * rownum;
-
- return vec2(clamp((integer_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias), 0.0, 1.0),
- (x < mortar_size || y < mortar_size ||
- x > (brick_width - mortar_size) ||
- y > (row_height - mortar_size)) ? 1.0 : 0.0);
-}
-#endif
-
-void node_tex_brick(vec3 co,
- vec4 color1, vec4 color2,
- vec4 mortar, float scale,
- float mortar_size, float bias,
- float brick_width, float row_height,
- float offset_amount, float offset_frequency,
- float squash_amount, float squash_frequency,
- out vec4 color, out float fac)
-{
-#ifdef BIT_OPERATIONS
- vec2 f2 = calc_brick_texture(co * scale,
- mortar_size, bias,
- brick_width, row_height,
- offset_amount, int(offset_frequency),
- squash_amount, int(squash_frequency));
- float tint = f2.x;
- float f = f2.y;
- if (f != 1.0) {
- float facm = 1.0 - tint;
- color1 = facm * color1 + tint * color2;
- }
- color = (f == 1.0) ? mortar : color1;
- fac = f;
-#else
- color = vec4(1.0);
- fac = 1.0;
-#endif
-}
-
-void node_tex_clouds(vec3 co, float size, out vec4 color, out float fac)
-{
- color = vec4(1.0);
- fac = 1.0;
-}
-
-void node_tex_environment_equirectangular(vec3 co, sampler2D ima, out vec4 color)
-{
- vec3 nco = normalize(co);
- float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5;
- float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5;
-
- color = texture2D(ima, vec2(u, v));
-}
-
-void node_tex_environment_mirror_ball(vec3 co, sampler2D ima, out vec4 color)
-{
- vec3 nco = normalize(co);
-
- nco.y -= 1.0;
-
- float div = 2.0 * sqrt(max(-0.5 * nco.y, 0.0));
- if (div > 0.0)
- nco /= div;
-
- float u = 0.5 * (nco.x + 1.0);
- float v = 0.5 * (nco.z + 1.0);
-
- color = texture2D(ima, vec2(u, v));
-}
-
-void node_tex_environment_empty(vec3 co, out vec4 color)
-{
- color = vec4(1.0, 0.0, 1.0, 1.0);
-}
-
-void node_tex_image(vec3 co, sampler2D ima, out vec4 color, out float alpha)
-{
- color = texture2D(ima, co.xy);
- alpha = color.a;
-}
-
-void node_tex_image_box(vec3 texco,
- vec3 nob,
- sampler2D ima,
- float blend,
- out vec4 color,
- out float alpha)
-{
- /* project from direction vector to barycentric coordinates in triangles */
- nob = vec3(abs(nob.x), abs(nob.y), abs(nob.z));
- nob /= (nob.x + nob.y + nob.z);
-
- /* basic idea is to think of this as a triangle, each corner representing
- * one of the 3 faces of the cube. in the corners we have single textures,
- * in between we blend between two textures, and in the middle we a blend
- * between three textures.
- *
- * the Nxyz values are the barycentric coordinates in an equilateral
- * triangle, which in case of blending, in the middle has a smaller
- * equilateral triangle where 3 textures blend. this divides things into
- * 7 zones, with an if () test for each zone */
-
- vec3 weight = vec3(0.0, 0.0, 0.0);
- float limit = 0.5 * (1.0 + blend);
-
- /* first test for corners with single texture */
- if (nob.x > limit * (nob.x + nob.y) && nob.x > limit * (nob.x + nob.z)) {
- weight.x = 1.0;
- }
- else if (nob.y > limit * (nob.x + nob.y) && nob.y > limit * (nob.y + nob.z)) {
- weight.y = 1.0;
- }
- else if (nob.z > limit * (nob.x + nob.z) && nob.z > limit * (nob.y + nob.z)) {
- weight.z = 1.0;
- }
- else if (blend > 0.0) {
- /* in case of blending, test for mixes between two textures */
- if (nob.z < (1.0 - limit) * (nob.y + nob.x)) {
- weight.x = nob.x / (nob.x + nob.y);
- weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
- weight.y = 1.0 - weight.x;
- }
- else if (nob.x < (1.0 - limit) * (nob.y + nob.z)) {
- weight.y = nob.y / (nob.y + nob.z);
- weight.y = clamp((weight.y - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
- weight.z = 1.0 - weight.y;
- }
- else if (nob.y < (1.0 - limit) * (nob.x + nob.z)) {
- weight.x = nob.x / (nob.x + nob.z);
- weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
- weight.z = 1.0 - weight.x;
- }
- else {
- /* last case, we have a mix between three */
- weight.x = ((2.0 - limit) * nob.x + (limit - 1.0)) / (2.0 * limit - 1.0);
- weight.y = ((2.0 - limit) * nob.y + (limit - 1.0)) / (2.0 * limit - 1.0);
- weight.z = ((2.0 - limit) * nob.z + (limit - 1.0)) / (2.0 * limit - 1.0);
- }
- }
- else {
- /* Desperate mode, no valid choice anyway, fallback to one side.*/
- weight.x = 1.0;
- }
- color = vec4(0);
- if (weight.x > 0.0) {
- color += weight.x * texture2D(ima, texco.yz);
- }
- if (weight.y > 0.0) {
- color += weight.y * texture2D(ima, texco.xz);
- }
- if (weight.z > 0.0) {
- color += weight.z * texture2D(ima, texco.yx);
- }
-
- alpha = color.a;
-}
-
-void node_tex_image_empty(vec3 co, out vec4 color, out float alpha)
-{
- color = vec4(0.0);
- alpha = 0.0;
-}
-
-void node_tex_magic(vec3 co, float scale, float distortion, float depth, out vec4 color, out float fac)
-{
- vec3 p = co * scale;
- float x = sin((p.x + p.y + p.z) * 5.0);
- float y = cos((-p.x + p.y - p.z) * 5.0);
- float z = -cos((-p.x - p.y + p.z) * 5.0);
-
- if (depth > 0) {
- x *= distortion;
- y *= distortion;
- z *= distortion;
- y = -cos(x - y + z);
- y *= distortion;
- if (depth > 1) {
- x = cos(x - y - z);
- x *= distortion;
- if (depth > 2) {
- z = sin(-x - y - z);
- z *= distortion;
- if (depth > 3) {
- x = -cos(-x + y - z);
- x *= distortion;
- if (depth > 4) {
- y = -sin(-x + y + z);
- y *= distortion;
- if (depth > 5) {
- y = -cos(-x + y + z);
- y *= distortion;
- if (depth > 6) {
- x = cos(x + y + z);
- x *= distortion;
- if (depth > 7) {
- z = sin(x + y - z);
- z *= distortion;
- if (depth > 8) {
- x = -cos(-x - y + z);
- x *= distortion;
- if (depth > 9) {
- y = -sin(x - y + z);
- y *= distortion;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- if (distortion != 0.0) {
- distortion *= 2.0;
- x /= distortion;
- y /= distortion;
- z /= distortion;
- }
-
- color = vec4(0.5 - x, 0.5 - y, 0.5 - z, 1.0);
- fac = (color.x + color.y + color.z) / 3.0;
-}
-
-#ifdef BIT_OPERATIONS
-float noise_fade(float t)
-{
- return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
-}
-
-float noise_scale3(float result)
-{
- return 0.9820 * result;
-}
-
-float noise_nerp(float t, float a, float b)
-{
- return (1.0 - t) * a + t * b;
-}
-
-float noise_grad(uint hash, float x, float y, float z)
-{
- uint h = hash & 15u;
- float u = h < 8u ? x : y;
- float vt = ((h == 12u) || (h == 14u)) ? x : z;
- float v = h < 4u ? y : vt;
- return (((h & 1u) != 0u) ? -u : u) + (((h & 2u) != 0u) ? -v : v);
-}
-
-float noise_perlin(float x, float y, float z)
-{
- int X; float fx = floorfrac(x, X);
- int Y; float fy = floorfrac(y, Y);
- int Z; float fz = floorfrac(z, Z);
-
- float u = noise_fade(fx);
- float v = noise_fade(fy);
- float w = noise_fade(fz);
-
- float result;
-
- result = noise_nerp(w, noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z), fx, fy, fz),
- noise_grad(hash(X + 1, Y, Z), fx - 1.0, fy, fz)),
- noise_nerp(u, noise_grad(hash(X, Y + 1, Z), fx, fy - 1.0, fz),
- noise_grad(hash(X + 1, Y + 1, Z), fx - 1.0, fy - 1.0, fz))),
- noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z + 1), fx, fy, fz - 1.0),
- noise_grad(hash(X + 1, Y, Z + 1), fx - 1.0, fy, fz - 1.0)),
- noise_nerp(u, noise_grad(hash(X, Y + 1, Z + 1), fx, fy - 1.0, fz - 1.0),
- noise_grad(hash(X + 1, Y + 1, Z + 1), fx - 1.0, fy - 1.0, fz - 1.0))));
- return noise_scale3(result);
-}
-
-float noise(vec3 p)
-{
- return 0.5 * noise_perlin(p.x, p.y, p.z) + 0.5;
-}
-
-float snoise(vec3 p)
-{
- return noise_perlin(p.x, p.y, p.z);
-}
-
-float noise_turbulence(vec3 p, float octaves, int hard)
-{
- float fscale = 1.0;
- float amp = 1.0;
- float sum = 0.0;
- int i, n;
- octaves = clamp(octaves, 0.0, 16.0);
- n = int(octaves);
- for (i = 0; i <= n; i++) {
- float t = noise(fscale * p);
- if (hard != 0) {
- t = abs(2.0 * t - 1.0);
- }
- sum += t * amp;
- amp *= 0.5;
- fscale *= 2.0;
- }
- float rmd = octaves - floor(octaves);
- if (rmd != 0.0) {
- float t = noise(fscale * p);
- if (hard != 0) {
- t = abs(2.0 * t - 1.0);
- }
- float sum2 = sum + t * amp;
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1));
- return (1.0 - rmd) * sum + rmd * sum2;
- }
- else {
- sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
- return sum;
- }
-}
-#endif // BIT_OPERATIONS
-
-void node_tex_noise(vec3 co, float scale, float detail, float distortion, out vec4 color, out float fac)
-{
-#ifdef BIT_OPERATIONS
- vec3 p = co * scale;
- int hard = 0;
- if (distortion != 0.0) {
- vec3 r, offset = vec3(13.5, 13.5, 13.5);
- r.x = noise(p + offset) * distortion;
- r.y = noise(p) * distortion;
- r.z = noise(p - offset) * distortion;
- p += r;
- }
-
- fac = noise_turbulence(p, detail, hard);
- color = vec4(fac,
- noise_turbulence(vec3(p.y, p.x, p.z), detail, hard),
- noise_turbulence(vec3(p.y, p.z, p.x), detail, hard),
- 1);
-#else // BIT_OPERATIONS
- color = vec4(1.0);
- fac = 1.0;
-#endif // BIT_OPERATIONS
-}
-
-
-#ifdef BIT_OPERATIONS
-
-/* Musgrave fBm
- *
- * H: fractal increment parameter
- * lacunarity: gap between successive frequencies
- * octaves: number of frequencies in the fBm
- *
- * from "Texturing and Modelling: A procedural approach"
- */
-
-float noise_musgrave_fBm(vec3 p, float H, float lacunarity, float octaves)
-{
- float rmd;
- float value = 0.0;
- float pwr = 1.0;
- float pwHL = pow(lacunarity, -H);
- int i;
-
- for (i = 0; i < int(octaves); i++) {
- value += snoise(p) * pwr;
- pwr *= pwHL;
- p *= lacunarity;
- }
-
- rmd = octaves - floor(octaves);
- if (rmd != 0.0)
- value += rmd * snoise(p) * pwr;
-
- return value;
-}
-
-/* Musgrave Multifractal
- *
- * H: highest fractal dimension
- * lacunarity: gap between successive frequencies
- * octaves: number of frequencies in the fBm
- */
-
-float noise_musgrave_multi_fractal(vec3 p, float H, float lacunarity, float octaves)
-{
- float rmd;
- float value = 1.0;
- float pwr = 1.0;
- float pwHL = pow(lacunarity, -H);
- int i;
-
- for (i = 0; i < int(octaves); i++) {
- value *= (pwr * snoise(p) + 1.0);
- pwr *= pwHL;
- p *= lacunarity;
- }
-
- rmd = octaves - floor(octaves);
- if (rmd != 0.0)
- value *= (rmd * pwr * snoise(p) + 1.0); /* correct? */
-
- return value;
-}
-
-/* Musgrave Heterogeneous Terrain
- *
- * H: fractal dimension of the roughest area
- * lacunarity: gap between successive frequencies
- * octaves: number of frequencies in the fBm
- * offset: raises the terrain from `sea level'
- */
-
-float noise_musgrave_hetero_terrain(vec3 p, float H, float lacunarity, float octaves, float offset)
-{
- float value, increment, rmd;
- float pwHL = pow(lacunarity, -H);
- float pwr = pwHL;
- int i;
-
- /* first unscaled octave of function; later octaves are scaled */
- value = offset + snoise(p);
- p *= lacunarity;
-
- for (i = 1; i < int(octaves); i++) {
- increment = (snoise(p) + offset) * pwr * value;
- value += increment;
- pwr *= pwHL;
- p *= lacunarity;
- }
-
- rmd = octaves - floor(octaves);
- if (rmd != 0.0) {
- increment = (snoise(p) + offset) * pwr * value;
- value += rmd * increment;
- }
-
- return value;
-}
-
-/* Hybrid Additive/Multiplicative Multifractal Terrain
- *
- * H: fractal dimension of the roughest area
- * lacunarity: gap between successive frequencies
- * octaves: number of frequencies in the fBm
- * offset: raises the terrain from `sea level'
- */
-
-float noise_musgrave_hybrid_multi_fractal(vec3 p, float H, float lacunarity, float octaves, float offset, float gain)
-{
- float result, signal, weight, rmd;
- float pwHL = pow(lacunarity, -H);
- float pwr = pwHL;
- int i;
-
- result = snoise(p) + offset;
- weight = gain * result;
- p *= lacunarity;
-
- for (i = 1; (weight > 0.001f) && (i < int(octaves)); i++) {
- if (weight > 1.0)
- weight = 1.0;
-
- signal = (snoise(p) + offset) * pwr;
- pwr *= pwHL;
- result += weight * signal;
- weight *= gain * signal;
- p *= lacunarity;
- }
-
- rmd = octaves - floor(octaves);
- if (rmd != 0.0)
- result += rmd * ((snoise(p) + offset) * pwr);
-
- return result;
-}
-
-/* Ridged Multifractal Terrain
- *
- * H: fractal dimension of the roughest area
- * lacunarity: gap between successive frequencies
- * octaves: number of frequencies in the fBm
- * offset: raises the terrain from `sea level'
- */
-
-float noise_musgrave_ridged_multi_fractal(vec3 p, float H, float lacunarity, float octaves, float offset, float gain)
-{
- float result, signal, weight;
- float pwHL = pow(lacunarity, -H);
- float pwr = pwHL;
- int i;
-
- signal = offset - abs(snoise(p));
- signal *= signal;
- result = signal;
- weight = 1.0;
-
- for (i = 1; i < int(octaves); i++) {
- p *= lacunarity;
- weight = clamp(signal * gain, 0.0, 1.0);
- signal = offset - abs(snoise(p));
- signal *= signal;
- signal *= weight;
- result += signal * pwr;
- pwr *= pwHL;
- }
-
- return result;
-}
-
-float svm_musgrave(int type,
- float dimension,
- float lacunarity,
- float octaves,
- float offset,
- float intensity,
- float gain,
- vec3 p)
-{
- if (type == 0 /*NODE_MUSGRAVE_MULTIFRACTAL*/)
- return intensity * noise_musgrave_multi_fractal(p, dimension, lacunarity, octaves);
- else if (type == 1 /*NODE_MUSGRAVE_FBM*/)
- return intensity * noise_musgrave_fBm(p, dimension, lacunarity, octaves);
- else if (type == 2 /*NODE_MUSGRAVE_HYBRID_MULTIFRACTAL*/)
- return intensity * noise_musgrave_hybrid_multi_fractal(p, dimension, lacunarity, octaves, offset, gain);
- else if (type == 3 /*NODE_MUSGRAVE_RIDGED_MULTIFRACTAL*/)
- return intensity * noise_musgrave_ridged_multi_fractal(p, dimension, lacunarity, octaves, offset, gain);
- else if (type == 4 /*NODE_MUSGRAVE_HETERO_TERRAIN*/)
- return intensity * noise_musgrave_hetero_terrain(p, dimension, lacunarity, octaves, offset);
- return 0.0;
-}
-#endif // #ifdef BIT_OPERATIONS
-
-void node_tex_musgrave(vec3 co,
- float scale,
- float detail,
- float dimension,
- float lacunarity,
- float offset,
- float gain,
- float type,
- out vec4 color,
- out float fac)
-{
-#ifdef BIT_OPERATIONS
- fac = svm_musgrave(int(type),
- dimension,
- lacunarity,
- detail,
- offset,
- 1.0,
- gain,
- co * scale);
-#else
- fac = 1.0;
-#endif
-
- color = vec4(fac, fac, fac, 1.0);
-}
-
-void node_tex_sky(vec3 co, out vec4 color)
-{
- color = vec4(1.0);
-}
-
-void node_tex_voronoi(vec3 co, float scale, float coloring, out vec4 color, out float fac)
-{
-#ifdef BIT_OPERATIONS
- vec3 p = co * scale;
- int xx, yy, zz, xi, yi, zi;
- float da[4];
- vec3 pa[4];
-
- xi = floor_to_int(p[0]);
- yi = floor_to_int(p[1]);
- zi = floor_to_int(p[2]);
-
- da[0] = 1e+10;
- da[1] = 1e+10;
- da[2] = 1e+10;
- da[3] = 1e+10;
-
- for (xx = xi - 1; xx <= xi + 1; xx++) {
- for (yy = yi - 1; yy <= yi + 1; yy++) {
- for (zz = zi - 1; zz <= zi + 1; zz++) {
- vec3 ip = vec3(xx, yy, zz);
- vec3 vp = cellnoise_color(ip);
- vec3 pd = p - (vp + ip);
- float d = dot(pd, pd);
- vp += vec3(xx, yy, zz);
- if (d < da[0]) {
- da[3] = da[2];
- da[2] = da[1];
- da[1] = da[0];
- da[0] = d;
- pa[3] = pa[2];
- pa[2] = pa[1];
- pa[1] = pa[0];
- pa[0] = vp;
- }
- else if (d < da[1]) {
- da[3] = da[2];
- da[2] = da[1];
- da[1] = d;
-
- pa[3] = pa[2];
- pa[2] = pa[1];
- pa[1] = vp;
- }
- else if (d < da[2]) {
- da[3] = da[2];
- da[2] = d;
-
- pa[3] = pa[2];
- pa[2] = vp;
- }
- else if (d < da[3]) {
- da[3] = d;
- pa[3] = vp;
- }
- }
- }
- }
-
- if (coloring == 0.0) {
- fac = abs(da[0]);
- color = vec4(fac, fac, fac, 1);
- }
- else {
- color = vec4(cellnoise_color(pa[0]), 1);
- fac = (color.x + color.y + color.z) * (1.0 / 3.0);
- }
-#else // BIT_OPERATIONS
- color = vec4(1.0);
- fac = 1.0;
-#endif // BIT_OPERATIONS
-}
-
-#ifdef BIT_OPERATIONS
-float calc_wave(vec3 p, float distortion, float detail, float detail_scale, int wave_type, int wave_profile)
-{
- float n;
-
- if (wave_type == 0) /* type bands */
- n = (p.x + p.y + p.z) * 10.0;
- else /* type rings */
- n = length(p) * 20.0;
-
- if (distortion != 0.0)
- n += distortion * noise_turbulence(p * detail_scale, detail, 0);
-
- if (wave_profile == 0) { /* profile sin */
- return 0.5 + 0.5 * sin(n);
- }
- else { /* profile saw */
- n /= 2.0 * M_PI;
- n -= int(n);
- return (n < 0.0) ? n + 1.0 : n;
- }
-}
-#endif // BIT_OPERATIONS
-
-void node_tex_wave(
- vec3 co, float scale, float distortion, float detail, float detail_scale, float wave_type, float wave_profile,
- out vec4 color, out float fac)
-{
-#ifdef BIT_OPERATIONS
- float f;
- f = calc_wave(co * scale, distortion, detail, detail_scale, int(wave_type), int(wave_profile));
-
- color = vec4(f, f, f, 1.0);
- fac = f;
-#else // BIT_OPERATIONS
- color = vec4(1.0);
- fac = 1;
-#endif // BIT_OPERATIONS
-}
-
-/* light path */
-
-void node_light_path(
- out float is_camera_ray,
- out float is_shadow_ray,
- out float is_diffuse_ray,
- out float is_glossy_ray,
- out float is_singular_ray,
- out float is_reflection_ray,
- out float is_transmission_ray,
- out float ray_length,
- out float ray_depth,
- out float transparent_depth,
- out float transmission_depth)
-{
- is_camera_ray = 1.0;
- is_shadow_ray = 0.0;
- is_diffuse_ray = 0.0;
- is_glossy_ray = 0.0;
- is_singular_ray = 0.0;
- is_reflection_ray = 0.0;
- is_transmission_ray = 0.0;
- ray_length = 1.0;
- ray_depth = 1.0;
- transparent_depth = 1.0;
- transmission_depth = 1.0;
-}
-
-void node_light_falloff(float strength, float tsmooth, out float quadratic, out float linear, out float constant)
-{
- quadratic = strength;
- linear = strength;
- constant = strength;
-}
-
-void node_object_info(out vec3 location, out float object_index, out float material_index, out float random)
-{
- location = vec3(0.0);
- object_index = 0.0;
- material_index = 0.0;
- random = 0.0;
-}
-
-void node_normal_map(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal)
-{
- vec3 B = tangent.w * cross(normal, tangent.xyz);
-
- outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal;
- outnormal = normalize(outnormal);
-}
-
-void node_bump(float strength, float dist, float height, vec3 N, vec3 surf_pos, float invert, out vec3 result)
-{
- if (invert != 0.0) {
- dist *= -1.0;
- }
- vec3 dPdx = dFdx(surf_pos);
- vec3 dPdy = dFdy(surf_pos);
-
- /* Get surface tangents from normal. */
- vec3 Rx = cross(dPdy, N);
- vec3 Ry = cross(N, dPdx);
-
- /* Compute surface gradient and determinant. */
- float det = dot(dPdx, Rx);
- float absdet = abs(det);
-
- float dHdx = dFdx(height);
- float dHdy = dFdy(height);
- vec3 surfgrad = dHdx * Rx + dHdy * Ry;
-
- strength = max(strength, 0.0);
-
- result = normalize(absdet * N - dist * sign(det) * surfgrad);
- result = normalize(strength * result + (1.0 - strength) * N);
-}
-
-/* output */
-
-void node_output_material(vec4 surface, vec4 volume, float displacement, out vec4 result)
-{
- result = surface;
-}
-
-void node_output_world(vec4 surface, vec4 volume, out vec4 result)
-{
- result = surface;
-}
-
/* ********************** matcap style render ******************** */
void material_preview_matcap(vec4 color, sampler2D ima, vec4 N, vec4 mask, out vec4 result)
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_ambient_occlusion.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_ambient_occlusion.glsl
new file mode 100644
index 00000000000..a7f594a29f5
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_ambient_occlusion.glsl
@@ -0,0 +1,160 @@
+/* -------- Utils Functions --------- */
+vec2 sample_disk(float nsample, float invsamplenbr)
+{
+ vec3 Xi = hammersley_3d(nsample, invsamplenbr);
+
+ float x = Xi.x * Xi.y;
+ float y = Xi.x * Xi.z;
+
+ return vec2(x, y);
+}
+
+vec3 sample_hemisphere(float nsample, float invsamplenbr, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample, invsamplenbr);
+
+ float z = Xi.x; /* cos theta */
+ float r = sqrt( 1.0f - z*z ); /* sin theta */
+ float x = r * Xi.y;
+ float y = r * Xi.z;
+
+ Ht = vec3(x, y, z); /* Global variable */
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+#ifdef USE_SSAO
+
+#if 0 /* Cheap */
+
+float ssao(vec3 viewpos, vec3 viewnor, out float result)
+{
+ float dist = unfssaoparam.z;
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ /* get uv of the shading point */
+ vec4 projvec = gl_ProjectionMatrix * vec4(viewpos, 1.0);
+ vec2 uv = (projvec.xy / projvec.w) * 0.5 + 0.5;
+
+ vec2 offset;
+ offset.x = gl_ProjectionMatrix[0][0] * dist / projvec.w;
+ offset.y = gl_ProjectionMatrix[1][1] * dist / projvec.w;
+ /* convert from -1.0...1.0 range to 0.0..1.0 for easy use with texture coordinates */
+ offset *= 0.5;
+
+ float factor = 0.0;
+ /* We don't need as much samples for ssao */
+ for (float i = 0; i < unfssaoparam.x; i++) {
+ vec2 Xi = sample_disk(i, 1/unfssaoparam.x);
+ vec2 uvsample = uv + Xi * offset;
+
+ if (uvsample.x > 1.0 || uvsample.x < 0.0 || uvsample.y > 1.0 || uvsample.y < 0.0)
+ continue;
+
+ float sampledepth = frontface_depth_linear(ivec2(uvsample * unfclip.zw));
+
+ /* Background Case */
+ if (sampledepth == 1.0)
+ continue;
+
+ vec3 samplepos = position_from_depth(uvsample, sampledepth);
+
+ vec3 dir = samplepos - viewpos;
+ float len = length(dir);
+ float f = dot(dir, viewnor);
+
+ /* use minor bias here to cancel self shadowing */
+ if (f > 0.05 * len + 0.0001)
+ factor += f / (len + len * len * len);
+ }
+
+ result = saturate(1.0 - factor / unfssaoparam.x);
+}
+
+#else /* Expensive */
+
+void ssao(vec3 viewpos, vec3 viewnor, out float result)
+{
+ float dist = unfssaoparam.z;
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ vec3 T;
+ make_orthonormals(viewnor, T, B); /* Generate tangent space */
+
+ float homcoord = (gl_ProjectionMatrix[3][3] == 0.0) ? gl_ProjectionMatrix[2][3] * viewpos.z : dist;
+
+ float factor = 0.0;
+ float weight = 0.0;
+
+ for (float i = 0; i < unfssaoparam.x; i++) {
+ vec3 Xi = sample_hemisphere(i, 1/unfssaoparam.x, viewnor, T, B);
+
+ float pdf = dot(Xi, viewnor);
+ weight += pdf;
+
+ /* Raymarch */
+ /* We jitter the starting position by a percentage of the normal
+ * offset to a v o i d banding artifact for thin objects but
+ * we still finish at the same final position to a v o i d
+ * less sampling at the edges of the occlusion effect */
+ vec3 offset = dist * Xi / unfssaoparam.y;
+ vec3 ray = (jitternoise.y - 0.5) * -offset;
+ vec3 endoffset = offset - ray;
+
+ for (float j = unfssaoparam.y; j > 0.0; j--) {
+
+ ray += (j == 1) ? endoffset : offset;
+
+ vec4 co = unfpixelprojmat * vec4(ray + viewpos, 1.0);
+ co.xy /= co.w;
+
+ /* Discard ray leaving screen */
+ if (co.x > unfclip.z || co.x < 0.0 || co.y > unfclip.w || co.y < 0.0)
+ break;
+
+ float sampledepth = frontface_depth_linear(ivec2(co.xy), 0);
+
+ /* Background Case */
+ if (sampledepth == 1.0)
+ continue;
+
+ /* We have a hit */
+ if (sampledepth > ray.z + viewpos.z + homcoord * 0.002
+#ifdef USE_BACKFACE
+ && backface_depth_linear(ivec2(co.xy), 0) < ray.z + viewpos.z
+#endif
+ )
+ {
+ factor += pdf;
+ break;
+ }
+ }
+ }
+
+ result = saturate(1.0 - (factor / weight) * unfssaoparam.w);
+}
+#endif
+#else
+void ssao(vec3 viewpos, vec3 viewnor, out float result)
+{
+ result = 1.0;
+}
+#endif
+
+
+/* -------- BSDF --------- */
+
+/* -------- Preview Lights --------- */
+
+/* -------- Physical Lights --------- */
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_ambient_occlusion(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ result = vec3(ao_factor);
+} \ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_anisotropic.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_anisotropic.glsl
new file mode 100644
index 00000000000..cad9e28037a
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_anisotropic.glsl
@@ -0,0 +1,772 @@
+/* -------- Utils Functions --------- */
+
+/* From
+ * Importance Sampling Microfacet-Based BSDFs with the Distribution of Visible Normals
+ * Supplemental Material 2/2 */
+vec3 sample_ggx_aniso(float nsample, float ax, float ay, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample);
+
+ float tmp = sqrt( Xi.x / (1.0 - Xi.x) );
+
+ float x = ax * tmp * Xi.y;
+ float y = ay * tmp * Xi.z;
+
+ Ht = normalize(vec3(x, y, 1.0)); /* Global variable */
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+vec3 sample_beckmann_aniso(float nsample, float ax, float ay, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample);
+
+ float tmp = sqrt( -log(Xi.x) );
+
+ float x = ax * tmp * Xi.y;
+ float y = ay * tmp * Xi.z;
+
+ Ht = normalize(vec3(x, y, 1.0)); /* Global variable */
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+float bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x, float n_y, inout vec3 Xi, out float phi)
+{
+ phi = atan(sqrt((n_x + 1.0) / (n_y + 1.0)) * (Xi.z / Xi.y));
+ Xi.y = cos(phi);
+ Xi.z = sin(phi);
+ return pow(Xi.x, 1.0 / (n_x * Xi.y*Xi.y + n_y * Xi.z*Xi.z + 1.0));
+}
+
+vec3 sample_ashikhmin_shirley_aniso(float nsample, float n_x, float n_y, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample);
+ float phi, z;
+
+ if(Xi.x < 0.25) { /* first quadrant */
+ Xi.x = 4.0 * Xi.x;
+ z = bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, Xi, phi);
+ }
+ else if(Xi.x < 0.5) { /* second quadrant */
+ Xi.x = 4.0 * (.5 - Xi.x);
+ z = bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, Xi, phi);
+ phi = M_PI - phi;
+ Xi.y = cos(phi);
+ Xi.z = sin(phi);
+ }
+ else if(Xi.x < 0.75) { /* third quadrant */
+ Xi.x = 4.0 * (Xi.x - 0.5);
+ z = bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, Xi, phi);
+ phi = M_PI + phi;
+ Xi.y = cos(phi);
+ Xi.z = sin(phi);
+ }
+ else { /* fourth quadrant */
+ Xi.x = 4.0 * (1.0 - Xi.x);
+ z = bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, Xi, phi);
+ phi = M_2PI - phi;
+ Xi.y = cos(phi);
+ Xi.z = sin(phi);
+ }
+
+ float r = sqrt( 1.0 - z * z ); /* sin theta */
+ float x = r * Xi.y;
+ float y = r * Xi.z;
+
+ Ht = vec3(x, y, z); /* Global variable */
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+float D_ggx_aniso_opti(float NH, float XH2, float YH2, float a2, float ax2, float ay2)
+{
+ float tmp = NH*NH + XH2/ax2 + YH2/ay2; /* Distributing NH² */
+ return M_PI * a2 * tmp*tmp; /* Doing RCP at the end */
+}
+
+float D_beckmann_aniso(float NH, float XH, float YH, float a2, float ax, float ay)
+{
+ float sx = -XH / (NH * ax);
+ float sy = -YH / (NH * ay);
+
+ float NH2 = NH * NH;
+
+ return exp(-sx*sx - sy*sy) / (M_PI * a2 * NH2 * NH2);
+}
+
+float pdf_ggx_aniso(float NH, float XH2, float YH2, float a2, float ax2, float ay2)
+{
+ float D = D_ggx_aniso_opti(NH, XH2, YH2, a2, ax2, ay2);
+ return NH / D;
+}
+
+float pdf_beckmann_aniso(float NH, float XH, float YH, float a2, float ax, float ay)
+{
+ float D = D_beckmann_aniso(NH, XH, YH, a2, ax, ay);
+ return NH / D;
+}
+
+float pdf_ashikhmin_shirley_aniso(float NH, float VH, float XH2, float YH2, float n_x, float n_y)
+{
+ float e = (n_x * XH2 + n_y * YH2) / (1.0 - NH*NH);
+ float lobe = pow(NH, e);
+ float norm = sqrt((n_x + 1.0)*(n_y + 1.0)) * 0.125 * M_1_PI;
+
+ return norm * lobe / VH;
+}
+
+/* TODO : this could be precomputed */
+void prepare_aniso(vec3 N, float roughness, float rotation, inout vec3 T, inout float anisotropy, out float rough_x, out float rough_y)
+{
+ anisotropy = clamp(anisotropy, -0.99, 0.99);
+
+ if (anisotropy < 0.0) {
+ rough_x = roughness / (1.0 + anisotropy);
+ rough_y = roughness * (1.0 + anisotropy);
+ }
+ else {
+ rough_x = roughness * (1.0 - anisotropy);
+ rough_y = roughness / (1.0 - anisotropy);
+ }
+
+ T = axis_angle_rotation(T, N, rotation * M_2PI); /* rotate tangent around normal */
+}
+
+/* -------- BSDF --------- */
+
+float bsdf_ggx_aniso(vec3 N, vec3 T, vec3 L, vec3 V, float roughness_x, float roughness_y)
+{
+ /* GGX Spec Anisotropic */
+ /* A few note about notations :
+ * I is the cycles term for Incoming Light, Noted L here (light vector)
+ * Omega (O) is the cycles term for the Outgoing Light, Noted V here (View vector) */
+ N = normalize(N);
+ vec3 X = T, Y, Z = N; /* Inside cycles Z=Normal; X=Tangent; Y=Bitangent; */
+ make_orthonormals_tangent(Z, X, Y);
+ vec3 H = normalize(L + V);
+
+ float ax, ax2; prepare_glossy(roughness_x, ax, ax2);
+ float ay, ay2; prepare_glossy(roughness_y, ay, ay2);
+ float a2 = ax*ay;
+
+ float NH = max(1e-8, dot(N, H));
+ float NL = max(1e-8, dot(N, L));
+ float NV = max(1e-8, dot(N, V));
+ float VX2 = pow(dot(V, X), 2); /* cosPhiO² */
+ float VY2 = pow(dot(V, Y), 2); /* sinPhiO² */
+ float LX2 = pow(dot(L, X), 2); /* cosPhiI² */
+ float LY2 = pow(dot(L, Y), 2); /* sinPhiI² */
+ float XH2 = pow(dot(X, H), 2);
+ float YH2 = pow(dot(Y, H), 2);
+
+ /* G_Smith_GGX */
+ float alphaV2 = (VX2 * ax2 + VY2 * ay2) / (VX2 + VY2);
+ float alphaL2 = (LX2 * ax2 + LY2 * ay2) / (LX2 + LY2);
+ float G = G1_Smith_GGX(NV, alphaV2) * G1_Smith_GGX(NL, alphaL2); /* Doing RCP at the end */
+
+ /* D_GGX */
+ float D = D_ggx_aniso_opti(NH, XH2, YH2, a2, ax2, ay2);
+
+ /* Denominator is canceled by G1_Smith */
+ /* bsdf = D * G / (4.0 * NL * NV); /* Reference function */
+ return NL / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */
+}
+
+/* This one returns the brdf already divided by the pdf */
+float bsdf_ggx_aniso_pdf(float ax2, float ay2, float LX2, float LY2, float NH, float NV, float NL, float VH, float G1_V)
+{
+ float alphaL2 = (LX2 * ax2 + LY2 * ay2) / (LX2 + LY2);
+ float G = G1_V * G1_Smith_GGX(NL, alphaL2);
+
+ /* Denominator is canceled by G1_Smith
+ * brdf = D * G / (4.0 * NL * NV)
+ * pdf = D * NH / (4 * VH) [D canceled later by D in brdf] */
+ return 4.0 * VH / (NH * G); /* brdf / pdf */
+}
+
+float bsdf_beckmann_aniso(vec3 N, vec3 T, vec3 L, vec3 V, float roughness_x, float roughness_y)
+{
+ /* Beckmann Spec Anisotropic */
+ /* A few note about notations :
+ * I is the cycles term for Incoming Light, Noted L here (light vector)
+ * Omega (O) is the cycles term for the Outgoing Light, Noted V here (View vector) */
+ N = normalize(N);
+ vec3 X = T, Y, Z = N; /* Inside cycles Z=Normal; X=Tangent; Y=Bitangent; */
+ make_orthonormals_tangent(Z, X, Y);
+ vec3 H = normalize(L + V);
+
+ float ax, ax2; prepare_glossy(roughness_x, ax, ax2);
+ float ay, ay2; prepare_glossy(roughness_y, ay, ay2);
+ float a2 = ax*ay;
+
+ float NH = max(1e-8, dot(N, H));
+ float NL = max(1e-8, dot(N, L));
+ float NV = max(1e-8, dot(N, V));
+ float VX2 = pow(dot(V, X), 2); /* cosPhiO² */
+ float VY2 = pow(dot(V, Y), 2); /* sinPhiO² */
+ float LX2 = pow(dot(L, X), 2); /* cosPhiI² */
+ float LY2 = pow(dot(L, Y), 2); /* sinPhiI² */
+ float XH = dot(X, H);
+ float YH = dot(Y, H);
+
+ float alphaV2 = (VX2 * ax2 + VY2 * ay2) / (VX2 + VY2);
+ float alphaL2 = (LX2 * ax2 + LY2 * ay2) / (LX2 + LY2);
+ float G = G1_Smith_beckmann(NV, alphaV2) * G1_Smith_beckmann(NL, alphaL2);
+
+ float D = D_beckmann_aniso(NH, XH, YH, a2, ax, ay);
+
+ return NL * D * G * 0.25 / (NL * NV);
+}
+
+/* This one returns the brdf already divided by the pdf */
+float bsdf_beckmann_aniso_pdf(float ax2, float ay2, float LX2, float LY2, float NH, float NV, float NL, float VH, float G1_V)
+{
+ float alphaL2 = (LX2 * ax2 + LY2 * ay2) / (LX2 + LY2);
+ float G = G1_V * G1_Smith_beckmann(NL, alphaL2);
+
+ /* brdf = D * G / (4.0 * NL * NV)
+ * pdf = D * NH / (4 * VH) [D canceled later by D in brdf] */
+ return G * VH / (NH * NV * NL); /* brdf / pdf */
+}
+
+float bsdf_ashikhmin_shirley_aniso(vec3 N, vec3 T, vec3 L, vec3 V, float roughness_x, float roughness_y)
+{
+ /* Ashikmin Shirley Spec Anisotropic */
+ /* A few note about notations :
+ * I is the cycles term for Incoming Light, Noted L here (light vector)
+ * Omega (O) is the cycles term for the Outgoing Light, Noted V here (View vector) */
+ N = normalize(N);
+ vec3 X = T, Y, Z = N; /* Inside cycles Z=Normal; X=Tangent; Y=Bitangent; */
+ make_orthonormals_tangent(Z, X, Y);
+ vec3 H = normalize(L + V);
+
+ float ax, ax2; prepare_glossy(roughness_x, ax, ax2);
+ float ay, ay2; prepare_glossy(roughness_y, ay, ay2);
+ float a2 = ax*ay;
+ float n_x = 2.0 / ax2 - 2.0;
+ float n_y = 2.0 / ay2 - 2.0;
+
+ float NL = max(dot(N, L), 1e-6);
+ float NV = max(dot(N, V), 1e-6);
+ float NH = max(dot(N, H), 1e-6);
+ float VH = max(abs(dot(V, H)), 1e-6);
+ float XH2 = max(pow(dot(X, H), 2), 1e-6);
+ float YH2 = max(pow(dot(Y, H), 2), 1e-6);
+
+ float pump = 1.0 / max(1e-6, VH * max(NL, NV));
+
+ float e = max(n_x * XH2 + n_y * YH2, 1e-4) / max(1.0 - NH*NH, 1e-4); /* Precision problem here */
+ float lobe = pow(NH, e);
+ float norm = sqrt((n_x + 1.0)*(n_y + 1.0)) * 0.125 * M_1_PI;
+
+ return NL * norm * lobe * pump;
+}
+
+/* -------- Preview Lights --------- */
+
+void node_bsdf_anisotropic_lights(vec4 color, float roughness, float anisotropy, float rotation, vec3 N, vec3 T, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ N = normalize(N);
+ shade_view(V, V); V = -V;
+
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, rotation, T, anisotropy, rough_x, rough_y);
+ vec3 accumulator = ambient_light.rgb;
+
+ if (max(rough_x, rough_y) <= 1e-4) {
+ result = vec4(accumulator * color.rgb, 1.0); //Should take roughness into account -> waiting LUT
+ return;
+ }
+
+ /* directional lights */
+ for(int i = 0; i < NUM_LIGHTS; i++) {
+ vec3 L = gl_LightSource[i].position.xyz;
+ vec3 light_color = gl_LightSource[i].specular.rgb;
+
+ accumulator += light_color * bsdf_ggx_aniso(N, T, L, V, rough_x, rough_y);
+ }
+
+ result = vec4(accumulator * color.rgb, 1.0);
+}
+
+
+/* -------- Physical Lights --------- */
+
+/* ANISOTROPIC GGX */
+
+void bsdf_anisotropic_ggx_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ if (max(rough_x, rough_y) < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float l_radius = l_areasizex;
+
+ vec3 R = reflect(V, N);
+ float energy_conservation = 1.0;
+ most_representative_point(l_radius, 0.0, vec3(0.0), l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_ggx_aniso(N, T, L, V, rough_x, rough_y);
+
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= sphere_energy(l_radius) * max(l_radius * l_radius, 1e-16); /* l_radius is already inside energy_conservation */
+ bsdf *= M_PI; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_anisotropic_ggx_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (max(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ l_areasizex *= l_areascale.x;
+ l_areasizey *= l_areascale.y;
+
+ /* Used later for Masking : Use the real Light Vector */
+ vec3 lampz = normalize( (l_mat * vec4(0.0,0.0,1.0,0.0) ).xyz );
+ float masking = max(dot( normalize(-L), lampz), 0.0);
+
+ vec3 R = reflect(V, N);
+
+ float energy_conservation = 1.0;
+ float max_size = max(l_areasizex, l_areasizey);
+ float min_size = min(l_areasizex, l_areasizey);
+ vec3 lampVec = (l_areasizex > l_areasizey) ? normalize( (l_mat * vec4(1.0,0.0,0.0,0.0) ).xyz ) : normalize( (l_mat * vec4(0.0,1.0,0.0,0.0) ).xyz );
+
+ most_representative_point(min_size/2, max_size-min_size, lampVec, l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_ggx_aniso(N, T, L, V, rough_x, rough_y);
+
+ /* energy_conservation */
+ float LineAngle = clamp( (max_size-min_size) / l_distance, 0.0, 1.0);
+ float energy_conservation_line = energy_conservation * ( roughness / clamp(roughness + 0.5 * LineAngle, 0.0, 1.1));
+
+ /* XXX : Empirical modification for low roughness matching */
+ float energy_conservation_mod = energy_conservation * (1 + roughness) / ( max_size/min_size );
+ energy_conservation = mix(energy_conservation_mod, energy_conservation_line, min(roughness/0.3, 0.9*(1.1-roughness)/0.1));
+
+ /* As we represent the Area Light by a tube light we must use a custom energy conservation */
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= masking;
+ bsdf *= 23.2; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_anisotropic_ggx_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ /* Correct ligth shape but uniform intensity
+ * Does not take into account the division by costheta^3 */
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ vec3 R = reflect(V, N);
+
+ float l_radius = l_areasizex;
+ float angle = atan(l_radius);
+
+ float costheta = dot(-L, R);
+ float cosangle = cos(angle);
+
+ float energy_conservation = 1.0;
+ most_representative_point_disk(l_radius, 1.0, R, L, roughness, energy_conservation);
+
+ bsdf = bsdf_ggx_aniso(N, T, L, V, rough_x, rough_y);
+ bsdf *= energy_conservation;
+}
+
+/* ANISOTROPIC BECKMANN */
+
+void bsdf_anisotropic_beckmann_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ if (max(rough_x, rough_y) < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float l_radius = l_areasizex;
+
+ vec3 R = reflect(V, N);
+ float energy_conservation = 1.0;
+ most_representative_point(l_radius, 0.0, vec3(0.0), l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_beckmann_aniso(N, T, L, V, rough_x, rough_y);
+
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= sphere_energy(l_radius) * max(l_radius * l_radius, 1e-16); /* l_radius is already inside energy_conservation */
+ bsdf *= M_PI; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_anisotropic_beckmann_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (max(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ l_areasizex *= l_areascale.x;
+ l_areasizey *= l_areascale.y;
+
+ /* Used later for Masking : Use the real Light Vector */
+ vec3 lampz = normalize( (l_mat * vec4(0.0,0.0,1.0,0.0) ).xyz );
+ float masking = max(dot( normalize(-L), lampz), 0.0);
+
+ vec3 R = reflect(V, N);
+
+ float energy_conservation = 1.0;
+ float max_size = max(l_areasizex, l_areasizey);
+ float min_size = min(l_areasizex, l_areasizey);
+ vec3 lampVec = (l_areasizex > l_areasizey) ? normalize( (l_mat * vec4(1.0,0.0,0.0,0.0) ).xyz ) : normalize( (l_mat * vec4(0.0,1.0,0.0,0.0) ).xyz );
+
+ most_representative_point(min_size/2, max_size-min_size, lampVec, l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_beckmann_aniso(N, T, L, V, rough_x, rough_y);
+
+ /* energy_conservation */
+ float LineAngle = clamp( (max_size-min_size) / l_distance, 0.0, 1.0);
+ float energy_conservation_line = energy_conservation * ( roughness / clamp(roughness + 0.5 * LineAngle, 0.0, 1.1));
+
+ /* XXX : Empirical modification for low roughness matching */
+ float energy_conservation_mod = energy_conservation * (1 + roughness) / ( max_size/min_size );
+ energy_conservation = mix(energy_conservation_mod, energy_conservation_line, min(roughness/0.3, 0.9*(1.1-roughness)/0.1));
+
+ /* As we represent the Area Light by a tube light we must use a custom energy conservation */
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= masking;
+ bsdf *= 23.2; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_anisotropic_beckmann_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ /* Correct ligth shape but uniform intensity
+ * Does not take into account the division by costheta^3 */
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ vec3 R = reflect(V, N);
+
+ float l_radius = l_areasizex;
+ float angle = atan(l_radius);
+
+ float costheta = dot(-L, R);
+ float cosangle = cos(angle);
+
+ float energy_conservation = 1.0;
+ most_representative_point_disk(l_radius, 1.0, R, L, roughness, energy_conservation);
+
+ bsdf = bsdf_beckmann_aniso(N, T, L, V, rough_x, rough_y);
+ bsdf *= energy_conservation;
+}
+
+/* ANISOTROPIC ASHIKHMIN SHIRLEY */
+
+void bsdf_anisotropic_ashikhmin_shirley_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ if (max(rough_x, rough_y) < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float l_radius = l_areasizex;
+
+ vec3 R = reflect(V, N);
+ float energy_conservation = 1.0;
+ most_representative_point(l_radius, 0.0, vec3(0.0), l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_ashikhmin_shirley_aniso(N, T, L, V, rough_x, rough_y);
+
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= sphere_energy(l_radius) * max(l_radius * l_radius, 1e-16); /* l_radius is already inside energy_conservation */
+ bsdf *= M_PI; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_anisotropic_ashikhmin_shirley_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (max(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ l_areasizex *= l_areascale.x;
+ l_areasizey *= l_areascale.y;
+
+ /* Used later for Masking : Use the real Light Vector */
+ vec3 lampz = normalize( (l_mat * vec4(0.0,0.0,1.0,0.0) ).xyz );
+ float masking = max(dot( normalize(-L), lampz), 0.0);
+
+ vec3 R = reflect(V, N);
+
+ float energy_conservation = 1.0;
+ float max_size = max(l_areasizex, l_areasizey);
+ float min_size = min(l_areasizex, l_areasizey);
+ vec3 lampVec = (l_areasizex > l_areasizey) ? normalize( (l_mat * vec4(1.0,0.0,0.0,0.0) ).xyz ) : normalize( (l_mat * vec4(0.0,1.0,0.0,0.0) ).xyz );
+
+ most_representative_point(min_size/2, max_size-min_size, lampVec, l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_ashikhmin_shirley_aniso(N, T, L, V, rough_x, rough_y);
+
+ /* energy_conservation */
+ float LineAngle = clamp( (max_size-min_size) / l_distance, 0.0, 1.0);
+ float energy_conservation_line = energy_conservation * ( roughness / clamp(roughness + 0.5 * LineAngle, 0.0, 1.1));
+
+ /* XXX : Empirical modification for low roughness matching */
+ float energy_conservation_mod = energy_conservation * (1 + roughness) / ( max_size/min_size );
+ energy_conservation = mix(energy_conservation_mod, energy_conservation_line, min(roughness/0.3, 0.9*(1.1-roughness)/0.1));
+
+ /* As we represent the Area Light by a tube light we must use a custom energy conservation */
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= masking;
+ bsdf *= 23.2; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_anisotropic_ashikhmin_shirley_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float rough_x, rough_y;
+ prepare_aniso(N, roughness, aniso_rotation, T, anisotropy, rough_x, rough_y);
+
+ /* Correct ligth shape but uniform intensity
+ * Does not take into account the division by costheta^3 */
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ vec3 R = reflect(V, N);
+
+ float l_radius = l_areasizex;
+ float angle = atan(l_radius);
+
+ float costheta = dot(-L, R);
+ float cosangle = cos(angle);
+
+ float energy_conservation = 1.0;
+ most_representative_point_disk(l_radius, 1.0, R, L, roughness, energy_conservation);
+
+ bsdf = bsdf_ashikhmin_shirley_aniso(N, T, L, V, rough_x, rough_y);
+ bsdf *= energy_conservation;
+}
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_aniso_ggx(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ float rough_x, rough_y; prepare_aniso(N, roughness, -aniso_rotation, T, anisotropy, rough_x, rough_y);
+ float ax, ax2; prepare_glossy(rough_x, ax, ax2);
+ float ay, ay2; prepare_glossy(rough_y, ay, ay2);
+ make_orthonormals_tangent(N, T, B);
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ /* Precomputation */
+ float max_a = min(1.0, max(ax, ay));
+ float min_a = min(1.0, min(ax, ay));
+ float a2 = ax*ay;
+ float NV = max(1e-8, abs(dot(I, N)));
+ float VX2 = pow(dot(I, T), 2); /* cosPhiO² */
+ float VY2 = pow(dot(I, B), 2); /* sinPhiO² */
+ float alphaV2 = (VX2 * ax2 + VY2 * ay2) / (VX2 + VY2);
+ float G1_V = G1_Smith_GGX(NV, alphaV2);
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_ggx_aniso(i, ax, ay, N, T, B); /* Microfacet normal */
+ vec3 L = reflect(I, H);
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = Ht.z;
+ float XH2 = Ht.x * Ht.x;
+ float YH2 = Ht.y * Ht.y;
+
+ float pdf = pdf_ggx_aniso(NH, XH2, YH2, a2, ax, ay);
+
+ vec4 sample = sample_reflect_pdf(L, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float VH = max(1e-8, -dot(I, H));
+ float LX2 = pow(dot(L, T), 2); /* cosPhiI² */
+ float LY2 = pow(dot(L, B), 2); /* sinPhiI² */
+ float brdf_pdf = bsdf_ggx_aniso_pdf(ax2, ay2, LX2, LY2, NH, NV, NL, VH, G1_V);
+
+ out_radiance += NL * sample * brdf_pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2);
+}
+
+void env_sampling_aniso_beckmann(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ float rough_x, rough_y; prepare_aniso(N, roughness, -aniso_rotation, T, anisotropy, rough_x, rough_y);
+ float ax, ax2; prepare_glossy(rough_x, ax, ax2);
+ float ay, ay2; prepare_glossy(rough_y, ay, ay2);
+ make_orthonormals_tangent(N, T, B);
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ /* Precomputation */
+ float a2 = ax*ay;
+ float NV = max(1e-8, abs(dot(I, N)));
+ float VX2 = pow(dot(I, T), 2); /* cosPhiO² */
+ float VY2 = pow(dot(I, B), 2); /* sinPhiO² */
+ float alphaV2 = (VX2 * ax2 + VY2 * ay2) / (VX2 + VY2);
+ float G1_V = G1_Smith_beckmann(NV, alphaV2);
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_beckmann_aniso(i, ax, ay, N, T, B); /* Microfacet normal */
+ vec3 L = reflect(I, H);
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = Ht.z;
+ float XH = Ht.x;
+ float YH = Ht.y;
+
+ float pdf = pdf_beckmann_aniso(NH, XH, YH, a2, ax, ay);
+
+ vec4 sample = sample_reflect_pdf(L, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float VH = max(1e-8, -dot(I, H));
+ float LX2 = pow(dot(L, T), 2); /* cosPhiI² */
+ float LY2 = pow(dot(L, B), 2); /* sinPhiI² */
+ float brdf_pdf = bsdf_beckmann_aniso_pdf(ax2, ay2, LX2, LY2, NH, NV, NL, VH, G1_V);
+
+ out_radiance += NL * sample * brdf_pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2);
+}
+
+void env_sampling_aniso_ashikhmin_shirley(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ float rough_x, rough_y; prepare_aniso(N, roughness, -aniso_rotation, T, anisotropy, rough_x, rough_y);
+ float ax, ax2; prepare_glossy(rough_x, ax, ax2);
+ float ay, ay2; prepare_glossy(rough_y, ay, ay2);
+ make_orthonormals_tangent(N, T, B);
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ /* Precomputation */
+ float a2 = ax*ay;
+ float NV = max(1e-8, abs(dot(I, N)));
+ float VX2 = pow(dot(I, T), 2); /* cosPhiO² */
+ float VY2 = pow(dot(I, B), 2); /* sinPhiO² */
+
+ float n_x = 2.0 / ax2 - 2.0;
+ float n_y = 2.0 / ay2 - 2.0;
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_ashikhmin_shirley_aniso(i, n_x, n_y, N, T, B); /* Microfacet normal */
+ float VH = dot(H, -I);
+ if (VH < 0.0) H = -H;
+ /* reflect I on H to get omega_in */
+ vec3 L = I + (2.0 * VH) * H;
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = Ht.z;
+ float XH2 = Ht.x * Ht.x;
+ float YH2 = Ht.y * Ht.y;
+ float VH = max(1e-8, -dot(I, H));
+
+ float pdf = pdf_ashikhmin_shirley_aniso(NH, VH, XH2, YH2, n_x, n_y);
+
+ vec4 sample = sample_reflect_pdf(L, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_ashikhmin_shirley_pdf(NV, NL, VH); /* Same as isotropic */
+
+ out_radiance += NL * sample * brdf_pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2);
+} \ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_diffuse.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_diffuse.glsl
new file mode 100644
index 00000000000..a2d6b42a57c
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_diffuse.glsl
@@ -0,0 +1,263 @@
+/* -------- Utils Functions --------- */
+
+vec3 sample_hemisphere(float nsample, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample);
+
+ float z = Xi.x; /* cos theta */
+ float r = sqrt( 1.0f - z*z ); /* sin theta */
+ float x = r * Xi.y;
+ float y = r * Xi.z;
+
+ Ht = vec3(x, y, z); /* Global variable */
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+float pdf_hemisphere()
+{
+ return 0.5 * M_1_PI;
+}
+
+/* Second order Spherical Harmonics */
+/* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */
+vec3 spherical_harmonics_L2(vec3 N)
+{
+ vec3 sh = vec3(0.0);
+
+ sh += 0.282095 * unfsh0;
+
+ sh += -0.488603 * N.z * unfsh1;
+ sh += 0.488603 * N.y * unfsh2;
+ sh += -0.488603 * N.x * unfsh3;
+
+ sh += 1.092548 * N.x * N.z * unfsh4;
+ sh += -1.092548 * N.z * N.y * unfsh5;
+ sh += 0.315392 * (3.0 * N.y * N.y - 1.0) * unfsh6;
+ sh += -1.092548 * N.x * N.y * unfsh7;
+ sh += 0.546274 * (N.x * N.x - N.z * N.z) * unfsh8;
+
+ return sh;
+}
+
+/* -------- BSDF --------- */
+
+float bsdf_lambert(float NL)
+{
+ return NL * M_1_PI;
+}
+
+float bsdf_oren_nayar(float NL, float LV, float NV, float sigma)
+{
+ float div = 1.0 / (M_PI + ((3.0 * M_PI - 4.0) / 6.0) * sigma);
+
+ float A = 1.0 * div;
+ float B = sigma * div;
+
+ float s = LV - NL * NV;
+ float t = mix(1.0, max(NL, NV), step(0.0, s));
+ return NL * (A + B * s / t);
+}
+
+float bsdf_oren_nayar(vec3 N, vec3 L, vec3 V, float sigma)
+{
+ float NL = max(0.0, dot(N, L));
+ float LV = max(0.0, dot(L, V));
+ float NV = max(1e-8, dot(N, V));
+ return bsdf_oren_nayar(NL, LV, NV, sigma);
+}
+
+
+/* -------- Preview Lights --------- */
+
+void node_bsdf_diffuse_lights(vec4 color, float roughness, vec3 N, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ shade_view(V, V); V = -V;
+
+ /* ambient light */
+ vec3 accumulator = ambient_light.rgb;
+
+ /* oren_nayar approximation for ambient */
+ float NV = clamp(dot(N, V), 0.0, 0.999);
+ float fac = 1.0 - pow(1.0 - NV, 1.3);
+ accumulator *= mix(1.0, 0.78, fac*roughness);
+
+ /* directional lights */
+ for(int i = 0; i < NUM_LIGHTS; i++) {
+ vec3 L = gl_LightSource[i].position.xyz;
+ vec3 light_color = gl_LightSource[i].diffuse.rgb;
+
+ float NL = saturate(dot(N,L));
+ float lambert = bsdf_lambert(NL);
+ float oren_nayar = bsdf_oren_nayar(N, L, V, roughness);
+
+ accumulator += light_color * mix(lambert, oren_nayar, roughness) * M_PI; /* M_PI to make preview brighter */
+ }
+
+ result = vec4(accumulator*color.rgb, 1.0);
+}
+
+
+/* -------- Physical Lights --------- */
+/* from Sebastien Lagarde
+ * course_notes_moving_frostbite_to_pbr.pdf */
+
+void bsdf_diffuse_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float l_radius = max(l_areasizex, 0.0001);
+ float costheta = clamp(dot(N, L), -0.999, 0.999);
+ float h = min(l_radius / l_distance , 0.9999);
+ float h2 = h*h;
+
+ bsdf = 0.0;
+ if ( costheta * costheta > h2 ) {
+ bsdf = M_PI * h2 * clamp(costheta, 0.0, 1.0);
+ }
+ else {
+ float sintheta = sqrt(1.0 - costheta * costheta);
+ float x = sqrt(1.0 / h2 - 1.0);
+ float y = -x * ( costheta / sintheta );
+ float sinthetasqrty = sintheta * sqrt(1.0 - y * y);
+ bsdf = (costheta * acos(y) - x * sinthetasqrty ) * h2 + atan(sinthetasqrty / x);
+ }
+
+ /* Energy conservation + cycle matching */
+ bsdf = max(bsdf, 0.0);
+ bsdf *= M_1_PI;
+ bsdf *= sphere_energy(l_radius);
+}
+
+void bsdf_diffuse_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (min(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ vec3 pos = V;
+ V = -normalize(V);
+ N = -N;
+
+ vec3 lampx, lampy, lampz;
+ vec2 halfsize = area_light_prepass(l_mat, l_areasizex, l_areasizey, l_areascale, lampx, lampy, lampz);
+
+ vec3 points[4];
+ area_light_points(l_coords, halfsize, lampx, lampy, points);
+
+ bsdf = ltc_evaluate(N, V, pos, mat3(1), points);
+ bsdf *= step(0.0, -dot(L, lampz));
+
+ /* Energy conservation + cycle matching */
+ bsdf *= M_1_2PI;
+ bsdf *= rectangle_energy(l_areasizex, l_areasizey);
+}
+float cot(float x){ return cos(x) / sin(x);}
+float acot(float x){ return atan(1 / x);}
+void bsdf_diffuse_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float l_radius = max(l_areasizex, 0.0001);
+ float costheta = clamp(dot(N, L), -0.999, 0.999);
+ float sintheta = sqrt(1.0 - costheta * costheta);
+ float h = 1 / l_radius;
+ float h2 = h * h;
+
+ if (acos(costheta) < atan(h)) {
+ bsdf = M_PI * (1 / (1 + h2)) * costheta;
+ }
+ else {
+ float cottheta = costheta / sintheta;
+ float x = sqrt(1 - h2 * cottheta * cottheta);
+ bsdf = (-h * x + costheta * (M_PI - acos(h * cottheta))) / (1 + h2) + atan(x / h);
+ }
+ /* Energy conservation + cycle matching */
+ float disk_energy = disk_energy(l_radius);
+ bsdf = max(bsdf, 0.0) * disk_energy;
+ /* TODO Refine this :
+ * We can try to add contribution of infinitely many point lights at the border of the disk if we know their intensity
+ * Border intensity should be added to the above uniform disk calculation and should be complementary */
+ //bsdf += sqrt(1.0 - abs(costheta * costheta * costheta)) * saturate(M_1_2PI - disk_energy);
+ bsdf *= M_1_PI;
+
+}
+
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_oren_nayar(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ /* Precomputation */
+ float NV = max(1e-8, abs(dot(I, N)));
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 L = sample_hemisphere(i, N, T, B);
+ vec3 H = normalize(L - I);
+
+ float NL = max(0.0, dot(N, L));
+
+ if (NL != 0.0) {
+ /* Step 1 : Sampling Environment */
+ float pdf = pdf_hemisphere();
+ vec4 irradiance = sample_probe_pdf(L, pdf);
+
+ /* Step 2 : Integrating BRDF*/
+ float LV = max(0.0, dot(L, -I) );
+ float brdf = bsdf_oren_nayar(NL, LV, NV, roughness);
+
+ out_radiance += irradiance * brdf / pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y;
+}
+
+void env_sampling_diffuse(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Lambert */
+ vec3 lambert_diff = spherical_harmonics_L2(N);
+
+ /* early out */
+ if (roughness < 1e-5) {
+ result = lambert_diff * ao_factor;
+ return;
+ }
+
+ /* Oren Nayar */
+ vec3 oren_nayar_diff;
+ env_sampling_oren_nayar(
+ pbr, viewpos, invviewmat, viewmat,
+ N, T, roughness, ior, sigma,
+ toon_size, toon_smooth, anisotropy, aniso_rotation,
+ ao_factor, oren_nayar_diff);
+
+ result = mix(lambert_diff, oren_nayar_diff, roughness);
+
+ /* Apply ambient occlusion */
+ result *= ao_factor;
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_glass.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_glass.glsl
new file mode 100644
index 00000000000..7e51aeb5ff5
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_glass.glsl
@@ -0,0 +1,345 @@
+/* -------- Utils Functions --------- */
+
+float fresnel_blend(float transmit_bsdf, float reflect_bsdf, vec3 V, vec3 N, float ior)
+{
+ /* Fresnel Blend */
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ float fresnel = fresnel_dielectric(V, N, eta);
+
+ return mix(max(0.0, transmit_bsdf), reflect_bsdf, fresnel);
+}
+
+vec3 fresnel_blend(vec3 transmit_bsdf, vec3 reflect_bsdf, vec3 V, vec3 N, float ior)
+{
+ /* Fresnel Blend */
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ float fresnel = fresnel_dielectric(V, N, eta);
+
+ return mix(transmit_bsdf, reflect_bsdf, fresnel);
+}
+
+
+/* -------- BSDF --------- */
+
+/* -------- Preview Lights --------- */
+
+void node_bsdf_glass_lights(vec4 color, float roughness, float ior, vec3 N, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ node_bsdf_glossy_lights(color, roughness, N, V, ambient_light, result);
+}
+
+/* -------- Physical Lights --------- */
+
+/* GLASS GGX */
+
+void bsdf_glass_ggx_sphere_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_ggx_sphere_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_ggx_sphere_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+void bsdf_glass_ggx_area_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_ggx_area_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_ggx_area_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+void bsdf_glass_ggx_sun_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_ggx_sun_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_ggx_sun_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+/* GLASS BECKMANN */
+
+void bsdf_glass_beckmann_sphere_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_beckmann_sphere_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_beckmann_sphere_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+void bsdf_glass_beckmann_area_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_beckmann_area_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_beckmann_area_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+void bsdf_glass_beckmann_sun_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_beckmann_sun_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_beckmann_sun_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+/* GLASS SHARP */
+
+void bsdf_glass_sharp_sphere_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_sharp_sphere_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_sharp_sphere_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+void bsdf_glass_sharp_area_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_sharp_area_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_sharp_area_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+void bsdf_glass_sharp_sun_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float transmit_bsdf, reflect_bsdf;
+
+ bsdf_refract_sharp_sun_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, transmit_bsdf);
+ bsdf_glossy_sharp_sun_light(N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, roughness, ior,
+ sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, reflect_bsdf);
+
+ bsdf = fresnel_blend(transmit_bsdf, reflect_bsdf, V, N, ior);
+}
+
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_glass_sharp(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+
+ /* reflection */
+ vec3 R = reflect(I, N);
+ vec4 reflect_bsdf = sample_reflect(R);
+
+ /* transmission */
+ float eta = (gl_FrontFacing) ? 1.0/ior : ior;
+ vec3 Tr = refract(I, N, eta);
+ vec4 transmit_bsdf = sample_refract(Tr);
+
+ result = fresnel_blend(transmit_bsdf.rgb, reflect_bsdf.rgb, I, N, ior) * specular_occlusion(dot(N,-I), ao_factor, 0.0);
+}
+
+void env_sampling_glass_ggx(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+ float a, a2; prepare_glossy(roughness, a, a2);
+ ior = max(ior, 1e-5);
+
+ /* Precomputation */
+ float NV = max(1e-8, abs(dot(I, N)));
+ float G1_V = G1_Smith_GGX(NV, a2);
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_ggx(i, a2, N, T, B); /* Microfacet normal */
+
+ /* TODO : For ior < 1.0 && roughness > 0.0 fresnel becomes not accurate.*/
+ float fresnel = fresnel_dielectric(I, H, (dot(H, -I) < 0.0) ? 1.0/ior : ior );
+
+ /* reflection */
+ vec3 R = reflect(I, H);
+ float NL = dot(N, R);
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = max(1e-8, dot(N, H)); /* cosTheta */
+ float VH = max(1e-8, -dot(I, H));
+
+ float pdf = pdf_ggx_reflect(NH, a2);
+
+ vec4 sample = sample_reflect_pdf(R, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_ggx_pdf(a2, NH, NL, VH, G1_V);
+
+ /* See reflect glossy */
+ out_radiance += NL * sample * brdf_pdf * fresnel;
+ }
+
+ /* transmission */
+ float eta = 1.0/ior;
+ if (dot(H, -I) < 0.0) {
+ H = -H;
+ eta = ior;
+ }
+
+ vec3 Tr = refract(I, H, eta);
+ NL = -dot(N, Tr);
+ if (NL > 0.0 && fresnel != 1.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = dot(N, H); /* cosTheta */
+ float VH = dot(-I, H);
+ float LH = dot(Tr, H);
+ float tmp = ior * VH + LH;
+ float Ht2 = tmp * tmp;
+
+ float pdf = pdf_ggx_refract(Ht2, NH, NV, VH, LH, G1_V, a2, eta);
+
+ vec4 sample = sample_refract_pdf(Tr, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_ggx_refract_pdf(a2, LH, NL, VH);
+
+ out_radiance += sample * brdf_pdf * (1 - fresnel);
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2);
+}
+
+void env_sampling_glass_beckmann(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+ float a, a2; prepare_glossy(roughness, a, a2);
+ ior = max(ior, 1e-5);
+
+ /* Precomputation */
+ float NV = max(1e-8, abs(dot(I, N)));
+ float G1_V = G1_Smith_GGX(NV, a2);
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_beckmann(i, a2, N, T, B); /* Microfacet normal */
+
+ /* TODO : For ior < 1.0 && roughness > 0.0 fresnel becomes not accurate.*/
+ float fresnel = fresnel_dielectric(I, H, (dot(H, -I) < 0.0) ? 1.0/ior : ior );
+
+ /* reflection */
+ vec3 R = reflect(I, H);
+ float NL = dot(N, R);
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = max(1e-8, dot(N, H)); /* cosTheta */
+ float VH = max(1e-8, -dot(I, H));
+
+ float pdf = pdf_beckmann_reflect(NH, a2);
+
+ vec4 sample = sample_reflect_pdf(R, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_beckmann_pdf(a2, NH, NV, NL, VH, G1_V);
+
+ /* See reflect glossy */
+ out_radiance += NL * sample * brdf_pdf * fresnel;
+ }
+
+ /* transmission */
+ float eta = 1.0/ior;
+ if (dot(H, -I) < 0.0) {
+ H = -H;
+ eta = ior;
+ }
+
+ vec3 Tr = refract(I, H, eta);
+ NL = -dot(N, Tr);
+ if (NL > 0.0 && fresnel != 1.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = dot(N, H); /* cosTheta */
+ float VH = dot(-I, H);
+ float LH = dot(Tr, H);
+ float tmp = ior * VH + LH;
+ float Ht2 = tmp * tmp;
+
+ float pdf = pdf_beckmann_refract(Ht2, NH, NV, VH, LH, G1_V, a2, eta);
+
+ vec4 sample = sample_refract_pdf(Tr, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_beckmann_refract_pdf(a2, LH, NL, VH);
+
+ out_radiance += sample * brdf_pdf * (1 - fresnel);
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2);
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_glossy.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_glossy.glsl
new file mode 100644
index 00000000000..612f5a3fc13
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_glossy.glsl
@@ -0,0 +1,710 @@
+/* -------- Utils Functions --------- */
+
+vec3 sample_ggx(float nsample, float a2, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample);
+
+ float z = sqrt( (1.0 - Xi.x) / ( 1.0 + a2 * Xi.x - Xi.x ) ); /* cos theta */
+ float r = sqrt( 1.0 - z * z ); /* sin theta */
+ float x = r * Xi.y;
+ float y = r * Xi.z;
+
+ /* Global variable */
+ Ht = vec3(x, y, z);
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+vec3 sample_beckmann(float nsample, float a2, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample);
+
+ float z = sqrt( 1.0 / ( 1.0 - a2 * log(1.0 - Xi.x) ) ); /* cos theta */
+ float r = sqrt( 1.0 - z * z ); /* sin theta */
+ float x = r * Xi.y;
+ float y = r * Xi.z;
+
+ /* Global variable */
+ Ht = vec3(x, y, z);
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+vec3 sample_ashikhmin_shirley(float nsample, float n_x, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample);
+
+ float z = pow( Xi.x, 1.0 / (n_x + 1.0) ); /* cos theta */
+ float r = sqrt( 1.0 - z * z ); /* sin theta */
+ float x = r * Xi.y;
+ float y = r * Xi.z;
+
+ /* Global variable */
+ Ht = vec3(x, y, z);
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+float D_ggx_opti(float NH, float a2)
+{
+ float tmp = (NH * a2 - NH) * NH + 1.0;
+ return M_PI * tmp*tmp; /* Doing RCP and mul a2 at the end */
+}
+
+float D_beckman(float NH, float a2)
+{
+ float NH2 = NH * NH;
+ return exp((NH2 - 1) / (NH2 * a2)) / (M_PI * a2 * NH2 * NH2);
+}
+
+float pdf_ggx_reflect(float NH, float a2)
+{
+ return NH * a2 / D_ggx_opti(NH, a2);
+}
+
+float pdf_beckmann_reflect(float NH, float a2)
+{
+ return NH * D_beckman(NH, a2);
+}
+
+float pdf_ashikhmin_shirley_reflect(float NH, float VH, float n_x)
+{
+ float lobe = pow(NH, n_x);
+ float norm = (n_x + 1.0) * 0.125 * M_1_PI;
+
+ return norm * lobe / VH;
+}
+
+void prepare_glossy(float roughness, out float a, out float a2)
+{
+ /* Artifacts appear with roughness below this threshold */
+ /* XXX TODO : find why flooring is necessary */
+ a = clamp(roughness, 2e-4, 0.9999999);
+ a2 = max(1e-8, a*a);
+}
+
+float G1_Smith_GGX(float NX, float a2)
+{
+ /* Using Brian Karis approach and refactoring by NX/NX
+ * this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV
+ * Rcp is done on the whole G later
+ * Note that this is not convenient for the transmition formula */
+ return NX + sqrt( NX * (NX - NX * a2) + a2 );
+ /* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function */
+}
+
+float G1_Smith_beckmann(float NX, float a2)
+{
+ float tmp = 1 / (sqrt(a2 * (1 - NX * NX) / (NX * NX)));
+ return (tmp < 1.6) ? (3.535 * tmp + 2.181 * tmp * tmp) / (1 + 2.276 * tmp + 2.577 * tmp * tmp) : 1.0;
+}
+
+/* -------- BSDF --------- */
+
+float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
+{
+ float a, a2; prepare_glossy(roughness, a, a2);
+
+ vec3 H = normalize(L + V);
+ float NH = max(dot(N, H), 1e-8);
+ float NL = max(dot(N, L), 1e-8);
+ float NV = max(dot(N, V), 1e-8);
+
+ float G = G1_Smith_GGX(NV, a2) * G1_Smith_GGX(NL, a2); /* Doing RCP at the end */
+ float D = D_ggx_opti(NH, a2);
+
+ /* Denominator is canceled by G1_Smith */
+ /* bsdf = D * G / (4.0 * NL * NV); /* Reference function */
+ return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */
+}
+
+/* This one returns the brdf already divided by the pdf */
+float bsdf_ggx_pdf(float a2, float NH, float NL, float VH, float G1_V)
+{
+ float G = G1_V * G1_Smith_GGX(NL, a2); /* Doing RCP at the end */
+
+ /* Denominator is canceled by G1_Smith
+ * brdf = D * G / (4.0 * NL * NV) [denominator canceled by G]
+ * pdf = D * NH / (4 * VH) [D canceled later by D in brdf] */
+ return 4.0 * VH / (NH * G); /* brdf / pdf */
+}
+
+float bsdf_beckmann(vec3 N, vec3 L, vec3 V, float roughness)
+{
+ float a, a2; prepare_glossy(roughness, a, a2);
+
+ vec3 H = normalize(L + V);
+ float NH = max(dot(N, H), 1e-8);
+ float NL = max(dot(N, L), 1e-8);
+ float NV = max(dot(N, V), 1e-8);
+
+ float G = G1_Smith_beckmann(NV, a2) * G1_Smith_beckmann(NL, a2);
+ float D = D_beckman(NH, a2);
+
+ return NL * D * G * 0.25 / (NL * NV);
+}
+
+/* This one returns the brdf already divided by the pdf */
+float bsdf_beckmann_pdf(float a2, float NH, float NV, float NL, float VH, float G1_V)
+{
+ float G = G1_V * G1_Smith_beckmann(NL, a2);
+
+ /* brdf = D * G / (4.0 * NL * NV)
+ * pdf = D * NH / (4 * VH) [D canceled later by D in brdf] */
+ return G * VH / (NH * NV * NL); /* brdf / pdf */
+}
+
+float bsdf_ashikhmin_shirley(vec3 N, vec3 L, vec3 V, float roughness)
+{
+ float a, a2; prepare_glossy(roughness, a, a2);
+
+ vec3 H = normalize(L + V);
+ float NL = max(dot(N, L), 1e-6);
+ float NV = max(dot(N, V), 1e-6);
+ float NH = max(dot(N, H), 1e-6);
+ float VH = max(abs(dot(V, H)), 1e-6);
+
+ float pump = 1.0 / max(1e-6, VH * max(NL, NV));
+ float n_x = 2.0 / a2 - 2.0;
+ float lobe = pow(NH, n_x);
+ float norm = (n_x + 1.0f) * 0.125 * M_1_PI;
+
+ return NL * norm * lobe * pump;
+}
+
+/* This one returns the brdf already divided by the pdf */
+float bsdf_ashikhmin_shirley_pdf(float NV, float NL, float VH)
+{
+ float pump = 1.0 / max(1e-6, VH * max(NL, NV));
+
+ return VH * pump;
+}
+
+/* -------- Preview Lights --------- */
+
+void node_bsdf_glossy_lights(vec4 color, float roughness, vec3 N, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ vec3 accumulator = ambient_light.rgb;
+
+ if (roughness <= 1e-4) {
+ result = vec4(accumulator * color.rgb, 1.0);
+ return;
+ }
+
+ shade_view(V, V); V = -V;
+ N = normalize(N);
+
+ /* directional lights */
+ for(int i = 0; i < NUM_LIGHTS; i++) {
+ vec3 L = gl_LightSource[i].position.xyz;
+ vec3 light_color = gl_LightSource[i].specular.rgb;
+
+ accumulator += light_color * bsdf_ggx(N, L, V, roughness);
+ }
+
+ result = vec4(accumulator * color.rgb, 1.0);
+}
+
+/* -------- Physical Lights --------- */
+
+/* GLOSSY SHARP */
+
+void bsdf_glossy_sharp_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float l_radius = l_areasizex;
+ L = l_distance * L;
+ vec3 R = -reflect(V, N);
+
+ vec3 P = line_aligned_plane_intersect(vec3(0.0), R, L);
+ bsdf = (distance_squared(P, L) < l_radius * l_radius) ? 1.0 : 0.0;
+
+ /* Energy conservation + cycle matching */
+ bsdf *= sphere_energy(l_radius);
+}
+
+void bsdf_glossy_sharp_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (max(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ L = l_distance * L;
+
+ vec3 lampx, lampy, lampz;
+ vec2 halfsize = area_light_prepass(l_mat, l_areasizex, l_areasizey, l_areascale, lampx, lampy, lampz);
+
+ /* Find the intersection point E between the reflection vector and the light plane */
+ vec3 R = reflect(V, N);
+ vec3 E = line_plane_intersect(vec3(0.0), R, L, lampz);
+
+ /* Project it onto the light plane */
+ vec3 projection = E - L;
+ float A = dot(lampx, projection);
+ float B = dot(lampy, projection);
+
+ bsdf = (abs(A) < halfsize.x && abs(B) < halfsize.y) ? 1.0 : 0.0;
+
+ /* Masking */
+ bsdf *= (dot(-L, lampz) > 0.0) ? 1.0 : 0.0;
+ bsdf *= (dot(R, lampz) > 0.0) ? 1.0 : 0.0;
+
+ /* Energy conservation + cycle matching */
+ bsdf *= rectangle_energy(l_areasizex, l_areasizey);
+}
+
+void bsdf_glossy_sharp_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ vec3 R = reflect(V, N);
+
+ float l_radius = l_areasizex;
+ float angle = atan(l_radius);
+
+ float costheta = dot(-L, R);
+ float cosangle = cos(angle);
+
+ bsdf = (costheta > cosangle) ? 1.0 : 0.0;
+
+ /* Energy conservation + cycle matching */
+ bsdf *= disk_energy(l_radius);
+ bsdf /= costheta * costheta * costheta;
+ bsdf *= M_PI;
+}
+
+/* GLOSSY GGX */
+
+void bsdf_glossy_ggx_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+#if 1
+ /* MRP is twice as fast as LTC, does not exhibit crucial artifacts and is better looking */
+ float l_radius = l_areasizex;
+
+ shade_view(V, V);
+ vec3 R = reflect(V, N);
+
+ float energy_conservation = 1.0;
+ most_representative_point(l_radius, 0.0, vec3(0.0), l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_ggx(N, L, V, roughness);
+
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= sphere_energy(l_radius) * max(l_radius * l_radius, 1e-16); /* l_radius is already inside energy_conservation */
+ bsdf *= M_PI;
+#else
+ /* LTC */
+ float l_radius = max(0.007, l_areasizex);
+
+ vec3 pos = V;
+ shade_view(V, V);
+ vec3 R = -reflect(V, N);
+ L = l_distance * L;
+ V = -V;
+ N = -N;
+
+ vec3 P = line_aligned_plane_intersect(vec3(0.0), R, L);
+ vec3 Px = normalize(P - L) * l_radius;
+ vec3 Py = axis_angle_rotation(Px, R, M_PI_2);
+
+ vec3 points[4];
+ points[0] = l_coords + Px;
+ points[1] = l_coords - Py;
+ points[2] = l_coords - Px;
+ points[3] = l_coords + Py;
+
+ float NV = max(dot(N, V), 1e-8);
+ vec2 uv = ltc_coords(NV, sqrt(roughness));
+ mat3 ltcmat = ltc_matrix(uv);
+
+ bsdf = ltc_evaluate(N, V, pos, ltcmat, points);
+ bsdf *= texture2D(unfltcmag, uv).r; /* Bsdf matching */
+
+ bsdf *= M_1_2PI;
+ bsdf *= sphere_energy(l_radius);
+#endif
+}
+
+void bsdf_glossy_ggx_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (min(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ vec3 pos = V;
+ shade_view(V, V);
+ V = -V;
+ N = -N;
+
+ vec3 lampx, lampy, lampz;
+ vec2 halfsize = area_light_prepass(l_mat, l_areasizex, l_areasizey, l_areascale, lampx, lampy, lampz);
+
+ vec3 points[4];
+ area_light_points(l_coords, halfsize, lampx, lampy, points);
+
+ float NV = max(dot(N, V), 1e-8);
+ vec2 uv = ltc_coords(NV, sqrt(roughness));
+ mat3 ltcmat = ltc_matrix(uv);
+
+ bsdf = ltc_evaluate(N, V, pos, ltcmat, points);
+ bsdf *= texture2D(unfltcmag, uv).r; /* Bsdf matching */
+
+ bsdf *= step(0.0, -dot(L, lampz));
+ bsdf *= M_1_2PI;
+ bsdf *= rectangle_energy(l_areasizex, l_areasizey);
+}
+
+void bsdf_glossy_ggx_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ /* Correct ligth shape but uniform intensity
+ * Does not take into account the division by costheta^3 */
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ vec3 R = reflect(V, N);
+
+ float l_radius = l_areasizex;
+ float angle = atan(l_radius);
+
+ float costheta = dot(-L, R);
+ float cosangle = cos(angle);
+
+ float energy_conservation = 1.0;
+ most_representative_point_disk(l_radius, 1.0, R, L, roughness, energy_conservation);
+
+ bsdf = bsdf_ggx(N, L, V, roughness);
+ bsdf *= energy_conservation;
+}
+
+/* GLOSSY BECKMANN */
+
+void bsdf_glossy_beckmann_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ /* MRP is twice as fast as LTC, does not exhibit crucial artifacts and is better looking */
+ float l_radius = l_areasizex;
+
+ shade_view(V, V);
+ vec3 R = reflect(V, N);
+
+ float energy_conservation = 1.0; /* XXX TODO : Energy conservation is done for GGX */
+ most_representative_point(l_radius, 0.0, vec3(0.0), l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_beckmann(N, L, V, roughness);
+
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= sphere_energy(l_radius) * max(l_radius * l_radius, 1e-16); /* l_radius is already inside energy_conservation */
+ bsdf *= M_PI;
+}
+
+void bsdf_glossy_beckmann_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ /* TODO Make the other ltc luts */
+ bsdf_glossy_ggx_area_light( N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale,
+ l_mat, roughness, ior, sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, bsdf);
+}
+
+void bsdf_glossy_beckmann_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ /* Correct ligth shape but uniform intensity
+ * Does not take into account the division by costheta^3 */
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ vec3 R = reflect(V, N);
+
+ float l_radius = l_areasizex;
+ float angle = atan(l_radius);
+
+ float costheta = dot(-L, R);
+ float cosangle = cos(angle);
+
+ float energy_conservation = 1.0;
+ most_representative_point_disk(l_radius, 1.0, R, L, roughness, energy_conservation);
+
+ bsdf = bsdf_beckmann(N, L, V, roughness);
+ bsdf *= energy_conservation;
+}
+
+/* GLOSSY ASHIKhMIN SHIRLEY */
+
+void bsdf_glossy_ashikhmin_shirley_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ /* MRP is twice as fast as LTC, does not exhibit crucial artifacts and is better looking */
+ float l_radius = l_areasizex;
+
+ shade_view(V, V);
+ vec3 R = reflect(V, N);
+
+ float energy_conservation = 1.0; /* XXX TODO : Energy conservation is done for GGX */
+ most_representative_point(l_radius, 0.0, vec3(0.0), l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_ashikhmin_shirley(N, L, V, roughness);
+
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= sphere_energy(l_radius) * max(l_radius * l_radius, 1e-16); /* l_radius is already inside energy_conservation */
+ bsdf *= M_PI;
+}
+
+void bsdf_glossy_ashikhmin_shirley_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ /* TODO Make the other ltc luts */
+ bsdf_glossy_ggx_area_light( N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale,
+ l_mat, roughness, ior, sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, bsdf);
+}
+
+void bsdf_glossy_ashikhmin_shirley_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ /* Correct ligth shape but uniform intensity
+ * Does not take into account the division by costheta^3 */
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ vec3 R = reflect(V, N);
+
+ float l_radius = l_areasizex;
+ float angle = atan(l_radius);
+
+ float costheta = dot(-L, R);
+ float cosangle = cos(angle);
+
+ float energy_conservation = 1.0;
+ most_representative_point_disk(l_radius, 1.0, R, L, roughness, energy_conservation);
+
+ bsdf = bsdf_ashikhmin_shirley(N, L, V, roughness);
+ bsdf *= energy_conservation;
+}
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_glossy_sharp(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+
+ /* Precomputation */
+ vec3 L = reflect(I, N);
+ vec3 vL = (viewmat * vec4(L, 0.0)).xyz;
+
+ /* Probe */
+ vec4 sample_probe = sample_reflect(L) * specular_occlusion(dot(N, -I), ao_factor, 0.0);
+
+#ifdef USE_SSR
+ /* SSR */
+ vec2 hitpixel; vec3 hitco; float hitstep;
+
+ bool hit = raycast(viewpos, vL, hitstep, hitpixel, hitco);
+ float contrib = ssr_contribution(viewpos, hitstep, hit, hitco);
+
+ vec4 sample_ssr = bufferFetch(unfscenebuf, ivec2(hitpixel), 0);
+ srgb_to_linearrgb(sample_ssr, sample_ssr);
+
+ result = mix(sample_probe.rgb, sample_ssr.rgb, contrib);
+ //result = mix(vec3(1.0,0.0,0.0), vec3(0.0,1.0,0.0), hit);
+ //result = sample_ssr.rgb * float(hit);
+ // result = hitco;
+//result = vL.rgb;
+ //result = vec3(frontface_depth(ivec2(gl_FragCoord.xy), 0));
+#else
+ result = sample_probe.rgb;
+#endif
+}
+
+void env_sampling_glossy_ggx(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+ float a, a2; prepare_glossy(roughness, a, a2);
+
+ /* Precomputation */
+ float NV = max(1e-8, abs(dot(I, N)));
+ float G1_V = G1_Smith_GGX(NV, a2);
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_ggx(i, a2, N, T, B); /* Microfacet normal */
+ vec3 L = reflect(I, H);
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = max(1e-8, dot(N, H)); /* cosTheta */
+ float VH = max(1e-8, -dot(I, H));
+
+ float pdf = pdf_ggx_reflect(NH, a2);
+
+ vec4 sample = sample_reflect_pdf(L, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_ggx_pdf(a2, NH, NL, VH, G1_V);
+
+ out_radiance += NL * sample * brdf_pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2);
+}
+
+void env_sampling_glossy_beckmann(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+ float a, a2; prepare_glossy(roughness, a, a2);
+
+ /* Precomputation */
+ float NV = max(1e-8, abs(dot(I, N)));
+ float G1_V = G1_Smith_beckmann(NV, a2);
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_beckmann(i, a2, N, T, B); /* Microfacet normal */
+ vec3 L = reflect(I, H);
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = max(1e-8, dot(N, H)); /* cosTheta */
+ float VH = max(1e-8, -dot(I, H));
+
+ float pdf = pdf_beckmann_reflect(NH, a2);
+
+ vec4 sample = sample_reflect_pdf(L, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_beckmann_pdf(a2, NH, NV, NL, VH, G1_V);
+
+ out_radiance += NL * sample * brdf_pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2);
+}
+
+void env_sampling_glossy_ashikhmin_shirley(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+ float a, a2; prepare_glossy(roughness, a, a2);
+
+ /* Precomputation */
+ float NV = max(1e-8, abs(dot(I, N)));
+ float n_x = 2.0 / a2 - 2.0;
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_ashikhmin_shirley(i, n_x, N, T, B); /* Microfacet normal */
+ float VH = dot(H, -I);
+ if (VH < 0.0) H = -H;
+ /* reflect I on H to get omega_in */
+ vec3 L = I + (2.0 * VH) * H;
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = max(1e-8, dot(N, H)); /* cosTheta */
+ VH = max(1e-8, abs(VH));
+ NL = max(1e-8, NL);
+
+ float pdf = pdf_ashikhmin_shirley_reflect(NH, VH, n_x);
+
+ vec4 sample = sample_reflect_pdf(L, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_ashikhmin_shirley_pdf(NV, NL, VH);
+
+ out_radiance += NL * sample * brdf_pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2);
+}
+
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_refraction.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_refraction.glsl
new file mode 100644
index 00000000000..aeecb56696a
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_refraction.glsl
@@ -0,0 +1,490 @@
+/* -------- Utils Functions --------- */
+
+float pdf_ggx_refract(float Ht2, float NH, float NV, float VH, float LH, float G1_V, float a2, float eta)
+{
+ return (VH * abs(LH)) * (eta*eta) * a2 * G1_V / (Ht2 * NV * D_ggx_opti(NH, a2));
+}
+
+float pdf_beckmann_refract(float Ht2, float NH, float NV, float VH, float LH, float G1_V, float a2, float eta)
+{
+ /* pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */
+ return (VH * abs(LH)) * (eta*eta) * D_beckman(NH, a2) * G1_V / (Ht2 * NV);
+}
+
+/* -------- BSDF --------- */
+
+float bsdf_ggx_refract(float Ht2, float NH, float NL, float NV, float VH, float LH, float a2, float eta)
+{
+ float G = G1_Smith_GGX(NV, a2) * G1_Smith_GGX(NL, a2);
+ float D = D_ggx_opti(NH, a2); /* Doing RCP and mul a2 at the end */
+
+ /* bsdf = abs(LH * VH) * (eta*eta) * G * D / (NV * Ht2); /* Reference function */
+ return abs(LH * VH) * (NV * NL * 4.0) * a2 * (eta*eta) / (D * G * NV * Ht2); /* Balancing the adjustments made in G1_Smith with (NV * NL * 4.0)*/
+}
+
+float bsdf_ggx_refract(vec3 N, vec3 L, vec3 V, float eta, float roughness)
+{
+ /* GGX Spec Isotropic Transmited */
+ float a, a2; prepare_glossy(roughness, a, a2);
+
+ vec3 ht = -(eta * L + V);
+ vec3 Ht = normalize(ht);
+ float Ht2 = dot(ht, ht);
+ float NH = dot(N, Ht);
+ float NL = dot(N, -L);
+ float NV = dot(N, V);
+ float VH = dot(V, Ht);
+ float LH = dot(-L, Ht);
+
+ return bsdf_ggx_refract(Ht2, NH, NL, NV, VH, LH, a2, eta);
+}
+
+/* This one returns the brdf already divided by the pdf */
+float bsdf_ggx_refract_pdf(float a2, float LH, float NL, float VH)
+{
+ float G1_L = NL * 2.0 / G1_Smith_GGX(NL, a2); /* Balancing the adjustments made in G1_Smith */
+
+ /* brdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV)
+ * pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */
+ return G1_L * abs(VH*LH) / (VH * abs(LH));
+}
+
+float bsdf_beckmann_refract(float Ht2, float NH, float NL, float NV, float VH, float LH, float a, float a2, float eta)
+{
+ float G = G1_Smith_beckmann(NV, a2) * G1_Smith_beckmann(NL, a2);
+ float D = D_beckman(NH, a2);
+
+ return abs(LH * VH) * D * G * (eta*eta) / (NV * Ht2);
+}
+
+float bsdf_beckmann_refract(vec3 N, vec3 L, vec3 V, float eta, float roughness)
+{
+ float a, a2; prepare_glossy(roughness, a, a2);
+
+ vec3 ht = -(eta * L + V);
+ vec3 Ht = normalize(ht);
+ float Ht2 = dot(ht, ht);
+ float NH = dot(N, Ht);
+ float NL = dot(N, -L);
+ float NV = dot(N, V);
+ float VH = dot(V, Ht);
+ float LH = dot(-L, Ht);
+
+ return bsdf_beckmann_refract(Ht2, NH, NL, NV, VH, LH, a, a2, eta);
+}
+
+/* This one returns the brdf already divided by the pdf */
+float bsdf_beckmann_refract_pdf(float a2, float LH, float NL, float VH)
+{
+ /* brdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV)
+ * pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */
+ return G1_Smith_beckmann(NL, a2) * abs(VH*LH) / (VH * abs(LH));
+}
+
+/* -------- Preview Lights --------- */
+
+void node_bsdf_refraction_lights(vec4 color, float roughness, float ior, vec3 N, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ node_bsdf_glossy_lights(color, roughness, N, V, ambient_light, result);
+}
+
+/* -------- Physical Lights --------- */
+
+/* REFRACT SHARP */
+
+void bsdf_refract_sharp_sphere_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float l_radius = l_areasizex;
+ L = l_distance * L;
+ vec3 R = -refract(-V, N, (gl_FrontFacing) ? 1.0/ior : ior);
+
+ vec3 P = line_aligned_plane_intersect(vec3(0.0), R, L);
+ bsdf = (distance_squared(P, L) < l_radius * l_radius) ? 1.0 : 0.0;
+
+ /* Energy conservation + cycle matching */
+ bsdf *= sphere_energy(l_radius);
+}
+
+void bsdf_refract_sharp_area_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (max(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ L = l_distance * L;
+
+ vec3 lampx, lampy, lampz;
+ vec2 halfsize = area_light_prepass(l_mat, l_areasizex, l_areasizey, l_areascale, lampx, lampy, lampz);
+
+ /* Find the intersection point E between the reflection vector and the light plane */
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ vec3 R = refract(-V, N, 1.0/eta);
+ vec3 E = line_plane_intersect(vec3(0.0), R, L, lampz);
+
+ /* Project it onto the light plane */
+ vec3 projection = E - L;
+ float A = dot(lampx, projection);
+ float B = dot(lampy, projection);
+
+ bsdf = (abs(A) < halfsize.x && abs(B) < halfsize.y) ? 1.0 : 0.0;
+
+ /* Masking */
+ bsdf *= (dot(-L, lampz) > 0.0) ? 1.0 : 0.0;
+ bsdf *= (dot(-R, lampz) > 0.0) ? 1.0 : 0.0;
+
+ /* Energy conservation + cycle matching */
+ bsdf *= rectangle_energy(l_areasizex, l_areasizey);
+}
+
+void bsdf_refract_sharp_sun_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ vec3 R = refract(-V, N, 1.0/eta);
+
+ float l_radius = l_areasizex;
+ float angle = atan(l_radius);
+
+ float costheta = dot(L, R);
+ float cosangle = cos(angle);
+
+ bsdf = (costheta > cosangle) ? 1.0 : 0.0;
+
+ /* Energy conservation + cycle matching */
+ bsdf *= disk_energy(l_radius);
+ bsdf /= costheta * costheta * costheta;
+ bsdf *= M_PI;
+}
+
+/* REFRACT GGX */
+
+void bsdf_refract_ggx_sphere_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float l_radius = l_areasizex;
+
+ shade_view(V, V);
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ vec3 R = refract(-V, N, 1.0/eta);
+
+ float energy_conservation = 1.0;
+ most_representative_point(l_radius, 0.0, vec3(0.0), l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_ggx_refract(N, L, V, eta, roughness);
+
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= sphere_energy(l_radius) * max(l_radius * l_radius, 1e-16); /* l_radius is already inside energy_conservation */
+ bsdf *= M_PI; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_refract_ggx_area_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (min(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ l_areasizex *= l_areascale.x;
+ l_areasizey *= l_areascale.y;
+
+ /* Used later for Masking : Use the real Light Vector */
+ vec3 lampz = normalize( (l_mat * vec4(0.0,0.0,1.0,0.0) ).xyz ); //lamp projection axis
+ float masking = max(dot( normalize(-L), lampz), 0.0);
+
+ float max_size = max(l_areasizex, l_areasizey);
+ float min_size = min(l_areasizex, l_areasizey);
+ vec3 lampVec = (l_areasizex > l_areasizey) ? normalize( (l_mat * vec4(1.0,0.0,0.0,0.0) ).xyz ) : normalize( (l_mat * vec4(0.0,1.0,0.0,0.0) ).xyz );
+
+
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ vec3 R = refract(-V, N, 1.0/eta);
+ float energy_conservation = 1.0;
+ most_representative_point(min_size/2, max_size-min_size, lampVec, l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_ggx_refract(N, L, V, eta, roughness);
+
+ /* energy_conservation */
+ float LineAngle = clamp( (max_size-min_size) / l_distance, 0.0, 1.0);
+ float energy_conservation_line = energy_conservation * ( roughness / clamp(roughness + 0.5 * LineAngle, 0.0, 1.1));
+
+ /* XXX : Empirical modification for low roughness matching */
+ float energy_conservation_mod = energy_conservation * (1 + roughness) / ( max_size/min_size );
+ energy_conservation = mix(energy_conservation_mod, energy_conservation_line, min(roughness/0.3, 0.9*(1.1-roughness)/0.1));
+
+ /* As we represent the Area Light by a tube light we must use a custom energy conservation */
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= masking;
+ bsdf *= 23.2; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_refract_ggx_sun_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ /* Correct ligth shape but uniform intensity
+ * Does not take into account the division by costheta^3 */
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ vec3 R = refract(-V, N, 1.0/eta);
+
+ float l_radius = l_areasizex;
+
+ float energy_conservation = 1.0;
+ most_representative_point_disk(l_radius, 1.0, R, L, roughness, energy_conservation);
+
+ bsdf = bsdf_ggx_refract(N, L, V, eta, roughness);
+ bsdf *= energy_conservation;
+}
+
+/* REFRACT BECKMANN */
+
+void bsdf_refract_beckmann_sphere_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float l_radius = l_areasizex;
+
+ shade_view(V, V);
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ vec3 R = refract(-V, N, 1.0/eta);
+
+ float energy_conservation = 1.0;
+ most_representative_point(l_radius, 0.0, vec3(0.0), l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_beckmann_refract(N, L, V, eta, roughness);
+
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= sphere_energy(l_radius) * max(l_radius * l_radius, 1e-16); /* l_radius is already inside energy_conservation */
+ bsdf *= M_PI; /* XXX : !!! Very Empirical, Fit cycles power */
+ bsdf *= step(0.0, dot(-L, N));
+}
+
+void bsdf_refract_beckmann_area_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (min(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ l_areasizex *= l_areascale.x;
+ l_areasizey *= l_areascale.y;
+
+ /* Used later for Masking : Use the real Light Vector */
+ vec3 lampz = normalize( (l_mat * vec4(0.0,0.0,1.0,0.0) ).xyz ); //lamp projection axis
+ float masking = max(dot( normalize(-L), lampz), 0.0);
+
+ float max_size = max(l_areasizex, l_areasizey);
+ float min_size = min(l_areasizex, l_areasizey);
+ vec3 lampVec = (l_areasizex > l_areasizey) ? normalize( (l_mat * vec4(1.0,0.0,0.0,0.0) ).xyz ) : normalize( (l_mat * vec4(0.0,1.0,0.0,0.0) ).xyz );
+
+
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ vec3 R = refract(-V, N, 1.0/eta);
+ float energy_conservation = 1.0;
+ most_representative_point(min_size/2, max_size-min_size, lampVec, l_distance, R, L, roughness, energy_conservation);
+ bsdf = bsdf_beckmann_refract(N, L, V, eta, roughness);
+
+ /* energy_conservation */
+ float LineAngle = clamp( (max_size-min_size) / l_distance, 0.0, 1.0);
+ float energy_conservation_line = energy_conservation * ( roughness / clamp(roughness + 0.5 * LineAngle, 0.0, 1.1));
+
+ /* XXX : Empirical modification for low roughness matching */
+ float energy_conservation_mod = energy_conservation * (1 + roughness) / ( max_size/min_size );
+ energy_conservation = mix(energy_conservation_mod, energy_conservation_line, min(roughness/0.3, 0.9*(1.1-roughness)/0.1));
+
+ /* As we represent the Area Light by a tube light we must use a custom energy conservation */
+ bsdf *= energy_conservation / (l_distance * l_distance);
+ bsdf *= masking;
+ bsdf *= step(0.0, dot( normalize(-L), N));
+ bsdf *= 23.2; /* XXX : !!! Very Empirical, Fit cycles power */
+}
+
+void bsdf_refract_beckmann_sun_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ /* Correct ligth shape but uniform intensity
+ * Does not take into account the division by costheta^3 */
+ if (roughness < 1e-4 && l_areasizex == 0) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float eta = (gl_FrontFacing) ? ior : 1.0/ior;
+ vec3 R = refract(-V, N, 1.0/eta);
+
+ float l_radius = l_areasizex;
+
+ float energy_conservation = 1.0;
+ most_representative_point_disk(l_radius, 1.0, R, L, roughness, energy_conservation);
+
+ bsdf = bsdf_beckmann_refract(N, L, V, eta, roughness);
+ bsdf *= energy_conservation;
+ bsdf *= step(0.0, dot(-L, N));
+}
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_refract_sharp(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ float eta = (gl_FrontFacing) ? 1.0/ior : ior;
+
+ vec3 L = refract(I, N, eta);
+
+ result = sample_refract(L).rgb;
+}
+
+void env_sampling_refract_ggx(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+ float a, a2; prepare_glossy(roughness, a, a2);
+ ior = max(ior, 1e-5);
+
+ /* Precomputation */
+ float NV = max(1e-8, abs(dot(I, N)));
+ float G1_V = G1_Smith_GGX(NV, a2);
+
+ /* Early out */
+ if (abs(ior - 1.0) < 1e-4) {
+ result = sample_transparent();
+ return;
+ }
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_ggx(i, a2, N, T, B); /* Microfacet normal */
+ float eta = 1.0/ior;
+ float fresnel = fresnel_dielectric(I, H, ior);
+
+ if (dot(H, -I) < 0.0) {
+ H = -H;
+ eta = ior;
+ }
+
+ vec3 L = refract(I, H, eta);
+ float NL = -dot(N, L);
+ if (NL > 0.0 && fresnel != 1.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = dot(N, H); /* cosTheta */
+ float VH = dot(-I, H);
+ float LH = dot(L, H);
+ float tmp = ior * VH + LH;
+ float Ht2 = tmp * tmp;
+
+ float pdf = pdf_ggx_refract(Ht2, NH, NV, VH, LH, G1_V, a2, eta);
+
+ vec4 sample = sample_refract_pdf(L, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_ggx_refract_pdf(a2, LH, NL, VH);
+
+ out_radiance += sample * brdf_pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y;
+}
+
+void env_sampling_refract_beckmann(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+ float a, a2; prepare_glossy(roughness, a, a2);
+ ior = max(ior, 1e-5);
+
+ /* Precomputation */
+ float NV = max(1e-8, abs(dot(I, N)));
+ float G1_V = G1_Smith_GGX(NV, a2);
+
+ /* Early out */
+ if (abs(ior - 1.0) < 1e-4) {
+ result = sample_transparent();
+ return;
+ }
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 H = sample_beckmann(i, a2, N, T, B); /* Microfacet normal */
+ float eta = 1.0/ior;
+ float fresnel = fresnel_dielectric(I, H, ior);
+
+ if (dot(H, -I) < 0.0) {
+ H = -H;
+ eta = ior;
+ }
+
+ vec3 L = refract(I, H, eta);
+ float NL = -dot(N, L);
+ if (NL > 0.0 && fresnel != 1.0) {
+ /* Step 1 : Sampling Environment */
+ float NH = dot(N, H); /* cosTheta */
+ float VH = dot(-I, H);
+ float LH = dot(L, H);
+ float tmp = ior * VH + LH;
+ float Ht2 = tmp * tmp;
+
+ float pdf = pdf_beckmann_refract(Ht2, NH, NV, VH, LH, G1_V, a2, eta);
+
+ vec4 sample = sample_refract_pdf(L, roughness, pdf);
+
+ /* Step 2 : Integrating BRDF */
+ float brdf_pdf = bsdf_beckmann_refract_pdf(a2, LH, NL, VH);
+
+ out_radiance += sample * brdf_pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y;
+} \ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_toon.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_toon.glsl
new file mode 100644
index 00000000000..7976429b3a4
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_toon.glsl
@@ -0,0 +1,273 @@
+/* -------- Utils Functions --------- */
+
+vec3 sample_uniform_cone(float nsample, float angle, vec3 N, vec3 T, vec3 B)
+{
+ vec3 Xi = hammersley_3d(nsample);
+
+ float z = cos(angle * Xi.x);
+ float r = sqrt(1.0 - z*z);
+ float x = r * Xi.y;
+ float y = r * Xi.z;
+
+ Ht = vec3(x, y, z); /* Global variable */
+
+ return from_tangent_to_world(Ht, N, T, B);
+}
+
+float pdf_uniform_cone(float angle)
+{
+ return 0.5 * M_1_PI / (1.0 - cos(angle));
+}
+
+void prepare_toon(inout float tsize, inout float tsmooth, out float sample_angle)
+{
+ tsize = saturate(tsize) * M_PI_2;
+ tsmooth = saturate(tsmooth) * M_PI_2;
+ sample_angle = min(tsize + tsmooth, M_PI_2);
+}
+
+float bsdf_toon_get_intensity(float max_angle, float tsmooth, float angle)
+{
+ if(angle < max_angle)
+ return 1.0;
+ else if(angle < (max_angle + tsmooth) && tsmooth != 0.0)
+ return (1.0 - (angle - max_angle)/tsmooth);
+ else
+ return 0.0;
+}
+
+/* -------- BSDF --------- */
+
+float bsdf_toon(float cosangle, float pdf, float max_angle, float tsmooth)
+{
+ float angle = acos(min(0.9999999, cosangle));
+ float eval = bsdf_toon_get_intensity(max_angle, tsmooth, angle);
+ return pdf * eval;
+}
+
+float bsdf_toon_diffuse(vec3 N, vec3 L, float sample_angle, float max_angle, float tsmooth)
+{
+ float NL = max(0.0, dot(N, L));
+
+ if(NL > 0.0) {
+ float pdf = pdf_uniform_cone(sample_angle);
+ return bsdf_toon(NL, pdf, max_angle, tsmooth);
+ }
+
+ return 0.0;
+}
+
+float bsdf_toon_glossy(vec3 N, vec3 L, vec3 V, float sample_angle, float max_angle, float tsmooth)
+{
+ float NL = max(0.0, dot(N, L));
+ float NV = max(0.0, dot(N, V));
+
+ if(NV > 0.0 && NL > 0.0) {
+ vec3 R = -reflect(V, N);
+ float RL = max(0.0, dot(R, L));
+ float pdf = pdf_uniform_cone(sample_angle);
+ return bsdf_toon(RL, pdf, max_angle, tsmooth);
+ }
+
+ return 0.0;
+}
+
+
+/* -------- Preview Lights --------- */
+
+void node_bsdf_toon_diffuse_lights(vec4 color, float size, float tsmooth, vec3 N, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ shade_view(V, V); V = -V;
+ N = normalize(N);
+ float sample_angle; prepare_toon(size, tsmooth, sample_angle);
+
+ /* ambient light */
+ vec3 accumulator = ambient_light.rgb;
+
+ /* directional lights */
+ for(int i = 0; i < NUM_LIGHTS; i++) {
+ vec3 L = gl_LightSource[i].position.xyz;
+ vec3 light_color = gl_LightSource[i].diffuse.rgb;
+
+ accumulator += light_color * bsdf_toon_diffuse(N, L, sample_angle, size, tsmooth) * M_PI; /* M_PI to make preview brighter */
+ }
+
+ result = vec4(accumulator*color.rgb, 1.0);
+}
+
+void node_bsdf_toon_glossy_lights(vec4 color, float size, float tsmooth, vec3 N, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ shade_view(V, V); V = -V;
+ N = normalize(N);
+ float sample_angle; prepare_toon(size, tsmooth, sample_angle);
+
+ /* ambient light */
+ vec3 accumulator = ambient_light.rgb;
+
+ /* directional lights */
+ for(int i = 0; i < NUM_LIGHTS; i++) {
+ vec3 L = gl_LightSource[i].position.xyz;
+ vec3 light_color = gl_LightSource[i].specular.rgb;
+
+ accumulator += light_color * bsdf_toon_glossy(N, L, V, sample_angle, size, tsmooth) * M_PI; /* M_PI to make preview brighter */
+ }
+
+ result = vec4(accumulator*color.rgb, 1.0);
+}
+
+
+/* -------- Physical Lights --------- */
+
+/* TOON DIFFUSE */
+
+void bsdf_toon_diffuse_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float sample_angle; prepare_toon(toon_size, toon_smooth, sample_angle);
+ bsdf = bsdf_toon_diffuse(N, L, sample_angle, toon_size, toon_smooth);
+
+ /* Energy conservation + cycle matching */
+ bsdf *= 8.0 / (l_distance * l_distance);
+}
+
+void bsdf_toon_diffuse_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float sample_angle; prepare_toon(toon_size, toon_smooth, sample_angle);
+ bsdf = bsdf_toon_diffuse(N, L, sample_angle, toon_size, toon_smooth);
+
+ /* Energy conservation + cycle matching */
+ bsdf *= 8.0 / (l_distance * l_distance);
+}
+
+void bsdf_toon_diffuse_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float sample_angle; prepare_toon(toon_size, toon_smooth, sample_angle);
+ bsdf = bsdf_toon_diffuse(N, L, sample_angle, toon_size, toon_smooth);
+}
+
+/* TOON GLOSSY */
+
+void bsdf_toon_glossy_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float sample_angle; prepare_toon(toon_size, toon_smooth, sample_angle);
+ bsdf = bsdf_toon_glossy(N, L, V, sample_angle, toon_size, toon_smooth);
+}
+
+void bsdf_toon_glossy_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float sample_angle; prepare_toon(toon_size, toon_smooth, sample_angle);
+ bsdf = bsdf_toon_glossy(N, L, V, sample_angle, toon_size, toon_smooth);
+
+ /* Energy conservation + cycle matching */
+ bsdf *= 8.0 / (l_distance * l_distance);
+}
+
+void bsdf_toon_glossy_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float sample_angle; prepare_toon(toon_size, toon_smooth, sample_angle);
+ bsdf = bsdf_toon_glossy(N, L, V, sample_angle, toon_size, toon_smooth);
+}
+
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_toon_diffuse(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ float sample_angle; prepare_toon(toon_size, toon_smooth, sample_angle);
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ /* Precomputation */
+ float NV = max(0.0, dot(I, N));
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 L = sample_uniform_cone(i, sample_angle, N, T, B);
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ /* Step 1 : Sampling Environment */
+ float pdf = pdf_uniform_cone(sample_angle);
+ vec4 irradiance = sample_probe_pdf(L, pdf);
+
+ /* Step 2 : Integrating BRDF*/
+ float brdf = bsdf_toon(NL, pdf, toon_size, toon_smooth);
+
+ out_radiance += irradiance * brdf / pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * ao_factor;
+}
+
+void env_sampling_toon_glossy(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ /* Setup */
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ float sample_angle; prepare_toon(toon_size, toon_smooth, sample_angle);
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ /* Precomputation */
+ float NV = max(1e-8, dot(I, N));
+ vec3 R = reflect(I, N);
+
+ /* We are sampling aroung R so generate basis with it */
+ make_orthonormals(R, T, B); /* Generate tangent space */
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 L = sample_uniform_cone(i, sample_angle, R, T, B);
+
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ float RL = max(0.0, dot(R, L));
+
+ /* Step 1 : Sampling Environment */
+ float pdf = pdf_uniform_cone(sample_angle);
+ vec4 irradiance = sample_reflect_pdf(L, 0.0, pdf);
+
+ /* Step 2 : Integrating BRDF*/
+ float brdf = bsdf_toon(RL, pdf, toon_size, toon_smooth);
+
+ out_radiance += irradiance * brdf / pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, roughness);
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_translucent.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_translucent.glsl
new file mode 100644
index 00000000000..4cec442da9a
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_translucent.glsl
@@ -0,0 +1,55 @@
+/* -------- Utils Functions --------- */
+
+/* -------- BSDF --------- */
+
+/* -------- Preview Lights --------- */
+
+void node_bsdf_translucent_lights(vec4 color, vec3 N, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ node_bsdf_diffuse_lights(color, 0.0, -N, V, ambient_light, result);
+}
+
+
+/* -------- Physical Lights --------- */
+
+void bsdf_translucent_sphere_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ bsdf_diffuse_sphere_light(-N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, 0.0, ior, sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, bsdf);
+}
+
+void bsdf_translucent_area_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ bsdf_diffuse_area_light(-N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, 0.0, ior, sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, bsdf);
+}
+
+void bsdf_translucent_sun_light(
+ vec3 N, vec3 T, vec3 L, vec3 V,
+ vec3 l_coords, float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ bsdf_diffuse_sun_light(-N, T, L, V, l_coords, l_distance, l_areasizex, l_areasizey, l_areascale, l_mat, 0.0, ior, sigma, toon_size, toon_smooth, anisotropy, aniso_rotation, bsdf);
+}
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_translucent(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ env_sampling_diffuse(
+ pbr, viewpos, invviewmat, viewmat,
+ -N, T, 0.0, ior, sigma,
+ toon_size, toon_smooth, anisotropy, aniso_rotation,
+ ao_factor, result);
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_transparent.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_transparent.glsl
new file mode 100644
index 00000000000..a4f1725bbf7
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_transparent.glsl
@@ -0,0 +1,30 @@
+/* -------- Utils Functions --------- */
+
+vec3 sample_transparent()
+{
+#ifndef PLANAR_PROBE
+ vec4 sample = textureCube(unfprobe, I);
+#else
+ vec4 sample = texture2D(unfrefract, refpos.xy);
+#endif
+ srgb_to_linearrgb(sample, sample);
+ return sample.rgb;
+}
+
+/* -------- BSDF --------- */
+
+/* -------- Preview Lights --------- */
+
+/* -------- Physical Lights --------- */
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_transparent(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ result = sample_transparent();
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_velvet.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_velvet.glsl
new file mode 100644
index 00000000000..553627edead
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_velvet.glsl
@@ -0,0 +1,156 @@
+/* -------- Utils Functions --------- */
+
+void prepare_velvet(float sigma, out float m_1_sig2)
+{
+ sigma = max(sigma, 1e-2);
+ m_1_sig2 = 1 / (sigma * sigma);
+}
+
+/* -------- BSDF --------- */
+
+float bsdf_ashikhmin_velvet(float NL, float NV, float NH, float VH, float m_1_sig2)
+{
+ float NHdivVH = NH / VH;
+ NHdivVH = max(NHdivVH, 1e-5);
+
+ float fac1 = 2 * abs(NHdivVH * NV);
+ float fac2 = 2 * abs(NHdivVH * NL);
+
+ float sinNH2 = 1 - NH * NH;
+ float sinNH4 = sinNH2 * sinNH2;
+ float cotan2 = (NH * NH) / sinNH2;
+
+ float D = exp(-cotan2 * m_1_sig2) * m_1_sig2 * M_1_PI / sinNH4;
+ float G = min(1.0, min(fac1, fac2)); // TODO: derive G from D analytically
+
+ return 0.25 * (D * G) / NV;
+}
+
+float bsdf_ashikhmin_velvet(vec3 N, vec3 L, vec3 V, float m_1_sig2)
+{
+ vec3 H = normalize(L + V);
+
+ float NL = max(dot(N, L), 0.0);
+ float NV = max(dot(N, V), 1e-5);
+ float NH = dot(N, H);
+ float VH = max(abs(dot(V, H)), 1e-5);
+
+ if(abs(NH) < 1.0-1e-5) {
+ return bsdf_ashikhmin_velvet(NL, NV, NH, VH, m_1_sig2);
+ }
+
+ return 0.0;
+}
+
+/* -------- Preview Lights --------- */
+
+void node_bsdf_velvet_lights(vec4 color, float sigma, vec3 N, vec3 V, vec4 ambient_light, out vec4 result)
+{
+ shade_view(V, V); V = -V;
+ float m_1_sig2; prepare_velvet(sigma, m_1_sig2);
+
+ /* ambient light */
+ vec3 accumulator = ambient_light.rgb;
+
+ /* directional lights */
+ for(int i = 0; i < NUM_LIGHTS; i++) {
+ vec3 L = gl_LightSource[i].position.xyz;
+ vec3 light_color = gl_LightSource[i].specular.rgb;
+
+ accumulator += light_color * bsdf_ashikhmin_velvet(N, L, V, m_1_sig2);
+ }
+
+ result = vec4(accumulator * color.rgb, 1.0);
+}
+
+/* -------- Physical Lights --------- */
+
+/* VELVET */
+
+void bsdf_velvet_sphere_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float m_1_sig2; prepare_velvet(sigma, m_1_sig2);
+
+ bsdf = bsdf_ashikhmin_velvet(N, L, V, m_1_sig2);
+
+ /* Energy conservation + cycle matching */
+ bsdf *= 8.0 / (l_distance * l_distance);
+ /* bsdf *= sphere_energy(l_radius); Velvet is using only point lights for now */
+}
+
+void bsdf_velvet_area_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ if (max(l_areasizex, l_areasizey) < 1e-6) {
+ bsdf = 0.0;
+ return;
+ }
+
+ float m_1_sig2; prepare_velvet(sigma, m_1_sig2);
+
+ bsdf = bsdf_ashikhmin_velvet(N, L, V, m_1_sig2);
+
+ /* Energy conservation + cycle matching */
+ bsdf *= 8.0 / (l_distance * l_distance);
+
+ /* l_areasizex *= scale.x;
+ * l_areasizey *= scale.y;
+ * bsdf *= rectangle_energy(l_areasizex, l_areasizey); Velvet is using only point lights for now*/
+}
+
+void bsdf_velvet_sun_light(vec3 N, vec3 T, vec3 L, vec3 V, vec3 l_coords,
+ float l_distance, float l_areasizex, float l_areasizey, vec2 l_areascale, mat4 l_mat,
+ float roughness, float ior, float sigma, float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ out float bsdf)
+{
+ float m_1_sig2; prepare_velvet(sigma, m_1_sig2);
+
+ bsdf = bsdf_ashikhmin_velvet(N, L, V, m_1_sig2);
+}
+
+
+/* -------- Image Based Lighting --------- */
+
+void env_sampling_velvet(
+ float pbr, vec3 viewpos, mat4 invviewmat, mat4 viewmat,
+ vec3 N, vec3 T, float roughness, float ior, float sigma,
+ float toon_size, float toon_smooth, float anisotropy, float aniso_rotation,
+ float ao_factor, out vec3 result)
+{
+ vector_prepass(viewpos, N, invviewmat, viewmat);
+ make_orthonormals(N, T, B); /* Generate tangent space */
+ setup_noise(gl_FragCoord.xy); /* Noise to dither the samples */
+
+ /* Precomputation */
+ float m_1_sig2; prepare_velvet(sigma, m_1_sig2);
+ float NV = max(1e-5, abs(dot(I, N)));
+
+ /* Integrating Envmap */
+ vec4 out_radiance = vec4(0.0);
+ for (float i = 0; i < unfbsdfsamples.x; i++) {
+ vec3 L = sample_hemisphere(i, N, T, B);
+ vec3 H = normalize(L - I);
+
+ float NL = dot(N, L);
+ float NH = dot(N, H); /* cosTheta */
+
+ if (NL > 0.0 && abs(NH) < 1.0-1e-5) {
+ /* Step 1 : Sampling Environment */
+ float pdf = pdf_hemisphere();
+ vec4 irradiance = sample_probe_pdf(L, pdf);
+
+ /* Step 2 : Integrating BRDF*/
+ float VH = max(abs(dot(I, H)), 1e-5);
+ float brdf = bsdf_ashikhmin_velvet(NL, NV, NH, VH, m_1_sig2);
+
+ out_radiance += irradiance * brdf / pdf;
+ }
+ }
+
+ result = out_radiance.rgb * unfbsdfsamples.y * ao_factor;
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_material_new_shading.glsl b/source/blender/gpu/shaders/gpu_shader_material_new_shading.glsl
new file mode 100644
index 00000000000..c5b2e1f0b81
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_new_shading.glsl
@@ -0,0 +1,1207 @@
+/*********** NEW SHADER NODES ***************/
+
+/* Blending */
+void node_bsdf_opaque(vec4 color, vec4 ambient_light, vec4 direct_light, out vec4 result)
+{
+ result = vec4( (ambient_light.rgb + direct_light.rgb) * color.rgb, 1.0);
+}
+
+void node_bsdf_transparent(vec4 color, vec4 background, out vec4 result)
+{
+ result = vec4( background.rgb * color.rgb, color.a);
+}
+
+/* Others Bsdfs */
+
+void node_subsurface_scattering(vec4 color, float scale, vec3 radius, float sharpen, float texture_blur, vec3 N, out vec4 result)
+{
+ node_bsdf_diffuse_lights(color, 0.0, N, vec3(0.0), vec4(0.2), result);
+}
+
+void node_bsdf_hair(vec4 color, float offset, float roughnessu, float roughnessv, vec3 tangent, out vec4 result)
+{
+ result = color;
+}
+
+void node_ambient_occlusion(vec4 color, out vec4 result)
+{
+ result = color;
+}
+
+void node_holdout(out vec4 result)
+{
+ result = vec4(0.0);
+}
+
+/* emission */
+
+void node_emission(vec4 color, float strength, vec3 N, out vec4 result)
+{
+ result = color*strength;
+}
+
+/* blackbody */
+
+void node_blackbody(float T, out vec4 col)
+{
+ float u = ( 0.860117757 + 1.54118254e-4 * T + 1.28641212e-7 * T*T ) / ( 1.0 + 8.42420235e-4 * T + 7.08145163e-7 * T*T );
+ float v = ( 0.317398726 + 4.22806245e-5 * T + 4.20481691e-8 * T*T ) / ( 1.0 - 2.89741816e-5 * T + 1.61456053e-7 * T*T );
+
+ float x = 3*u / ( 2*u - 8*v + 4 );
+ float y = 2*v / ( 2*u - 8*v + 4 );
+ float z = 1 - x - y;
+
+ float Y = 1;
+ float X = Y/y * x;
+ float Z = Y/y * z;
+
+ mat3 XYZtoRGB = mat3(
+ 3.2404542, -0.9692660, 0.0556434,
+ -1.5371385, 1.8760108, -0.2040259,
+ -0.4985314, 0.0415560, 1.0572252
+ );
+
+ col = vec4(( XYZtoRGB * vec3( X, Y, Z ) ), 1.0);
+}
+
+vec3 xyz_to_rgb(float x, float y, float z)
+{
+ return vec3( 3.240479 * x + -1.537150 * y + -0.498535 * z,
+ -0.969256 * x + 1.875991 * y + 0.041556 * z,
+ 0.055648 * x + -0.204043 * y + 1.057311 * z);
+}
+
+// CIE colour matching functions xBar, yBar, and zBar for
+// wavelengths from 380 through 780 nanometers, every 5
+// nanometers. For a wavelength lambda in this range:
+// cie_colour_match[(lambda - 380) / 5][0] = xBar
+// cie_colour_match[(lambda - 380) / 5][1] = yBar
+// cie_colour_match[(lambda - 380) / 5][2] = zBar
+uniform vec3 node_wavelength_LUT[81] = vec3[81](
+ vec3(0.0014,0.0000,0.0065), vec3(0.0022,0.0001,0.0105), vec3(0.0042,0.0001,0.0201),
+ vec3(0.0076,0.0002,0.0362), vec3(0.0143,0.0004,0.0679), vec3(0.0232,0.0006,0.1102),
+ vec3(0.0435,0.0012,0.2074), vec3(0.0776,0.0022,0.3713), vec3(0.1344,0.0040,0.6456),
+ vec3(0.2148,0.0073,1.0391), vec3(0.2839,0.0116,1.3856), vec3(0.3285,0.0168,1.6230),
+ vec3(0.3483,0.0230,1.7471), vec3(0.3481,0.0298,1.7826), vec3(0.3362,0.0380,1.7721),
+ vec3(0.3187,0.0480,1.7441), vec3(0.2908,0.0600,1.6692), vec3(0.2511,0.0739,1.5281),
+ vec3(0.1954,0.0910,1.2876), vec3(0.1421,0.1126,1.0419), vec3(0.0956,0.1390,0.8130),
+ vec3(0.0580,0.1693,0.6162), vec3(0.0320,0.2080,0.4652), vec3(0.0147,0.2586,0.3533),
+ vec3(0.0049,0.3230,0.2720), vec3(0.0024,0.4073,0.2123), vec3(0.0093,0.5030,0.1582),
+ vec3(0.0291,0.6082,0.1117), vec3(0.0633,0.7100,0.0782), vec3(0.1096,0.7932,0.0573),
+ vec3(0.1655,0.8620,0.0422), vec3(0.2257,0.9149,0.0298), vec3(0.2904,0.9540,0.0203),
+ vec3(0.3597,0.9803,0.0134), vec3(0.4334,0.9950,0.0087), vec3(0.5121,1.0000,0.0057),
+ vec3(0.5945,0.9950,0.0039), vec3(0.6784,0.9786,0.0027), vec3(0.7621,0.9520,0.0021),
+ vec3(0.8425,0.9154,0.0018), vec3(0.9163,0.8700,0.0017), vec3(0.9786,0.8163,0.0014),
+ vec3(1.0263,0.7570,0.0011), vec3(1.0567,0.6949,0.0010), vec3(1.0622,0.6310,0.0008),
+ vec3(1.0456,0.5668,0.0006), vec3(1.0026,0.5030,0.0003), vec3(0.9384,0.4412,0.0002),
+ vec3(0.8544,0.3810,0.0002), vec3(0.7514,0.3210,0.0001), vec3(0.6424,0.2650,0.0000),
+ vec3(0.5419,0.2170,0.0000), vec3(0.4479,0.1750,0.0000), vec3(0.3608,0.1382,0.0000),
+ vec3(0.2835,0.1070,0.0000), vec3(0.2187,0.0816,0.0000), vec3(0.1649,0.0610,0.0000),
+ vec3(0.1212,0.0446,0.0000), vec3(0.0874,0.0320,0.0000), vec3(0.0636,0.0232,0.0000),
+ vec3(0.0468,0.0170,0.0000), vec3(0.0329,0.0119,0.0000), vec3(0.0227,0.0082,0.0000),
+ vec3(0.0158,0.0057,0.0000), vec3(0.0114,0.0041,0.0000), vec3(0.0081,0.0029,0.0000),
+ vec3(0.0058,0.0021,0.0000), vec3(0.0041,0.0015,0.0000), vec3(0.0029,0.0010,0.0000),
+ vec3(0.0020,0.0007,0.0000), vec3(0.0014,0.0005,0.0000), vec3(0.0010,0.0004,0.0000),
+ vec3(0.0007,0.0002,0.0000), vec3(0.0005,0.0002,0.0000), vec3(0.0003,0.0001,0.0000),
+ vec3(0.0002,0.0001,0.0000), vec3(0.0002,0.0001,0.0000), vec3(0.0001,0.0000,0.0000),
+ vec3(0.0001,0.0000,0.0000), vec3(0.0001,0.0000,0.0000), vec3(0.0000,0.0000,0.0000)
+);
+
+void node_wavelength(float w, out vec4 col)
+{
+ float ii = (w-380.0) * (1.0/5.0); // scaled 0..80
+ int i = int(ii);
+ vec3 color;
+
+ if(i < 0 || i >= 80) {
+ color = vec3(0.0, 0.0, 0.0);
+ }
+ else {
+ ii -= i;
+ color = mix(node_wavelength_LUT[i], node_wavelength_LUT[i+1], ii);
+ }
+
+ color = xyz_to_rgb(color.x, color.y, color.z);
+ color *= 1.0/2.52; // Empirical scale from lg to make all comps <= 1
+
+ /* Clamp to zero if values are smaller */
+ col = vec4(max(color, vec3(0.0, 0.0, 0.0)), 1.0);
+
+ // srgb_to_linearrgb(col, col);
+}
+
+/* background */
+
+void background_transform_to_world(vec3 viewvec, out vec3 worldvec)
+{
+ vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
+ vec4 co_homogenous = (gl_ProjectionMatrixInverse * v);
+
+ vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
+ worldvec = (gl_ModelViewMatrixInverse * co).xyz;
+}
+
+void node_background(vec4 color, float strength, vec3 N, out vec4 result)
+{
+ result = color * strength;
+}
+
+/* closures */
+
+void node_mix_shader(float fac, vec4 shader1, vec4 shader2, out vec4 shader)
+{
+ shader = mix(shader1, shader2, saturate(fac));
+}
+
+void node_add_shader(vec4 shader1, vec4 shader2, out vec4 shader)
+{
+ shader = shader1 + shader2;
+}
+
+/* fresnel */
+
+void node_fresnel(float ior, vec3 N, vec3 I, out float result)
+{
+ /* handle perspective/orthographic */
+ vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
+
+ float eta = max(ior, 0.00001);
+ result = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? eta : 1.0/eta);
+}
+
+/* layer_weight */
+
+void node_layer_weight(float blend, vec3 N, vec3 I, out float fresnel, out float facing)
+{
+ /* fresnel */
+ float eta = max(1.0 - blend, 0.00001);
+ vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
+
+ fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing)? 1.0/eta : eta );
+
+ /* facing */
+ facing = abs(dot(I_view, N));
+ if (blend != 0.5) {
+ blend = clamp(blend, 0.0, 0.99999);
+ blend = (blend < 0.5) ? 2.0 * blend : 0.5 / (1.0 - blend);
+ facing = pow(facing, blend);
+ }
+ facing = 1.0 - facing;
+}
+
+/* gamma */
+
+void node_gamma(vec4 col, float gamma, out vec4 outcol)
+{
+ outcol = col;
+
+ if (col.r > 0.0)
+ outcol.r = compatible_pow(col.r, gamma);
+ if (col.g > 0.0)
+ outcol.g = compatible_pow(col.g, gamma);
+ if (col.b > 0.0)
+ outcol.b = compatible_pow(col.b, gamma);
+}
+
+/* geometry */
+
+void node_attribute(vec3 attr, out vec4 outcol, out vec3 outvec, out float outf)
+{
+ outcol = vec4(attr, 1.0);
+ outvec = attr;
+ outf = (attr.x + attr.y + attr.z) / 3.0;
+}
+
+void node_uvmap(vec3 attr_uv, out vec3 outvec)
+{
+ outvec = attr_uv;
+}
+
+void tangent_orco_x(vec3 orco_in, out vec3 orco_out)
+{
+ orco_out = vec3(0.0, orco_in.z * -0.5, orco_in.y * 0.5);
+}
+
+void tangent_orco_y(vec3 orco_in, out vec3 orco_out)
+{
+ orco_out = vec3(orco_in.z * -0.5, 0.0, orco_in.x * 0.5);
+}
+
+void tangent_orco_z(vec3 orco_in, out vec3 orco_out)
+{
+ orco_out = vec3(orco_in.y * -0.5, orco_in.x * 0.5, 0.0);
+}
+
+void node_tangent(vec3 N, vec3 orco, mat4 objmat, mat4 invviewmat, out vec3 T)
+{
+ N = (invviewmat*vec4(N, 0.0)).xyz;
+ T = (objmat*vec4(orco, 0.0)).xyz;
+ T = cross(N, normalize(cross(T, N)));
+}
+
+void node_tangentmap(vec4 attr_tangent, mat4 toworld, out vec3 tangent)
+{
+ tangent = (toworld * vec4(attr_tangent.xyz, 0.0)).xyz;
+}
+
+void default_tangent(vec3 N, vec3 orco, mat4 objmat, mat4 viewmat, mat4 invviewmat, out vec3 T)
+{
+ orco = vec3(orco.y * -0.5, orco.x * 0.5, 0.0);
+ node_tangent(N, orco, objmat, invviewmat, T);
+ T = (viewmat * vec4(T, 0.0)).xyz;
+}
+
+void node_geometry(vec3 I, vec3 N, vec3 attr_orco, mat4 toworld, mat4 fromobj,
+ out vec3 position, out vec3 normal, out vec3 tangent,
+ out vec3 true_normal, out vec3 incoming, out vec3 parametric,
+ out float backfacing, out float pointiness)
+{
+ position = (toworld * vec4(I, 1.0)).xyz;
+ normal = (toworld * vec4(N, 0.0)).xyz;
+ attr_orco = vec3(attr_orco.y * -0.5, attr_orco.x * 0.5, 0.0);
+ node_tangent(N, attr_orco, fromobj, toworld, tangent);
+ true_normal = normal;
+
+ /* handle perspective/orthographic */
+ vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
+ incoming = -(toworld * vec4(I_view, 0.0)).xyz;
+
+ parametric = vec3(0.0);
+ backfacing = (gl_FrontFacing) ? 0.0 : 1.0;
+ pointiness = 0.5;
+}
+
+void node_geometry_lamp(vec3 N, vec4 P, vec3 I, mat4 toworld,
+ out vec3 position, out vec3 normal, out vec3 tangent,
+ out vec3 true_normal, out vec3 incoming, out vec3 parametric,
+ out float backfacing, out float pointiness)
+{
+ position = (toworld*P).xyz;
+ normal = normalize(toworld*vec4(N, 0.0)).xyz;
+ tangent = vec3(0.0);
+ true_normal = normal;
+ incoming = normalize(toworld*vec4(I, 0.0)).xyz;
+
+ parametric = vec3(0.0);
+ backfacing = 0.0;
+ pointiness = 0.0;
+}
+
+void node_tex_coord(
+ vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac,
+ vec3 attr_orco, vec3 attr_uv,
+ out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object,
+ out vec3 camera, out vec3 window, out vec3 reflection)
+{
+ generated = attr_orco * 0.5 + vec3(0.5);
+ normal = normalize((obinvmat * (viewinvmat * vec4(N, 0.0))).xyz);
+ uv = attr_uv;
+ object = (obinvmat * (viewinvmat * vec4(I, 1.0))).xyz;
+ camera = vec3(I.xy, -I.z);
+ vec4 projvec = gl_ProjectionMatrix * vec4(I, 1.0);
+ window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0);
+
+ vec3 shade_I;
+ shade_view(I, shade_I);
+ vec3 view_reflection = reflect(shade_I, normalize(N));
+ reflection = (viewinvmat * vec4(view_reflection, 0.0)).xyz;
+}
+
+void node_tex_coord_background(
+ vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac,
+ vec3 attr_orco, vec3 attr_uv,
+ out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object,
+ out vec3 camera, out vec3 window, out vec3 reflection)
+{
+ vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
+ vec4 co_homogenous = (gl_ProjectionMatrixInverse * v);
+
+ vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
+
+ co = normalize(co);
+ vec3 coords = (gl_ModelViewMatrixInverse * co).xyz;
+
+ generated = coords;
+ normal = -coords;
+ uv = vec3(attr_uv.xy, 0.0);
+ object = coords;
+
+ camera = vec3(co.xy, -co.z);
+ window = (gl_ProjectionMatrix[3][3] == 0.0) ?
+ vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0) :
+ vec3(vec2(0.5) * camerafac.xy + camerafac.zw, 0.0);
+
+ reflection = -coords;
+}
+
+/* textures */
+
+float calc_gradient(vec3 p, int gradient_type)
+{
+ float x, y, z;
+ x = p.x;
+ y = p.y;
+ z = p.z;
+ if (gradient_type == 0) { /* linear */
+ return x;
+ }
+ else if (gradient_type == 1) { /* quadratic */
+ float r = max(x, 0.0);
+ return r * r;
+ }
+ else if (gradient_type == 2) { /* easing */
+ float r = min(max(x, 0.0), 1.0);
+ float t = r * r;
+ return (3.0 * t - 2.0 * t * r);
+ }
+ else if (gradient_type == 3) { /* diagonal */
+ return (x + y) * 0.5;
+ }
+ else if (gradient_type == 4) { /* radial */
+ return atan(y, x) / (M_PI * 2) + 0.5;
+ }
+ else {
+ float r = max(1.0 - sqrt(x * x + y * y + z * z), 0.0);
+ if (gradient_type == 5) { /* quadratic sphere */
+ return r * r;
+ }
+ else if (gradient_type == 6) { /* sphere */
+ return r;
+ }
+ }
+ return 0.0;
+}
+
+void node_tex_gradient(vec3 co, float gradient_type, out vec4 color, out float fac)
+{
+ float f = calc_gradient(co, int(gradient_type));
+ f = clamp(f, 0.0, 1.0);
+
+ color = vec4(f, f, f, 1.0);
+ fac = f;
+}
+
+void node_tex_checker(vec3 co, vec4 color1, vec4 color2, float scale, out vec4 color, out float fac)
+{
+ vec3 p = co * scale;
+
+ /* Prevent precision issues on unit coordinates. */
+ p.x = (p.x + 0.000001) * 0.999999;
+ p.y = (p.y + 0.000001) * 0.999999;
+ p.z = (p.z + 0.000001) * 0.999999;
+
+ int xi = int(abs(floor(p.x)));
+ int yi = int(abs(floor(p.y)));
+ int zi = int(abs(floor(p.z)));
+
+ bool check = ((mod(xi, 2) == mod(yi, 2)) == bool(mod(zi, 2)));
+
+ color = check ? color1 : color2;
+ fac = check ? 1.0 : 0.0;
+}
+
+#ifdef BIT_OPERATIONS
+vec2 calc_brick_texture(vec3 p, float mortar_size, float bias,
+ float brick_width, float row_height,
+ float offset_amount, int offset_frequency,
+ float squash_amount, int squash_frequency)
+{
+ int bricknum, rownum;
+ float offset = 0.0;
+ float x, y;
+
+ rownum = floor_to_int(p.y / row_height);
+
+ if (offset_frequency != 0 && squash_frequency != 0) {
+ brick_width *= (rownum % squash_frequency != 0) ? 1.0 : squash_amount; /* squash */
+ offset = (rownum % offset_frequency != 0) ? 0.0 : (brick_width * offset_amount); /* offset */
+ }
+
+ bricknum = floor_to_int((p.x + offset) / brick_width);
+
+ x = (p.x + offset) - brick_width * bricknum;
+ y = p.y - row_height * rownum;
+
+ return vec2(clamp((integer_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias), 0.0, 1.0),
+ (x < mortar_size || y < mortar_size ||
+ x > (brick_width - mortar_size) ||
+ y > (row_height - mortar_size)) ? 1.0 : 0.0);
+}
+#endif
+
+void node_tex_brick(vec3 co,
+ vec4 color1, vec4 color2,
+ vec4 mortar, float scale,
+ float mortar_size, float bias,
+ float brick_width, float row_height,
+ float offset_amount, float offset_frequency,
+ float squash_amount, float squash_frequency,
+ out vec4 color, out float fac)
+{
+#ifdef BIT_OPERATIONS
+ vec2 f2 = calc_brick_texture(co * scale,
+ mortar_size, bias,
+ brick_width, row_height,
+ offset_amount, int(offset_frequency),
+ squash_amount, int(squash_frequency));
+ float tint = f2.x;
+ float f = f2.y;
+ if (f != 1.0) {
+ float facm = 1.0 - tint;
+ color1 = facm * color1 + tint * color2;
+ }
+ color = (f == 1.0) ? mortar : color1;
+ fac = f;
+#else
+ color = vec4(1.0);
+ fac = 1.0;
+#endif
+}
+
+void node_tex_clouds(vec3 co, float size, out vec4 color, out float fac)
+{
+ color = vec4(1.0);
+ fac = 1.0;
+}
+
+void node_tex_environment_equirectangular(vec3 co, sampler2D ima, out vec4 color)
+{
+ vec3 nco = normalize(co);
+ float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5;
+ float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5;
+
+ color = texture2D(ima, vec2(u, v));
+}
+
+void node_tex_environment_mirror_ball(vec3 co, sampler2D ima, out vec4 color)
+{
+ vec3 nco = normalize(co);
+
+ nco.y -= 1.0;
+
+ float div = 2.0 * sqrt(max(-0.5 * nco.y, 0.0));
+ if(div > 0.0)
+ nco /= div;
+
+ float u = 0.5 * (nco.x + 1.0);
+ float v = 0.5 * (nco.z + 1.0);
+
+ color = texture2D(ima, vec2(u, v));
+}
+
+void node_tex_environment_empty(vec3 co, out vec4 color)
+{
+ color = vec4(1.0, 0.0, 1.0, 1.0);
+}
+
+void node_tex_image(vec3 co, sampler2D ima, out vec4 color, out float alpha)
+{
+ color = texture2D(ima, co.xy);
+ alpha = color.a;
+}
+
+void node_tex_image_closest(vec3 co, sampler2D ima, vec2 res, out vec4 color, out float alpha)
+{
+#if __VERSION__ < 130
+ color = texture2DLod(ima, (floor(co.xy * res) + 0.5) / res, 0.0);
+#else
+ color = texelFetch(ima, ivec2(fract(co.xy) * res), 0);
+#endif
+ alpha = color.a;
+}
+
+void node_tex_image_box(vec3 texco,
+ vec3 nob,
+ sampler2D ima,
+ float blend,
+ out vec4 color,
+ out float alpha)
+{
+ /* project from direction vector to barycentric coordinates in triangles */
+ nob = vec3(abs(nob.x), abs(nob.y), abs(nob.z));
+ nob /= (nob.x + nob.y + nob.z);
+
+ /* basic idea is to think of this as a triangle, each corner representing
+ * one of the 3 faces of the cube. in the corners we have single textures,
+ * in between we blend between two textures, and in the middle we a blend
+ * between three textures.
+ *
+ * the Nxyz values are the barycentric coordinates in an equilateral
+ * triangle, which in case of blending, in the middle has a smaller
+ * equilateral triangle where 3 textures blend. this divides things into
+ * 7 zones, with an if () test for each zone */
+
+ vec3 weight = vec3(0.0, 0.0, 0.0);
+ float limit = 0.5 * (1.0 + blend);
+
+ /* first test for corners with single texture */
+ if (nob.x > limit * (nob.x + nob.y) && nob.x > limit * (nob.x + nob.z)) {
+ weight.x = 1.0;
+ }
+ else if (nob.y > limit * (nob.x + nob.y) && nob.y > limit * (nob.y + nob.z)) {
+ weight.y = 1.0;
+ }
+ else if (nob.z > limit * (nob.x + nob.z) && nob.z > limit * (nob.y + nob.z)) {
+ weight.z = 1.0;
+ }
+ else if (blend > 0.0) {
+ /* in case of blending, test for mixes between two textures */
+ if (nob.z < (1.0 - limit) * (nob.y + nob.x)) {
+ weight.x = nob.x / (nob.x + nob.y);
+ weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
+ weight.y = 1.0 - weight.x;
+ }
+ else if (nob.x < (1.0 - limit) * (nob.y + nob.z)) {
+ weight.y = nob.y / (nob.y + nob.z);
+ weight.y = clamp((weight.y - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
+ weight.z = 1.0 - weight.y;
+ }
+ else if (nob.y < (1.0 - limit) * (nob.x + nob.z)) {
+ weight.x = nob.x / (nob.x + nob.z);
+ weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
+ weight.z = 1.0 - weight.x;
+ }
+ else {
+ /* last case, we have a mix between three */
+ weight.x = ((2.0 - limit) * nob.x + (limit - 1.0)) / (2.0 * limit - 1.0);
+ weight.y = ((2.0 - limit) * nob.y + (limit - 1.0)) / (2.0 * limit - 1.0);
+ weight.z = ((2.0 - limit) * nob.z + (limit - 1.0)) / (2.0 * limit - 1.0);
+ }
+ }
+ else {
+ /* Desperate mode, no valid choice anyway, fallback to one side.*/
+ weight.x = 1.0;
+ }
+
+ color = vec4(0);
+ if (weight.x > 0.0) {
+ color += weight.x * texture2D(ima, texco.yz);
+ }
+ if (weight.y > 0.0) {
+ color += weight.y * texture2D(ima, texco.xz);
+ }
+ if (weight.z > 0.0) {
+ color += weight.z * texture2D(ima, texco.yx);
+ }
+
+ alpha = color.a;
+}
+
+void node_tex_image_empty(vec3 co, out vec4 color, out float alpha)
+{
+ color = vec4(0.0);
+ alpha = 0.0;
+}
+
+void node_tex_magic(vec3 co, float scale, float distortion, float depth, out vec4 color, out float fac)
+{
+ vec3 p = co * scale;
+ float x = sin((p.x + p.y + p.z) * 5.0);
+ float y = cos((-p.x + p.y - p.z) * 5.0);
+ float z = -cos((-p.x - p.y + p.z) * 5.0);
+
+ if (depth > 0) {
+ x *= distortion;
+ y *= distortion;
+ z *= distortion;
+ y = -cos(x - y + z);
+ y *= distortion;
+ if (depth > 1) {
+ x = cos(x - y - z);
+ x *= distortion;
+ if (depth > 2) {
+ z = sin(-x - y - z);
+ z *= distortion;
+ if (depth > 3) {
+ x = -cos(-x + y - z);
+ x *= distortion;
+ if (depth > 4) {
+ y = -sin(-x + y + z);
+ y *= distortion;
+ if (depth > 5) {
+ y = -cos(-x + y + z);
+ y *= distortion;
+ if (depth > 6) {
+ x = cos(x + y + z);
+ x *= distortion;
+ if (depth > 7) {
+ z = sin(x + y - z);
+ z *= distortion;
+ if (depth > 8) {
+ x = -cos(-x - y + z);
+ x *= distortion;
+ if (depth > 9) {
+ y = -sin(x - y + z);
+ y *= distortion;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (distortion != 0.0) {
+ distortion *= 2.0;
+ x /= distortion;
+ y /= distortion;
+ z /= distortion;
+ }
+
+ color = vec4(0.5 - x, 0.5 - y, 0.5 - z, 1.0);
+ fac = (color.x + color.y + color.z) / 3.0;
+}
+
+#ifdef BIT_OPERATIONS
+float noise_fade(float t)
+{
+ return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
+}
+
+float noise_scale3(float result)
+{
+ return 0.9820 * result;
+}
+
+float noise_nerp(float t, float a, float b)
+{
+ return (1.0 - t) * a + t * b;
+}
+
+float noise_grad(uint hash, float x, float y, float z)
+{
+ uint h = hash & 15u;
+ float u = h < 8u ? x : y;
+ float vt = ((h == 12u) || (h == 14u)) ? x : z;
+ float v = h < 4u ? y : vt;
+ return (((h & 1u) != 0u) ? -u : u) + (((h & 2u) != 0u) ? -v : v);
+}
+
+float noise_perlin(float x, float y, float z)
+{
+ int X; float fx = floorfrac(x, X);
+ int Y; float fy = floorfrac(y, Y);
+ int Z; float fz = floorfrac(z, Z);
+
+ float u = noise_fade(fx);
+ float v = noise_fade(fy);
+ float w = noise_fade(fz);
+
+ float result;
+
+ result = noise_nerp(w, noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z), fx, fy, fz),
+ noise_grad(hash(X + 1, Y, Z), fx - 1.0, fy, fz)),
+ noise_nerp(u, noise_grad(hash(X, Y + 1, Z), fx, fy - 1.0, fz),
+ noise_grad(hash(X + 1, Y + 1, Z), fx - 1.0, fy - 1.0, fz))),
+ noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z + 1), fx, fy, fz - 1.0),
+ noise_grad(hash(X + 1, Y, Z + 1), fx - 1.0, fy, fz - 1.0)),
+ noise_nerp(u, noise_grad(hash(X, Y + 1, Z + 1), fx, fy - 1.0, fz - 1.0),
+ noise_grad(hash(X + 1, Y + 1, Z + 1), fx - 1.0, fy - 1.0, fz - 1.0))));
+ return noise_scale3(result);
+}
+
+float noise(vec3 p)
+{
+ return 0.5 * noise_perlin(p.x, p.y, p.z) + 0.5;
+}
+
+float snoise(vec3 p)
+{
+ return noise_perlin(p.x, p.y, p.z);
+}
+
+float noise_turbulence(vec3 p, float octaves, int hard)
+{
+ float fscale = 1.0;
+ float amp = 1.0;
+ float sum = 0.0;
+ int i, n;
+ octaves = clamp(octaves, 0.0, 16.0);
+ n = int(octaves);
+ for (i = 0; i <= n; i++) {
+ float t = noise(fscale * p);
+ if (hard != 0) {
+ t = abs(2.0 * t - 1.0);
+ }
+ sum += t * amp;
+ amp *= 0.5;
+ fscale *= 2.0;
+ }
+ float rmd = octaves - floor(octaves);
+ if (rmd != 0.0) {
+ float t = noise(fscale * p);
+ if (hard != 0) {
+ t = abs(2.0 * t - 1.0);
+ }
+ float sum2 = sum + t * amp;
+ sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
+ sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1));
+ return (1.0 - rmd) * sum + rmd * sum2;
+ }
+ else {
+ sum *= (float(1 << n) / float((1 << (n + 1)) - 1));
+ return sum;
+ }
+}
+#endif // BIT_OPERATIONS
+
+void node_tex_noise(vec3 co, float scale, float detail, float distortion, out vec4 color, out float fac)
+{
+#ifdef BIT_OPERATIONS
+ vec3 p = co * scale;
+ int hard = 0;
+ if (distortion != 0.0) {
+ vec3 r, offset = vec3(13.5, 13.5, 13.5);
+ r.x = noise(p + offset) * distortion;
+ r.y = noise(p) * distortion;
+ r.z = noise(p - offset) * distortion;
+ p += r;
+ }
+
+ fac = noise_turbulence(p, detail, hard);
+ color = vec4(fac,
+ noise_turbulence(vec3(p.y, p.x, p.z), detail, hard),
+ noise_turbulence(vec3(p.y, p.z, p.x), detail, hard),
+ 1);
+#else // BIT_OPERATIONS
+ color = vec4(1.0);
+ fac = 1.0;
+#endif // BIT_OPERATIONS
+}
+
+
+#ifdef BIT_OPERATIONS
+
+/* Musgrave fBm
+ *
+ * H: fractal increment parameter
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ *
+ * from "Texturing and Modelling: A procedural approach"
+ */
+
+float noise_musgrave_fBm(vec3 p, float H, float lacunarity, float octaves)
+{
+ float rmd;
+ float value = 0.0;
+ float pwr = 1.0;
+ float pwHL = pow(lacunarity, -H);
+ int i;
+
+ for (i = 0; i < int(octaves); i++) {
+ value += snoise(p) * pwr;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ rmd = octaves - floor(octaves);
+ if (rmd != 0.0)
+ value += rmd * snoise(p) * pwr;
+
+ return value;
+}
+
+/* Musgrave Multifractal
+ *
+ * H: highest fractal dimension
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ */
+
+float noise_musgrave_multi_fractal(vec3 p, float H, float lacunarity, float octaves)
+{
+ float rmd;
+ float value = 1.0;
+ float pwr = 1.0;
+ float pwHL = pow(lacunarity, -H);
+ int i;
+
+ for (i = 0; i < int(octaves); i++) {
+ value *= (pwr * snoise(p) + 1.0);
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ rmd = octaves - floor(octaves);
+ if (rmd != 0.0)
+ value *= (rmd * pwr * snoise(p) + 1.0); /* correct? */
+
+ return value;
+}
+
+/* Musgrave Heterogeneous Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float noise_musgrave_hetero_terrain(vec3 p, float H, float lacunarity, float octaves, float offset)
+{
+ float value, increment, rmd;
+ float pwHL = pow(lacunarity, -H);
+ float pwr = pwHL;
+ int i;
+
+ /* first unscaled octave of function; later octaves are scaled */
+ value = offset + snoise(p);
+ p *= lacunarity;
+
+ for (i = 1; i < int(octaves); i++) {
+ increment = (snoise(p) + offset) * pwr * value;
+ value += increment;
+ pwr *= pwHL;
+ p *= lacunarity;
+ }
+
+ rmd = octaves - floor(octaves);
+ if (rmd != 0.0) {
+ increment = (snoise(p) + offset) * pwr * value;
+ value += rmd * increment;
+ }
+
+ return value;
+}
+
+/* Hybrid Additive/Multiplicative Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float noise_musgrave_hybrid_multi_fractal(vec3 p, float H, float lacunarity, float octaves, float offset, float gain)
+{
+ float result, signal, weight, rmd;
+ float pwHL = pow(lacunarity, -H);
+ float pwr = pwHL;
+ int i;
+
+ result = snoise(p) + offset;
+ weight = gain * result;
+ p *= lacunarity;
+
+ for (i = 1; (weight > 0.001f) && (i < int(octaves)); i++) {
+ if (weight > 1.0)
+ weight = 1.0;
+
+ signal = (snoise(p) + offset) * pwr;
+ pwr *= pwHL;
+ result += weight * signal;
+ weight *= gain * signal;
+ p *= lacunarity;
+ }
+
+ rmd = octaves - floor(octaves);
+ if (rmd != 0.0)
+ result += rmd * ((snoise(p) + offset) * pwr);
+
+ return result;
+}
+
+/* Ridged Multifractal Terrain
+ *
+ * H: fractal dimension of the roughest area
+ * lacunarity: gap between successive frequencies
+ * octaves: number of frequencies in the fBm
+ * offset: raises the terrain from `sea level'
+ */
+
+float noise_musgrave_ridged_multi_fractal(vec3 p, float H, float lacunarity, float octaves, float offset, float gain)
+{
+ float result, signal, weight;
+ float pwHL = pow(lacunarity, -H);
+ float pwr = pwHL;
+ int i;
+
+ signal = offset - abs(snoise(p));
+ signal *= signal;
+ result = signal;
+ weight = 1.0;
+
+ for (i = 1; i < int(octaves); i++) {
+ p *= lacunarity;
+ weight = clamp(signal * gain, 0.0, 1.0);
+ signal = offset - abs(snoise(p));
+ signal *= signal;
+ signal *= weight;
+ result += signal * pwr;
+ pwr *= pwHL;
+ }
+
+ return result;
+}
+
+float svm_musgrave(int type,
+ float dimension,
+ float lacunarity,
+ float octaves,
+ float offset,
+ float intensity,
+ float gain,
+ vec3 p)
+{
+ if (type == 0 /*NODE_MUSGRAVE_MULTIFRACTAL*/)
+ return intensity * noise_musgrave_multi_fractal(p, dimension, lacunarity, octaves);
+ else if (type == 1 /*NODE_MUSGRAVE_FBM*/)
+ return intensity * noise_musgrave_fBm(p, dimension, lacunarity, octaves);
+ else if (type == 2 /*NODE_MUSGRAVE_HYBRID_MULTIFRACTAL*/)
+ return intensity * noise_musgrave_hybrid_multi_fractal(p, dimension, lacunarity, octaves, offset, gain);
+ else if (type == 3 /*NODE_MUSGRAVE_RIDGED_MULTIFRACTAL*/)
+ return intensity * noise_musgrave_ridged_multi_fractal(p, dimension, lacunarity, octaves, offset, gain);
+ else if (type == 4 /*NODE_MUSGRAVE_HETERO_TERRAIN*/)
+ return intensity * noise_musgrave_hetero_terrain(p, dimension, lacunarity, octaves, offset);
+ return 0.0;
+}
+#endif // #ifdef BIT_OPERATIONS
+
+void node_tex_musgrave(vec3 co,
+ float scale,
+ float detail,
+ float dimension,
+ float lacunarity,
+ float offset,
+ float gain,
+ float type,
+ out vec4 color,
+ out float fac)
+{
+#ifdef BIT_OPERATIONS
+ fac = svm_musgrave(int(type),
+ dimension,
+ lacunarity,
+ detail,
+ offset,
+ 1.0,
+ gain,
+ co * scale);
+#else
+ fac = 1.0;
+#endif
+
+ color = vec4(fac, fac, fac, 1.0);
+}
+
+void node_tex_sky(vec3 co, out vec4 color)
+{
+ color = vec4(1.0);
+}
+
+void node_tex_voronoi(vec3 co, float scale, float coloring, out vec4 color, out float fac)
+{
+#ifdef BIT_OPERATIONS
+ vec3 p = co * scale;
+ int xx, yy, zz, xi, yi, zi;
+ float da[4];
+ vec3 pa[4];
+
+ xi = floor_to_int(p[0]);
+ yi = floor_to_int(p[1]);
+ zi = floor_to_int(p[2]);
+
+ da[0] = 1e+10;
+ da[1] = 1e+10;
+ da[2] = 1e+10;
+ da[3] = 1e+10;
+
+ for (xx = xi - 1; xx <= xi + 1; xx++) {
+ for (yy = yi - 1; yy <= yi + 1; yy++) {
+ for (zz = zi - 1; zz <= zi + 1; zz++) {
+ vec3 ip = vec3(xx, yy, zz);
+ vec3 vp = cellnoise_color(ip);
+ vec3 pd = p - (vp + ip);
+ float d = dot(pd, pd);
+ vp += vec3(xx, yy, zz);
+ if (d < da[0]) {
+ da[3] = da[2];
+ da[2] = da[1];
+ da[1] = da[0];
+ da[0] = d;
+ pa[3] = pa[2];
+ pa[2] = pa[1];
+ pa[1] = pa[0];
+ pa[0] = vp;
+ }
+ else if (d < da[1]) {
+ da[3] = da[2];
+ da[2] = da[1];
+ da[1] = d;
+
+ pa[3] = pa[2];
+ pa[2] = pa[1];
+ pa[1] = vp;
+ }
+ else if (d < da[2]) {
+ da[3] = da[2];
+ da[2] = d;
+
+ pa[3] = pa[2];
+ pa[2] = vp;
+ }
+ else if (d < da[3]) {
+ da[3] = d;
+ pa[3] = vp;
+ }
+ }
+ }
+ }
+
+ if (coloring == 0.0) {
+ fac = abs(da[0]);
+ color = vec4(fac, fac, fac, 1);
+ }
+ else {
+ color = vec4(cellnoise_color(pa[0]), 1);
+ fac = (color.x + color.y + color.z) * (1.0 / 3.0);
+ }
+#else // BIT_OPERATIONS
+ color = vec4(1.0);
+ fac = 1.0;
+#endif // BIT_OPERATIONS
+}
+
+#ifdef BIT_OPERATIONS
+float calc_wave(vec3 p, float distortion, float detail, float detail_scale, int wave_type, int wave_profile)
+{
+ float n;
+
+ if (wave_type == 0) /* type bands */
+ n = (p.x + p.y + p.z) * 10.0;
+ else /* type rings */
+ n = length(p) * 20.0;
+
+ if (distortion != 0.0)
+ n += distortion * noise_turbulence(p * detail_scale, detail, 0);
+
+ if (wave_profile == 0) { /* profile sin */
+ return 0.5 + 0.5 * sin(n);
+ }
+ else { /* profile saw */
+ n /= 2.0 * M_PI;
+ n -= int(n);
+ return (n < 0.0) ? n + 1.0 : n;
+ }
+}
+#endif // BIT_OPERATIONS
+
+void node_tex_wave(
+ vec3 co, float scale, float distortion, float detail, float detail_scale, float wave_type, float wave_profile,
+ out vec4 color, out float fac)
+{
+#ifdef BIT_OPERATIONS
+ float f;
+ f = calc_wave(co * scale, distortion, detail, detail_scale, int(wave_type), int(wave_profile));
+
+ color = vec4(f, f, f, 1.0);
+ fac = f;
+#else // BIT_OPERATIONS
+ color = vec4(1.0);
+ fac = 1;
+#endif // BIT_OPERATIONS
+}
+
+/* light path */
+
+void node_light_path(
+ out float is_camera_ray,
+ out float is_shadow_ray,
+ out float is_diffuse_ray,
+ out float is_glossy_ray,
+ out float is_singular_ray,
+ out float is_reflection_ray,
+ out float is_transmission_ray,
+ out float ray_length,
+ out float ray_depth,
+ out float transparent_depth,
+ out float transmission_depth)
+{
+ is_camera_ray = 1.0;
+ is_shadow_ray = 0.0;
+ is_diffuse_ray = 0.0;
+ is_glossy_ray = 0.0;
+ is_singular_ray = 0.0;
+ is_reflection_ray = 0.0;
+ is_transmission_ray = 0.0;
+ ray_length = 1.0;
+ ray_depth = 1.0;
+ transparent_depth = 1.0;
+ transmission_depth = 1.0;
+}
+
+void node_light_falloff(float strength, float tsmooth, vec4 lamppos, vec3 pos, out float quadratic, out float linear, out float constant)
+{
+ float ray_length = length(lamppos.xyz - pos);
+
+ if (tsmooth > 0.0) {
+ float squared = ray_length * ray_length;
+ strength *= squared / (tsmooth + squared);
+ }
+
+ quadratic = strength;
+ linear = (strength * ray_length);
+ constant = (strength * ray_length * ray_length);
+}
+
+void node_object_info(mat4 objmat, out vec3 location, out float object_index, out float material_index, out float random)
+{
+ location = objmat[3].xyz;
+ object_index = 0.0;
+ material_index = 0.0;
+ random = 0.0;
+}
+
+void node_normal_map(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal)
+{
+ vec3 B = tangent.w * cross(normal, tangent.xyz);
+
+ outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal;
+ outnormal = normalize(outnormal);
+}
+
+void node_bump(float strength, float dist, float height, vec3 N, vec3 surf_pos, float invert, out vec3 result)
+{
+ if (invert != 0.0) {
+ dist *= -1.0;
+ }
+ vec3 dPdx = dFdx(surf_pos);
+ vec3 dPdy = dFdy(surf_pos);
+
+ /* Get surface tangents from normal. */
+ vec3 Rx = cross(dPdy, N);
+ vec3 Ry = cross(N, dPdx);
+
+ /* Compute surface gradient and determinant. */
+ float det = dot(dPdx, Rx);
+ float absdet = abs(det);
+
+ float dHdx = dFdx(height);
+ float dHdy = dFdy(height);
+ vec3 surfgrad = dHdx * Rx + dHdy * Ry;
+
+ strength = max(strength, 0.0);
+
+ result = normalize(absdet * N - dist * sign(det) * surfgrad);
+ result = normalize(strength * result + (1.0 - strength) * N);
+}
+
+/* output */
+
+void node_output_material(vec4 surface, vec4 volume, float displacement, out vec4 result)
+{
+ result = surface;
+}
+
+void node_output_world(vec4 surface, vec4 volume, out vec4 result)
+{
+ result = surface;
+}
+
+void node_output_lamp(vec4 surface, out vec4 result)
+{
+ result = surface;
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_material_utils.glsl b/source/blender/gpu/shaders/gpu_shader_material_utils.glsl
new file mode 100644
index 00000000000..dd7cabfb0cd
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_material_utils.glsl
@@ -0,0 +1,1113 @@
+/* ------- Defines -------- */
+
+/* Number of default opengl lights */
+#define NUM_LIGHTS 3
+
+/* SSR Maximum iterations */
+#define MAX_SSR_REFINE_STEPS 8
+
+/* Importance sampling Max iterations */
+#define BSDF_SAMPLES 1024
+
+/* Linearly Transformed Cosines */
+#define LTC_LUT_SIZE 64
+
+/* needed for uint type and bitwise operation */
+#extension GL_EXT_gpu_shader4: enable
+
+/* ------- PBR Uniform --------- */
+
+uniform samplerCube unfprobe;
+uniform sampler2D unfreflect;
+uniform sampler2D unfrefract;
+uniform sampler2D unfltcmat;
+uniform sampler2D unfltcmag;
+uniform sampler2D unfscenebuf;
+uniform sampler2D unfdepthbuf;
+uniform sampler2D unfbackfacebuf;
+uniform sampler2D unfjitter;
+uniform sampler1D unflutsamples;
+uniform float unflodfactor;
+uniform vec2 unfbsdfsamples;
+uniform vec3 unfsh0;
+uniform vec3 unfsh1;
+uniform vec3 unfsh2;
+uniform vec3 unfsh3;
+uniform vec3 unfsh4;
+uniform vec3 unfsh5;
+uniform vec3 unfsh6;
+uniform vec3 unfsh7;
+uniform vec3 unfsh8;
+uniform vec3 unfprobepos;
+uniform vec3 unfplanarvec;
+uniform vec3 unfssrparam;
+uniform vec4 unfssaoparam;
+uniform vec4 unfclip;
+uniform mat4 unfprobecorrectionmat;
+uniform mat4 unfplanarreflectmat;
+uniform mat4 unfpixelprojmat;
+
+/* ------- Global Variables -------- */
+
+vec3 worldpos, refpos;
+vec3 viewnor, viewi;
+vec3 planarfac, planarvec;
+vec3 I, B, Ht;
+vec2 jitternoise = vec2(0.0);
+
+/* ------- Convenience functions --------- */
+
+vec3 mul(mat3 m, vec3 v) { return m * v; }
+mat3 mul(mat3 m1, mat3 m2) { return m1 * m2; }
+
+float saturate(float a) { return clamp(a, 0.0, 1.0); }
+vec2 saturate(vec2 a) { return vec2(saturate(a.x), saturate(a.y)); }
+vec3 saturate(vec3 a) { return vec3(saturate(a.x), saturate(a.y), saturate(a.z)); }
+vec4 saturate(vec4 a) { return vec4(saturate(a.x), saturate(a.y), saturate(a.z), saturate(a.w)); }
+
+float distance_squared(vec2 a, vec2 b) { a -= b; return dot(a, a); }
+float distance_squared(vec3 a, vec3 b) { a -= b; return dot(a, a); }
+
+float hypot(float x, float y) { return sqrt(x*x + y*y); }
+
+float inverse_distance(vec3 V) { return max( 1 / length(V), 1e-8); }
+
+vec4 bufferFetch(sampler2D buf, ivec2 texelpos, int lod)
+{
+#if __VERSION__ < 130
+ return texture2DLod(buf, (vec2(texelpos) + 0.5) / (unfclip.zw / exp2(float(lod))), float(lod));
+#else
+ return texelFetch(buf, texelpos, lod);
+#endif
+}
+
+/* --------- Noise Utils Functions --------- */
+
+void generated_from_orco(vec3 orco, out vec3 generated)
+{
+ generated = orco * 0.5 + vec3(0.5);
+}
+
+int floor_to_int(float x)
+{
+ return int(floor(x));
+}
+
+int quick_floor(float x)
+{
+ return int(x) - ((x < 0) ? 1 : 0);
+}
+
+#ifdef BIT_OPERATIONS
+float integer_noise(int n)
+{
+ int nn;
+ n = (n + 1013) & 0x7fffffff;
+ n = (n >> 13) ^ n;
+ nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
+ return 0.5 * (float(nn) / 1073741824.0);
+}
+
+uint hash(uint kx, uint ky, uint kz)
+{
+#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+#define final(a, b, c) \
+{ \
+ c ^= b; c -= rot(b, 14); \
+ a ^= c; a -= rot(c, 11); \
+ b ^= a; b -= rot(a, 25); \
+ c ^= b; c -= rot(b, 16); \
+ a ^= c; a -= rot(c, 4); \
+ b ^= a; b -= rot(a, 14); \
+ c ^= b; c -= rot(b, 24); \
+}
+ // now hash the data!
+ uint a, b, c, len = 3u;
+ a = b = c = 0xdeadbeefu + (len << 2u) + 13u;
+
+ c += kz;
+ b += ky;
+ a += kx;
+ final (a, b, c);
+
+ return c;
+#undef rot
+#undef final
+}
+
+uint hash(int kx, int ky, int kz)
+{
+ return hash(uint(kx), uint(ky), uint(kz));
+}
+
+float bits_to_01(uint bits)
+{
+ float x = float(bits) * (1.0 / float(0xffffffffu));
+ return x;
+}
+
+float cellnoise(vec3 p)
+{
+ int ix = quick_floor(p.x);
+ int iy = quick_floor(p.y);
+ int iz = quick_floor(p.z);
+
+ return bits_to_01(hash(uint(ix), uint(iy), uint(iz)));
+}
+
+vec3 cellnoise_color(vec3 p)
+{
+ float r = cellnoise(p);
+ float g = cellnoise(vec3(p.y, p.x, p.z));
+ float b = cellnoise(vec3(p.y, p.z, p.x));
+
+ return vec3(r, g, b);
+}
+#endif // BIT_OPERATIONS
+
+float floorfrac(float x, out int i)
+{
+ i = floor_to_int(x);
+ return x - i;
+}
+
+/* --------- Geometric Utils Functions --------- */
+
+float linear_depth(float z)
+{
+ if (gl_ProjectionMatrix[3][3] == 0.0) {
+ float zn = unfclip.x; // camera z near
+ float zf = unfclip.y; // camera z far
+ return (zn * zf) / (z * (zn - zf) + zf);
+ }
+ else {
+ float zf = unfclip.y; // camera z far
+ return (z * 2.0 - 1.0) * zf;
+ }
+}
+
+float backface_depth(ivec2 texelpos, int lod)
+{
+ return bufferFetch(unfbackfacebuf, texelpos, lod).r;
+}
+
+float frontface_depth(ivec2 texelpos, int lod)
+{
+ return bufferFetch(unfdepthbuf, texelpos, lod).r;
+}
+
+float backface_depth_linear(ivec2 texelpos, int lod)
+{
+ float depth = linear_depth(bufferFetch(unfbackfacebuf, texelpos, lod).r);
+
+ /* background case */
+ if (depth == 1.0)
+ return -1e16;
+ else
+ return -depth;
+}
+
+float frontface_depth_linear(ivec2 texelpos, int lod)
+{
+ float depth = linear_depth(bufferFetch(unfdepthbuf, texelpos, lod).r);
+
+ /* background case */
+ if (depth == 1.0)
+ return -1e16;
+ else
+ return -depth;
+}
+
+vec3 position_from_depth(vec2 uv, float depth)
+{
+ vec3 pos;
+ float homcoord = gl_ProjectionMatrix[2][3] * depth + gl_ProjectionMatrix[3][3];
+ pos.x = gl_ProjectionMatrixInverse[0][0] * (uv.x * 2.0 - 1.0) * homcoord;
+ pos.y = gl_ProjectionMatrixInverse[1][1] * (uv.y * 2.0 - 1.0) * homcoord;
+ pos.z = depth;
+ return pos;
+}
+
+vec2 uv_from_position(vec3 position)
+{
+ vec4 projvec = gl_ProjectionMatrix * vec4(position, 1.0);
+ return (projvec.xy / projvec.w) * 0.5 + 0.5;
+}
+
+vec3 axis_angle_rotation(vec3 point, vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = cos(angle);
+ float oc = 1.0 - c;
+
+ mat3 mat = mat3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s,
+ oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,
+ oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c);
+
+ return mat * point;
+}
+
+void viewN_to_shadeN(vec3 N, out vec3 shadeN)
+{
+ shadeN = normalize(-N);
+}
+
+void make_orthonormals(vec3 N, out vec3 T, out vec3 B)
+{
+ vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0,0.0,1.0) : vec3(1.0,0.0,0.0);
+ T = normalize( cross(UpVector, N) );
+ B = cross(N, T);
+}
+
+void make_orthonormals_tangent(vec3 N, inout vec3 T, out vec3 B)
+{
+ B = normalize( cross(N, T) );
+ T = cross(B, N);
+}
+
+void default_coordinates(vec3 attr_orco, out vec3 generated)
+{
+ generated = attr_orco * 0.5 + vec3(0.5);
+}
+
+vec3 from_tangent_to_world( vec3 vector, vec3 N, vec3 T, vec3 B)
+{
+ return T * vector.x + B * vector.y + N * vector.z;
+}
+
+vec3 from_world_to_tangent( vec3 vector, vec3 N, vec3 T, vec3 B)
+{
+ return vec3( dot(T, vector), dot(B, vector), dot(N, vector));
+}
+
+float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
+{
+ return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection);
+}
+
+vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
+{
+ float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal);
+ return lineorigin + linedirection * dist;
+}
+
+/* from cycles ray_aligned_disk_intersect */
+float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
+{
+ /* aligned plane normal */
+ vec3 L = planeorigin - lineorigin;
+ float diskdist = length(L);
+ vec3 planenormal = -normalize(L);
+ return -diskdist / dot(planenormal, linedirection);
+}
+
+vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
+{
+ float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin);
+ if (dist < 0) {
+ /* if intersection is behind we fake the intersection to be
+ * really far and (hopefully) not inside the radius of interest */
+ dist = 1e16;
+ }
+ return lineorigin + linedirection * dist;
+}
+
+void most_representative_point(float l_radius, float l_lenght, vec3 l_Y,
+ float l_distance, vec3 R, inout vec3 L,
+ inout float roughness, inout float energy_conservation)
+{
+ L = l_distance * L;
+
+ /* Tube Light */
+ if(l_lenght>0){
+ roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */
+
+ // Use Tube Light specular instead of a plane.
+ // Energy conservation
+ // asin(x) is angle to sphere, atan(x) is angle to disk, saturate(x) is free and in the middle
+ //float LineAngle = clamp( l_lenght / l_distance, 0.0, 1.0);
+
+ //energy_conservation *= roughness / clamp(roughness + 0.5 * LineAngle, 0.0, 1.0);
+
+ /* Closest point on line segment to ray */
+ vec3 L01 = l_Y * l_lenght;
+ vec3 L0 = L - 0.5 * L01;
+ vec3 L1 = L + 0.5 * L01;
+
+ /* Shortest distance */
+ float a = l_lenght * l_lenght;
+ float b = dot(R, L01);
+ float t = saturate(dot( L0, b*R - L01 ) / (a - b*b));
+ L = L0 + t * L01;
+ }
+
+ /* Sphere Light */
+ if(l_radius>0){
+ roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */
+
+ /* energy preservation */
+ float SphereAngle = saturate(l_radius / l_distance);
+ energy_conservation *= pow(roughness / saturate(roughness + 0.5 * SphereAngle), 2.0);
+
+ /* sphere light */
+ //float dist = line_aligned_plane_intersect_dist(vec3(0.0), R, L);
+ float dist = dot(L, R);
+ vec3 closest_point_on_ray = dist * R;
+ vec3 center_to_ray = closest_point_on_ray - L;
+ /* closest point on sphere */
+ L = L + center_to_ray * saturate(l_radius * inverse_distance(center_to_ray));
+ }
+
+ L = normalize(L);
+}
+
+void most_representative_point_disk(float l_radius, float l_distance, vec3 R, inout vec3 L,
+ inout float roughness, inout float energy_conservation)
+{
+ L = l_distance * L;
+
+ /* Sphere Light */
+ if(l_radius>0){
+ roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */
+
+ /* energy preservation */
+ float SphereAngle = saturate(l_radius / l_distance);
+ energy_conservation *= pow(roughness / saturate(roughness + 0.5 * SphereAngle), 2.0);
+
+ /* sphere light */
+ vec3 closest_point_on_ray = line_plane_intersect(vec3(0.0), R, L, -normalize(L));
+ vec3 center_to_ray = closest_point_on_ray - L;
+ /* closest point on sphere */
+ L = L + center_to_ray * saturate(l_radius * inverse_distance(center_to_ray));
+ }
+
+ L = normalize(L);
+}
+
+vec2 area_light_prepass(mat4 lmat, inout float areasizex, inout float areasizey, vec2 areascale, out vec3 lampx, out vec3 lampy, out vec3 lampz)
+{
+ lampx = normalize( (lmat * vec4(1.0,0.0,0.0,0.0) ).xyz ); //lamp right axis
+ lampy = normalize( (lmat * vec4(0.0,1.0,0.0,0.0) ).xyz ); //lamp up axis
+ lampz = normalize( (lmat * vec4(0.0,0.0,1.0,0.0) ).xyz ); //lamp projection axis
+
+ areasizex *= areascale.x;
+ areasizey *= areascale.y;
+
+ return vec2(areasizex / 2.0, areasizey / 2.0);
+}
+
+
+/* ------ Linearly Transformed Cosines ------ */
+/* From https://eheitzresearch.wordpress.com/415-2/ */
+
+void area_light_points(vec3 lco, vec2 halfsize, vec3 lampx, vec3 lampy, out vec3 points[4])
+{
+ vec3 ex = lampx * halfsize.x;
+ vec3 ey = lampy * halfsize.y;
+
+ points[0] = lco - ex + ey;
+ points[1] = lco + ex + ey;
+ points[2] = lco + ex - ey;
+ points[3] = lco - ex - ey;
+}
+
+float integrate_edge(vec3 v1, vec3 v2)
+{
+ float cosTheta = dot(v1, v2);
+ cosTheta = clamp(cosTheta, -0.9999, 0.9999);
+
+ float theta = acos(cosTheta);
+ float res = cross(v1, v2).z * theta / sin(theta);
+
+ return res;
+}
+
+int clip_quad_to_horizon(inout vec3 L[5])
+{
+ /* detect clipping config */
+ int config = 0;
+ if (L[0].z > 0.0) config += 1;
+ if (L[1].z > 0.0) config += 2;
+ if (L[2].z > 0.0) config += 4;
+ if (L[3].z > 0.0) config += 8;
+
+ /* clip */
+ int n = 0;
+
+ if (config == 0)
+ {
+ /* clip all */
+ }
+ else if (config == 1) /* V1 clip V2 V3 V4 */
+ {
+ n = 3;
+ L[1] = -L[1].z * L[0] + L[0].z * L[1];
+ L[2] = -L[3].z * L[0] + L[0].z * L[3];
+ }
+ else if (config == 2) /* V2 clip V1 V3 V4 */
+ {
+ n = 3;
+ L[0] = -L[0].z * L[1] + L[1].z * L[0];
+ L[2] = -L[2].z * L[1] + L[1].z * L[2];
+ }
+ else if (config == 3) /* V1 V2 clip V3 V4 */
+ {
+ n = 4;
+ L[2] = -L[2].z * L[1] + L[1].z * L[2];
+ L[3] = -L[3].z * L[0] + L[0].z * L[3];
+ }
+ else if (config == 4) /* V3 clip V1 V2 V4 */
+ {
+ n = 3;
+ L[0] = -L[3].z * L[2] + L[2].z * L[3];
+ L[1] = -L[1].z * L[2] + L[2].z * L[1];
+ }
+ else if (config == 5) /* V1 V3 clip V2 V4) impossible */
+ {
+ n = 0;
+ }
+ else if (config == 6) /* V2 V3 clip V1 V4 */
+ {
+ n = 4;
+ L[0] = -L[0].z * L[1] + L[1].z * L[0];
+ L[3] = -L[3].z * L[2] + L[2].z * L[3];
+ }
+ else if (config == 7) /* V1 V2 V3 clip V4 */
+ {
+ n = 5;
+ L[4] = -L[3].z * L[0] + L[0].z * L[3];
+ L[3] = -L[3].z * L[2] + L[2].z * L[3];
+ }
+ else if (config == 8) /* V4 clip V1 V2 V3 */
+ {
+ n = 3;
+ L[0] = -L[0].z * L[3] + L[3].z * L[0];
+ L[1] = -L[2].z * L[3] + L[3].z * L[2];
+ L[2] = L[3];
+ }
+ else if (config == 9) /* V1 V4 clip V2 V3 */
+ {
+ n = 4;
+ L[1] = -L[1].z * L[0] + L[0].z * L[1];
+ L[2] = -L[2].z * L[3] + L[3].z * L[2];
+ }
+ else if (config == 10) /* V2 V4 clip V1 V3) impossible */
+ {
+ n = 0;
+ }
+ else if (config == 11) /* V1 V2 V4 clip V3 */
+ {
+ n = 5;
+ L[4] = L[3];
+ L[3] = -L[2].z * L[3] + L[3].z * L[2];
+ L[2] = -L[2].z * L[1] + L[1].z * L[2];
+ }
+ else if (config == 12) /* V3 V4 clip V1 V2 */
+ {
+ n = 4;
+ L[1] = -L[1].z * L[2] + L[2].z * L[1];
+ L[0] = -L[0].z * L[3] + L[3].z * L[0];
+ }
+ else if (config == 13) /* V1 V3 V4 clip V2 */
+ {
+ n = 5;
+ L[4] = L[3];
+ L[3] = L[2];
+ L[2] = -L[1].z * L[2] + L[2].z * L[1];
+ L[1] = -L[1].z * L[0] + L[0].z * L[1];
+ }
+ else if (config == 14) /* V2 V3 V4 clip V1 */
+ {
+ n = 5;
+ L[4] = -L[0].z * L[3] + L[3].z * L[0];
+ L[0] = -L[0].z * L[1] + L[1].z * L[0];
+ }
+ else if (config == 15) /* V1 V2 V3 V4 */
+ {
+ n = 4;
+ }
+
+ if (n == 3)
+ L[3] = L[0];
+ if (n == 4)
+ L[4] = L[0];
+
+ return n;
+}
+
+vec2 ltc_coords(float cosTheta, float roughness)
+{
+ float theta = acos(cosTheta);
+ vec2 coords = vec2(roughness, theta/(0.5*3.14159));
+
+ /* scale and bias coordinates, for correct filtered lookup */
+ return coords * (LTC_LUT_SIZE - 1.0) / LTC_LUT_SIZE + 0.5 / LTC_LUT_SIZE;
+}
+
+mat3 ltc_matrix(vec2 coord)
+{
+ /* load inverse matrix */
+ vec4 t = texture2D(unfltcmat, coord);
+ mat3 Minv = mat3(
+ vec3( 1, 0, t.y),
+ vec3( 0, t.z, 0),
+ vec3(t.w, 0, t.x)
+ );
+
+ return Minv;
+}
+
+float ltc_evaluate(vec3 N, vec3 V, vec3 P, mat3 Minv, vec3 points[4])
+{
+ /* construct orthonormal basis around N */
+ vec3 T1, T2;
+ T1 = normalize(V - N*dot(V, N));
+ T2 = cross(N, T1);
+
+ /* rotate area light in (T1, T2, R) basis */
+ Minv = mul(Minv, transpose(mat3(T1, T2, N)));
+
+ /* polygon (allocate 5 vertices for clipping) */
+ vec3 L[5];
+ L[0] = mul(Minv, points[0] - P);
+ L[1] = mul(Minv, points[1] - P);
+ L[2] = mul(Minv, points[2] - P);
+ L[3] = mul(Minv, points[3] - P);
+
+ int n = clip_quad_to_horizon(L);
+
+ if (n == 0)
+ return 0.0;
+
+ /* project onto sphere */
+ L[0] = normalize(L[0]);
+ L[1] = normalize(L[1]);
+ L[2] = normalize(L[2]);
+ L[3] = normalize(L[3]);
+ L[4] = normalize(L[4]);
+
+ /* integrate */
+ float sum = 0.0;
+
+ sum += integrate_edge(L[0], L[1]);
+ sum += integrate_edge(L[1], L[2]);
+ sum += integrate_edge(L[2], L[3]);
+ if (n >= 4)
+ sum += integrate_edge(L[3], L[4]);
+ if (n == 5)
+ sum += integrate_edge(L[4], L[0]);
+
+ return abs(sum);
+}
+
+/* ------- Fresnel ---------*/
+
+float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)
+{
+ /* compute fresnel reflectance without explicitly computing
+ * the refracted direction */
+ float c = abs(dot(Incoming, Normal));
+ float g = eta * eta - 1.0 + c * c;
+ float result;
+
+ if(g > 0.0) {
+ g = sqrt(g);
+ float A =(g - c)/(g + c);
+ float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0);
+ result = 0.5 * A * A *(1.0 + B * B);
+ }
+ else {
+ result = 1.0; /* TIR (no refracted component) */
+ }
+
+ return result;
+}
+
+
+/* ------- Energy Conversion for lights ------- */
+/* from Sebastien Lagarde
+ * course_notes_moving_frostbite_to_pbr.pdf */
+
+float sphere_energy(float radius)
+{
+ radius = max(radius, 1e-8);
+ return 0.25 * M_1_PI2 / (radius*radius) /* 1/(4*r²*Pi²) */
+ * M_PI2 * 10.0; /* XXX : Empirical, Fit cycles power */
+}
+
+float disk_energy(float radius)
+{
+ radius = max(radius, 1e-8);
+ return M_1_PI2 / (radius*radius); /* 1/(r²*Pi²) */
+}
+
+float tube_energy(float radius, float width)
+{
+ radius = max(radius, 1e-8);
+ return 0.5 * M_1_PI2 / (radius * (width + 2 * radius)); /* 1/(4*r²*Pi²) + 1/(2*r*w*Pi²) */
+}
+
+float rectangle_energy(float width, float height)
+{
+ return M_1_PI / (width*height) /* 1/(w*h*Pi) */
+ * 80.0; /* XXX : Empirical, Fit cycles power */
+}
+
+/* ------- Ambient Occlusion ------- */
+/* from Sebastien Lagarde
+ * course_notes_moving_frostbite_to_pbr.pdf */
+
+float specular_occlusion(float NV, float AO, float roughness)
+{
+#ifdef USE_SSAO
+ return saturate(pow(NV + AO, roughness) - 1.0 + AO);
+#else
+ return 1.0;
+#endif
+}
+
+/* ----------- Parallax Correction -------------- */
+
+void parallax_correct_ray(inout vec3 L)
+{
+#ifdef CORRECTION_NONE
+ return;
+#else
+ vec3 localray = (unfprobecorrectionmat * vec4(L, 0.0)).xyz;
+ vec3 localpos = (unfprobecorrectionmat * vec4(worldpos, 1.0)).xyz;
+
+#ifdef CORRECTION_BOX
+ /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ */
+ vec3 firstplane = (vec3( 1.0) - localpos) / localray;
+ vec3 secondplane = (vec3(-1.0) - localpos) / localray;
+ vec3 furthestplane = max(firstplane, secondplane);
+ float dist = min(furthestplane.x, min(furthestplane.y, furthestplane.z));
+#endif
+
+#ifdef CORRECTION_ELLIPSOID
+ /* ray sphere intersection */
+ float a = dot(localray, localray);
+ float b = dot(localray, localpos);
+ float c = dot(localpos, localpos) - 1;
+
+ float dist = 1e15;
+ float determinant = b * b - a * c;
+ if (determinant >= 0)
+ dist = (sqrt(determinant) - b) / a;
+#endif
+
+ /* Use Distance in WS directly to recover intersection */
+ vec3 intersection = worldpos + L * dist;
+ L = intersection - unfprobepos;
+#endif
+}
+
+/* ----------- Probe sampling wrappers -------------- */
+
+void vector_prepass(vec3 viewpos, vec3 worldnor, mat4 invviewmat, mat4 viewmat)
+{
+ worldpos = (invviewmat * vec4(viewpos, 1.0)).xyz;
+
+ shade_view(viewpos, viewi);
+ direction_transform_m4v3(viewi, invviewmat, I);
+
+#ifdef PLANAR_PROBE
+ /* View Normals */
+ direction_transform_m4v3(worldnor, viewmat, viewnor);
+
+ /* transposing plane orientation to view space */
+ direction_transform_m4v3(unfplanarvec, viewmat, planarvec);
+
+ planarfac.x = dot(planarvec, vec3(1.0, 0.0, 0.0));
+ planarfac.y = dot(planarvec, vec3(0.0, 1.0, 0.0));
+ planarfac.z = -viewpos.z + 1.0;
+
+ vec4 proj = unfplanarreflectmat * vec4(worldpos, 1.0);
+ refpos = proj.xyz/proj.w;
+#endif
+}
+
+#if 0
+float distance_based_roughness(float dist_intersection_to_shaded, float dist_intersection_to_reflectioncam, float roughness)
+{
+ /* from Sebastien Lagarde
+ * course_notes_moving_frostbite_to_pbr.pdf */
+ float newroughness = clamp(roughness * dist_intersection_to_shaded / dist_intersection_to_reflectioncam, 0, roughness);
+ return mix(newroughness, roughness, roughness);
+}
+#endif
+
+vec2 get_distorded_refl_uv(sampler2D planarprobe, vec2 Xi)
+{
+ vec2 distortion = vec2(dot(viewnor, vec3(1.0, 0.0, 0.0)) - planarfac.x,
+ dot(viewnor, vec3(0.0, 1.0, 0.0)) - planarfac.y);
+ distortion += Xi;
+
+ /* modulate intensity by distance to the viewer and by distance to the reflected object */
+ float dist_view_to_shaded = planarfac.z;
+ float dist_intersection_to_reflectioncam = texture2D(planarprobe, refpos.xy + Xi / dist_view_to_shaded).a;
+ float dist_intersection_to_shaded = dist_intersection_to_reflectioncam - dist_view_to_shaded; /* depth in alpha */
+
+ /* test in case of background */
+ if (dist_intersection_to_shaded > 0.0) {
+ float distortion_scale = abs(dot(viewi * dist_intersection_to_shaded, -planarvec));
+ distortion *= distortion_scale / (dist_view_to_shaded + 1.0);
+ }
+
+ return refpos.xy + distortion;
+}
+
+vec4 sample_probe_pdf(vec3 cubevec, float pdf)
+{
+ vec4 sample;
+
+ float lod = unflodfactor - 0.5 * log2(pdf);
+
+ parallax_correct_ray(cubevec);
+ sample = textureCubeLod(unfprobe, cubevec, lod);
+
+ srgb_to_linearrgb(sample, sample);
+ return sample;
+}
+
+vec4 sample_probe_pdf(sampler2D planarprobe, vec3 cubevec, float roughness, float pdf)
+{
+ vec4 sample;
+
+ float lod = unflodfactor - 0.5 * log2(pdf);
+
+ parallax_correct_ray(cubevec);
+ sample = textureCubeLod(unfprobe, cubevec, lod);
+
+#ifdef PLANAR_PROBE
+ vec4 sample_plane = vec4(0.0);
+ vec2 co = get_distorded_refl_uv(planarprobe, Ht.xy);
+ if (co.x > 0.0 && co.x < 1.0 && co.y > 0.0 && co.y < 1.0)
+ sample_plane = texture2DLod(planarprobe, co, lod);
+ else
+ sample_plane = sample;
+ sample = mix(sample_plane, sample, clamp(roughness * 2.0 - 0.5, 0.0, 1.0));
+#endif
+
+ srgb_to_linearrgb(sample, sample);
+ return sample;
+}
+
+vec4 sample_reflect_pdf(vec3 L, float roughness, float pdf)
+{
+ return sample_probe_pdf(unfreflect, L, roughness, pdf);
+}
+
+vec4 sample_refract_pdf(vec3 L, float roughness, float pdf)
+{
+ return sample_probe_pdf(unfrefract, L, roughness, pdf);
+}
+
+vec4 sample_probe(sampler2D planarprobe, vec3 cubevec)
+{
+ vec4 sample;
+
+ /* Cubemap */
+ parallax_correct_ray(cubevec);
+ sample = textureCube(unfprobe, cubevec);
+
+#ifdef PLANAR_PROBE
+ /* Planar */
+ vec2 co = get_distorded_refl_uv(planarprobe, vec2(0.0));
+ if (co.x > 0.0 && co.x < 1.0 && co.y > 0.0 && co.y < 1.0)
+ sample = texture2D(planarprobe, co);
+#endif
+
+ srgb_to_linearrgb(sample, sample);
+ return sample;
+}
+
+vec4 sample_reflect(vec3 L)
+{
+ return sample_probe(unfreflect, L);
+}
+
+vec4 sample_refract(vec3 L)
+{
+ return sample_probe(unfrefract, L);
+}
+
+/* ------- Sampling Random Ray -------- */
+
+void setup_noise(vec2 fragcoord)
+{
+ const int NOISE_SIZE = 64;
+ ivec2 texel = ivec2(mod(fragcoord.x, NOISE_SIZE), mod(fragcoord.y, NOISE_SIZE));
+#if __VERSION__ < 130
+ jitternoise = texture2DLod(unfjitter, (vec2(texel) + 0.5) / float(NOISE_SIZE), 0.0).rg; /* Global variable */
+#else
+ jitternoise = texelFetch(unfjitter, texel, 0).rg; /* Global variable */
+#endif
+}
+
+vec3 hammersley_3d(float i, float invsamplenbr)
+{
+ vec3 Xi; /* Theta, cos(Phi), sin(Phi) */
+
+ Xi.x = i * invsamplenbr; /* i/samples */
+ Xi.x = fract(Xi.x + jitternoise.x);
+
+ int u = int(mod(i + jitternoise.y * BSDF_SAMPLES, BSDF_SAMPLES));
+
+#if __VERSION__ < 130
+ Xi.yz = texture1DLod(unflutsamples, (float(u) + 0.5) / float(BSDF_SAMPLES), 0.0).rg; /* Global variable */
+#else
+ Xi.yz = texelFetch(unflutsamples, u, 0).rg; /* Global variable */
+#endif
+ return Xi;
+}
+
+vec3 hammersley_3d(float i)
+{
+ return hammersley_3d(i, unfbsdfsamples.y);
+}
+
+/* ------- Screen Space Raycasting ---------*/
+
+/* By Morgan McGuire and Michael Mara at Williams College 2014
+ * Released as open source under the BSD 2-Clause License
+ * http://opensource.org/licenses/BSD-2-Clause
+ * http://casual-effects.blogspot.fr/2014/08/screen-space-ray-tracing.html */
+void swapIfBigger(inout float a, inout float b)
+{
+ if (a > b) {
+ float temp = a;
+ a = b;
+ b = temp;
+ }
+}
+
+#if 1 /* Linear 2D raymarching */
+
+/* 2D raycast */
+bool raycast(vec3 ray_origin, vec3 ray_dir, out float hitstep, out vec2 hitpixel, out vec3 hitco)
+{
+ /* ssr_parameters */
+ float nearz = -unfclip.x; /* Near plane distance (Negative number) */
+ float farz = -unfclip.y; /* Far plane distance (Negative number) */
+
+ /* Clip ray to a near plane in 3D */
+ float ray_length = 1e16;
+ if ((ray_origin.z + ray_dir.z * ray_length) > nearz)
+ ray_length = (nearz - ray_origin.z) / ray_dir.z;
+
+ vec3 ray_end = ray_dir * ray_length + ray_origin;
+
+ /* Project into screen space */
+ vec4 H0 = unfpixelprojmat * vec4(ray_origin, 1.0);
+ vec4 H1 = unfpixelprojmat * vec4(ray_end, 1.0);
+
+ /* There are a lot of divisions by w that can be turned into multiplications
+ * at some minor precision loss...and we need to interpolate these 1/w values
+ * anyway. */
+ float k0 = 1.0 / H0.w;
+ float k1 = 1.0 / H1.w;
+
+ /* Switch the original points to values that interpolate linearly in 2D */
+ vec3 Q0 = ray_origin * k0;
+ vec3 Q1 = ray_end * k1;
+
+ /* Screen-space endpoints */
+ vec2 P0 = H0.xy * k0;
+ vec2 P1 = H1.xy * k1;
+
+ /* [Optional clipping to frustum sides here] */
+
+ /* Initialize to off screen */
+ hitpixel = vec2(-1.0, -1.0);
+
+ /* If the line is degenerate, make it cover at least one pixel
+ * to not have to handle zero-pixel extent as a special case later */
+ P1 += vec2((distance_squared(P0, P1) < 0.0001) ? 0.01 : 0.0);
+
+ vec2 delta = P1 - P0;
+
+ /* Permute so that the primary iteration is in x to reduce large branches later.
+ * After this, "x" is the primary iteration direction and "y" is the secondary one
+ * If it is a more-vertical line, create a permutation that swaps x and y in the output
+ * and directly swizzle the inputs. */
+ bool permute = false;
+ if (abs(delta.x) < abs(delta.y)) {
+ permute = true;
+ delta = delta.yx;
+ P1 = P1.yx;
+ P0 = P0.yx;
+ }
+
+ /* Track the derivatives */
+ float step_sign = sign(delta.x);
+ float invdx = step_sign / delta.x;
+ vec2 dP = vec2(step_sign, invdx * delta.y);
+ vec3 dQ = (Q1 - Q0) * invdx;
+ float dk = (k1 - k0) * invdx;
+
+ /* Slide each value from the start of the ray to the end */
+ vec4 pqk = vec4(P0, Q0.z, k0);
+
+ /* Scale derivatives by the desired pixel stride */
+ vec4 dPQK = vec4(dP, dQ.z, dk) * 1.0;
+
+ bool hit = false;
+
+ /* We track the ray depth at +/- 1/2 pixel to treat pixels as clip-space solid
+ * voxels. Because the depth at -1/2 for a given pixel will be the same as at
+ * +1/2 for the previous iteration, we actually only have to compute one value
+ * per iteration. */
+ float prev_zmax = ray_origin.z;
+ float zmax, zmin;
+
+ /* P1.x is never modified after this point, so pre-scale it by
+ * the step direction for a signed comparison */
+ float end = P1.x * step_sign;
+
+ for (hitstep = 0.0; hitstep < unfssrparam.x && !hit; hitstep++)
+ {
+ /* Ray finished & no hit*/
+ if ((pqk.x * step_sign) > end) break;
+
+ /* step through current cell */
+ pqk += dPQK;
+
+ hitpixel = permute ? pqk.yx : pqk.xy;
+ zmin = prev_zmax;
+ zmax = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);
+ prev_zmax = zmax;
+ swapIfBigger(zmin, zmax);
+
+ float frontface = frontface_depth_linear(ivec2(hitpixel), 0);
+
+ if (zmax < frontface) {
+ /* Below surface */
+#ifdef USE_BACKFACE
+ float backface = backface_depth_linear(ivec2(hitpixel), 0);
+#else
+ float backface = frontface - unfssrparam.z;
+#endif
+ hit = (zmin > backface);
+ }
+ }
+
+ /* Hit coordinate in 3D */
+ hitco = (Q0 + dQ * hitstep) / pqk.w;
+
+ return hit;
+}
+
+#else /* Hierarchical raymarching */
+
+vec3 point_on_line(vec3 origin, vec3 direction, float dist)
+{
+ return origin + direction * dist;
+}
+
+vec2 get_cell(vec2 ray, vec2 cellcount)
+{
+ return floor(ray * cellcount);
+}
+
+vec3 intersect_cell_boundary(vec3 o, vec3 d, vec2 cell, vec2 cellcount, vec2 crossstep, vec2 crossoffset)
+{
+ vec2 newcell = ((cell + crossstep) / cellcount) + crossoffset;
+ vec2 delta = (newcell - o.xy) / d.xy;
+ float t = min(delta.x, delta.y);
+
+ return point_on_line(o, d, t);
+}
+
+vec2 get_cell_count(float level)
+{
+ return floor(unfclip.zw / exp2(level));
+}
+
+bool crossed_cell_boundary(vec2 a, vec2 b)
+{
+ return (a.x != b.x) || (a.y != b.y);
+}
+
+#define HIZ_MAX_LEVEL 8.0
+
+bool raycast(vec3 ray_origin, vec3 ray_dir, out float hitstep, out vec2 hitpixel, out vec3 hitco)
+{
+ float miplvl = 0.0; /* Start level */
+ vec2 cellcount = get_cell_count(0.0);
+
+ /* Invert Z component so we can work with the raw depth buffer which is positive */
+ ray_dir.z *= -1;
+
+ /* scale vector such that z is 1.0f (maximum depth) */
+ ray_dir = ray_dir.xyz / abs(ray_dir.z);
+
+ vec2 crossstep, crossoffset;
+ vec2 cross_epsilon = vec2(0.2) / cellcount; /* Enough to get to the next cell */
+ crossstep.x = (ray_dir.x > 0.0) ? 1.0 : -1.0;
+ crossstep.y = (ray_dir.y > 0.0) ? 1.0 : -1.0;
+ crossoffset = crossstep * cross_epsilon;
+ crossstep = saturate(crossstep);
+
+ /* set current ray to original screen coordinate and depth */
+ vec3 ray;
+ ray.xy = uv_from_position(ray_origin);
+ ray.z = frontface_depth(ivec2(ray.xy * cellcount), int(miplvl));
+
+ /* project ray origin to the near clip plane */
+ vec3 o = point_on_line(ray, ray_dir, -ray.z);
+
+ /* Cross first cell (texel) to not get self-intersection */
+ vec2 cell = get_cell(ray.xy, cellcount);
+ ray = intersect_cell_boundary(o, ray_dir, cell, cellcount, crossstep, crossoffset);
+
+ for (hitstep = 0.0; hitstep < unfssrparam.x && miplvl >= 0.0; hitstep++)
+ {
+ cellcount = get_cell_count(miplvl);
+ cell = get_cell(ray.xy, cellcount);
+
+ float frontface = frontface_depth(ivec2(cell), int(miplvl));
+
+ /* Try intersecting the depth plane
+ * or stay in place if there is an intersection */
+ vec3 raytmp = point_on_line(o, ray_dir, max(ray.z, frontface));
+ /* Go to the next cell and go up a level */
+ vec2 celltmp = get_cell(raytmp.xy, cellcount);
+
+ /* If ray crossed a cell then it's not blocked */
+ if (crossed_cell_boundary(cell, celltmp)) {
+ /* Go to the next cell and go up a level */
+ raytmp = intersect_cell_boundary(o, ray_dir, cell, cellcount, crossstep, crossoffset);
+ miplvl = min(HIZ_MAX_LEVEL, miplvl + 2.0);
+ }
+
+ ray = raytmp;
+ --miplvl;
+ }
+
+ hitpixel = ray.xy * unfclip.zw;
+ hitco = position_from_depth(ray.xy, linear_depth(ray.z));
+
+ return (miplvl < 0.0 && ray.z < 1.0 && ray.z > 0.0 && ray_dir.z > 0 && ray.x > 0 && ray.x < 1 && ray.y > 0 && ray.y < 1);
+}
+#endif
+
+float ssr_contribution(vec3 ray_origin, float hitstep, bool hit, inout vec3 hitco)
+{
+ /* ssr_parameters */
+ float maxstep = unfssrparam.x; /* Maximum number of iteration when raymarching */
+ float attenuation = unfssrparam.y; /* Attenuation factor for screen edges and ray step fading */
+
+ /* ray step fade */
+ float stepfade = saturate((1.0 - hitstep / maxstep) * attenuation);
+
+ /* screen edges fade */
+ vec4 co = gl_ProjectionMatrix * vec4(hitco, 1.0);
+ co.xy /= co.w;
+ hitco.xy = co.xy * 0.5 + 0.5;
+ float maxdimension = saturate(max(abs(co.x), abs(co.y)));
+ float screenfade = saturate((0.999999 - maxdimension) * attenuation);
+
+ return smoothstep(0.0, 1.0, stepfade * screenfade) * float(hit);
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_probe_sh_compute_frag.glsl b/source/blender/gpu/shaders/gpu_shader_probe_sh_compute_frag.glsl
new file mode 100644
index 00000000000..6bf60407f57
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_probe_sh_compute_frag.glsl
@@ -0,0 +1,108 @@
+uniform samplerCube probe;
+
+#define M_4PI 12.5663706143591729
+
+const mat3 CUBE_ROTATIONS[6] = mat3[](
+ mat3(vec3( 0.0, 0.0, -1.0),
+ vec3( 0.0, -1.0, 0.0),
+ vec3(-1.0, 0.0, 0.0)),
+ mat3(vec3( 0.0, 0.0, 1.0),
+ vec3( 0.0, -1.0, 0.0),
+ vec3( 1.0, 0.0, 0.0)),
+ mat3(vec3( 1.0, 0.0, 0.0),
+ vec3( 0.0, 0.0, 1.0),
+ vec3( 0.0, -1.0, 0.0)),
+ mat3(vec3( 1.0, 0.0, 0.0),
+ vec3( 0.0, 0.0, -1.0),
+ vec3( 0.0, 1.0, 0.0)),
+ mat3(vec3( 1.0, 0.0, 0.0),
+ vec3( 0.0, -1.0, 0.0),
+ vec3( 0.0, 0.0, -1.0)),
+ mat3(vec3(-1.0, 0.0, 0.0),
+ vec3( 0.0, -1.0, 0.0),
+ vec3( 0.0, 0.0, 1.0)));
+
+float srgb_to_linearrgb(float c)
+{
+ if(c < 0.04045)
+ return (c < 0.0) ? 0.0: c * (1.0 / 12.92);
+ else
+ return pow((c + 0.055)*(1.0/1.055), 2.4);
+}
+
+void srgb_to_linearrgb(vec4 col_from, out vec4 col_to)
+{
+ col_to.r = srgb_to_linearrgb(col_from.r);
+ col_to.g = srgb_to_linearrgb(col_from.g);
+ col_to.b = srgb_to_linearrgb(col_from.b);
+ col_to.a = col_from.a;
+}
+
+vec3 get_cubemap_vector(vec2 co, int face)
+{
+ return normalize(CUBE_ROTATIONS[face] * vec3(co * 2.0 - 1.0, 1.0));
+}
+
+float area_element(float x, float y)
+{
+ return atan(x * y, sqrt(x * x + y * y + 1));
+}
+
+float texel_solid_angle(vec2 co, float halfpix)
+{
+ vec2 v1 = (co - vec2(halfpix)) * 2.0 - 1.0;
+ vec2 v2 = (co + vec2(halfpix)) * 2.0 - 1.0;
+
+ return area_element(v1.x, v1.y) - area_element(v1.x, v2.y) - area_element(v2.x, v1.y) + area_element(v2.x, v2.y);
+}
+
+void main()
+{
+ const float pixstep = 1.0 / CUBEMAP_RES;
+ const float halfpix = pixstep / 2.0;
+
+ vec2 pos = ceil(gl_FragCoord.xy);
+
+ if (pos.x > 3.0 || pos.y > 3.0) {
+ gl_FragColor = vec4(0.0);
+ return;
+ }
+
+ int shnbr = int(pos.x + (pos.y - 1) * 3);
+ float accum = 0.0;
+ vec3 sh = vec3(0.0);
+
+ for (int face = 0; face < 6; ++face)
+ {
+ for (float x = halfpix; x < 1.0; x += pixstep)
+ {
+ for (float y = halfpix; y < 1.0; y += pixstep)
+ {
+ float shcoef;
+
+ vec2 facecoord = vec2(x,y);
+ vec3 cubevec = get_cubemap_vector(facecoord, face);
+ float weight = texel_solid_angle(facecoord, halfpix);
+
+ if (shnbr == 1) shcoef = 0.282095;
+ else if (shnbr == 2) shcoef = -0.488603 * cubevec.z * 2.0 / 3.0;
+ else if (shnbr == 3) shcoef = 0.488603 * cubevec.y * 2.0 / 3.0;
+ else if (shnbr == 4) shcoef = -0.488603 * cubevec.x * 2.0 / 3.0;
+ else if (shnbr == 5) shcoef = 1.092548 * cubevec.x * cubevec.z * 1.0 / 4.0;
+ else if (shnbr == 6) shcoef = -1.092548 * cubevec.z * cubevec.y * 1.0 / 4.0;
+ else if (shnbr == 7) shcoef = 0.315392 * (3.0 * cubevec.y * cubevec.y - 1.0) * 1.0 / 4.0;
+ else if (shnbr == 8) shcoef = 1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0;
+ else /* (shnbr == 9) */ shcoef = 0.546274 * (cubevec.x * cubevec.x - cubevec.z * cubevec.z) * 1.0 / 4.0;
+
+ vec4 sample = textureCubeLod(probe, cubevec, 0.0);
+ srgb_to_linearrgb(sample, sample);
+ sh += sample.rgb * shcoef * weight;
+ accum += weight;
+ }
+ }
+ }
+
+ sh *= M_4PI / accum;
+
+ gl_FragColor = vec4(sh, 1.0);
+} \ No newline at end of file
diff --git a/source/blender/gpu/shaders/gpu_shader_probe_sh_compute_vert.glsl b/source/blender/gpu/shaders/gpu_shader_probe_sh_compute_vert.glsl
new file mode 100644
index 00000000000..9f5e788d902
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_probe_sh_compute_vert.glsl
@@ -0,0 +1,5 @@
+void main()
+{
+ /* We are using gl_FragCoord inside the fragment shader so nothing fancy here */
+ gl_Position = gl_Vertex;
+}