diff options
Diffstat (limited to 'source/blender/gpu/shaders')
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; +} |