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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/draw/engines/eevee/shaders/hair_lib.glsl')
-rw-r--r--source/blender/draw/engines/eevee/shaders/hair_lib.glsl330
1 files changed, 330 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/hair_lib.glsl b/source/blender/draw/engines/eevee/shaders/hair_lib.glsl
new file mode 100644
index 00000000000..489ee44dd0f
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/hair_lib.glsl
@@ -0,0 +1,330 @@
+#ifdef HAIR_SHADER_FIBERS
+
+#define M_PI 3.1415926535897932384626433832795
+
+mat4 translate(vec3 co)
+{
+ return mat4(1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ co.x, co.y, co.z, 1.0);
+}
+
+mat4 rotateX(float angle)
+{
+ float ca = cos(angle);
+ float sa = sin(angle);
+ return mat4(1.0, 0.0, 0.0, 0.0,
+ 0.0, ca, sa, 0.0,
+ 0.0, -sa, ca, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+mat4 rotateY(float angle)
+{
+ float ca = cos(angle);
+ float sa = sin(angle);
+ return mat4(ca, 0.0, sa, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ -sa, 0.0, ca, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+mat4 rotateZ(float angle)
+{
+ float ca = cos(angle);
+ float sa = sin(angle);
+ return mat4(ca, sa, 0.0, 0.0,
+ -sa, ca, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+/* Hair Displacement */
+
+/* Note: The deformer functions below calculate a new location vector
+ * as well as a new direction (aka "normal"), using the partial derivatives of the transformation.
+ *
+ * Each transformation function can depend on the location L as well as the curve parameter t:
+ *
+ * Lnew = f(L, t)
+ * => dLnew/dt = del f/del L * dL/dt + del f/del t
+ *
+ * The first term is the Jacobian of the function f, dL/dt is the original direction vector.
+ * Some more information can be found here:
+ * https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch42.html
+ */
+
+/* Hairs tend to stick together and run in parallel.
+ * The effect increases with distance from the root,
+ * as the stresses pulling fibers apart decrease.
+ */
+struct ClumpParams
+{
+ /* Relative strand thickness at the tip.
+ * (0.0, 1.0]
+ * 0.0 : Strand clumps into a single line
+ * 1.0 : Strand does not clump at all
+ * (> 1.0 is possible but not recommended)
+ */
+ float thickness;
+};
+
+/* Hairs often don't have a circular cross section, but are somewhat flattened.
+ * This creates the local bending which results in the typical curly hair geometry.
+ */
+struct CurlParams
+{
+ /* Radius of the curls.
+ * >= 0.0
+ */
+ float radius;
+ /* Steepness of curls
+ * < 0.0 : Clockwise curls
+ * > 0.0 : Anti-clockwise curls
+ */
+ float angle;
+};
+
+struct DeformParams
+{
+ /* Strand tapering with distance from the root.
+ * < 1.0 : Taper is concave (recommended)
+ * = 1.0 : Taper is linear
+ * > 1.0 : Taper is convex (not recommended)
+ */
+ float taper;
+
+ ClumpParams clump;
+ CurlParams curl;
+};
+
+void deform_taper(DeformParams params, float t, out float taper, out float dtaper)
+{
+ taper = pow(t, params.taper);
+ dtaper = (t > 0.0) ? taper * params.taper / t : 0.0;
+}
+
+void deform_clump(DeformParams params,
+ float t, float tscale, mat4 target_matrix,
+ inout vec3 co, inout vec3 tang)
+{
+ float taper, dtaper;
+ deform_taper(params, t, taper, dtaper);
+ float factor = (1.0 - params.clump.thickness) * taper;
+ float dfactor = (1.0 - params.clump.thickness) * dtaper;
+
+ vec3 target_co = target_matrix[3].xyz;
+ vec3 target_tang = target_matrix[0].xyz;
+ vec3 nco = co + (target_co - co) * factor;
+ vec3 ntang = normalize(tang + (target_tang - tang) * factor + (target_co - co) * dfactor);
+
+ co = nco;
+ tang = ntang;
+}
+
+void deform_curl(DeformParams params,
+ float t, float tscale,
+ inout mat4 target_matrix)
+{
+ float pitch = 2.0*M_PI * params.curl.radius * tan(params.curl.angle);
+ float turns = tscale / (params.curl.radius * tan(params.curl.angle));
+ float angle = t * turns;
+ mat4 local_mat = rotateX(angle) * translate(vec3(0.0, params.curl.radius, 0.0)) * rotateY(params.curl.angle);
+ target_matrix = target_matrix * local_mat;
+}
+
+void deform_fiber(DeformParams params,
+ float t, float tscale, mat4 target_matrix,
+ inout vec3 loc, inout vec3 tang)
+{
+ deform_curl(params, t, tscale, target_matrix);
+ deform_clump(params, t, tscale, target_matrix, loc, tang);
+}
+
+/*===================================*/
+/* Hair Interpolation */
+
+#define FIBER_RIBBON
+
+uniform sampler2D fiber_data;
+
+uniform int fiber_start;
+uniform int strand_map_start;
+uniform int strand_vertex_start;
+
+uniform float ribbon_width;
+uniform vec2 viewport_size;
+
+#define INDEX_INVALID -1
+
+vec2 read_texdata(int offset)
+{
+ ivec2 offset2 = ivec2(offset % HAIR_SHADER_TEX_WIDTH, offset / HAIR_SHADER_TEX_WIDTH);
+ return texelFetch(fiber_data, offset2, 0).rg;
+}
+
+mat4 mat4_from_vectors(vec3 nor, vec3 tang, vec3 co)
+{
+ tang = normalize(tang);
+ vec3 xnor = normalize(cross(nor, tang));
+ return mat4(vec4(tang, 0.0), vec4(xnor, 0.0), vec4(cross(tang, xnor), 0.0), vec4(co, 1.0));
+}
+
+void get_strand_data(int index, out int start, out int count)
+{
+ int offset = strand_map_start + index;
+ vec2 a = read_texdata(offset);
+
+ start = floatBitsToInt(a.r);
+ count = floatBitsToInt(a.g);
+}
+
+void get_strand_vertex(int index, out vec3 co, out vec3 nor, out vec3 tang)
+{
+ int offset = strand_vertex_start + index * 5;
+ vec2 a = read_texdata(offset);
+ vec2 b = read_texdata(offset + 1);
+ vec2 c = read_texdata(offset + 2);
+ vec2 d = read_texdata(offset + 3);
+ vec2 e = read_texdata(offset + 4);
+
+ co = vec3(a.rg, b.r);
+ nor = vec3(b.g, c.rg);
+ tang = vec3(d.rg, e.r);
+}
+
+void get_strand_root(int index, out vec3 co)
+{
+ int offset = strand_vertex_start + index * 5;
+ vec2 a = read_texdata(offset);
+ vec2 b = read_texdata(offset + 1);
+
+ co = vec3(a.rg, b.r);
+}
+
+void get_fiber_data(int fiber_index, out ivec4 parent_index, out vec4 parent_weight, out vec3 pos)
+{
+ int offset = fiber_start + fiber_index * 6;
+ vec2 a = read_texdata(offset);
+ vec2 b = read_texdata(offset + 1);
+ vec2 c = read_texdata(offset + 2);
+ vec2 d = read_texdata(offset + 3);
+ vec2 e = read_texdata(offset + 4);
+ vec2 f = read_texdata(offset + 5);
+
+ parent_index = ivec4(floatBitsToInt(a.rg), floatBitsToInt(b.rg));
+ parent_weight = vec4(c.rg, d.rg);
+ pos = vec3(e.rg, f.r);
+}
+
+void interpolate_parent_curve_full(int index, float curve_param, out vec3 co, out vec3 nor, out vec3 tang, out vec3 rootco)
+{
+ int start, count;
+ get_strand_data(index, start, count);
+
+ get_strand_root(start, rootco);
+
+#if 0 // Don't have to worry about out-of-bounds segment here, as long as lerpfac becomes 0.0 when curve_param==1.0
+ float maxlen = float(count - 1);
+ float arclength = curve_param * maxlen;
+ int segment = min(int(arclength), count - 2);
+ float lerpfac = arclength - min(floor(arclength), maxlen - 1.0);
+#else
+ float maxlen = float(count - 1);
+ float arclength = curve_param * maxlen;
+ int segment = int(arclength);
+ float lerpfac = arclength - floor(arclength);
+#endif
+
+ vec3 co0, nor0, tang0;
+ vec3 co1, nor1, tang1;
+ get_strand_vertex(start + segment, co0, nor0, tang0);
+ get_strand_vertex(start + segment + 1, co1, nor1, tang1);
+
+ co = mix(co0, co1, lerpfac) - rootco;
+ nor = mix(nor0, nor1, lerpfac);
+ tang = mix(tang0, tang1, lerpfac);
+}
+
+void interpolate_parent_curve(int index, float curve_param, out vec3 co, out vec3 tang)
+{
+ vec3 nor;
+ vec3 rootco;
+ interpolate_parent_curve_full(index, curve_param, co, nor, tang, rootco);
+}
+
+void interpolate_vertex(int fiber_index, float curve_param,
+ out vec3 co, out vec3 tang,
+ out mat4 target_matrix)
+{
+ co = vec3(0.0);
+ tang = vec3(0.0);
+ target_matrix = mat4(1.0);
+
+ ivec4 parent_index;
+ vec4 parent_weight;
+ vec3 rootco;
+ get_fiber_data(fiber_index, parent_index, parent_weight, rootco);
+
+ if (parent_index.x != INDEX_INVALID) {
+ vec3 pco, pnor, ptang, prootco;
+ interpolate_parent_curve_full(parent_index.x, curve_param, pco, pnor, ptang, prootco);
+ co += parent_weight.x * pco;
+ tang += parent_weight.x * normalize(ptang);
+
+ target_matrix = mat4_from_vectors(pnor, ptang, pco + prootco);
+ }
+ if (parent_index.y != INDEX_INVALID) {
+ vec3 pco, ptang;
+ interpolate_parent_curve(parent_index.y, curve_param, pco, ptang);
+ co += parent_weight.y * pco;
+ tang += parent_weight.y * normalize(ptang);
+ }
+ if (parent_index.z != INDEX_INVALID) {
+ vec3 pco, ptang;
+ interpolate_parent_curve(parent_index.z, curve_param, pco, ptang);
+ co += parent_weight.z * pco;
+ tang += parent_weight.z * normalize(ptang);
+ }
+ if (parent_index.w != INDEX_INVALID) {
+ vec3 pco, ptang;
+ interpolate_parent_curve(parent_index.w, curve_param, pco, ptang);
+ co += parent_weight.w * pco;
+ tang += parent_weight.w * normalize(ptang);
+ }
+
+ co += rootco;
+ tang = normalize(tang);
+}
+
+void hair_fiber_get_vertex(int fiber_index, float curve_param, mat4 ModelViewMatrix, out vec3 pos, out vec3 nor, out vec2 view_offset)
+{
+ vec3 target_loc;
+ mat4 target_matrix;
+ interpolate_vertex(fiber_index, curve_param, pos, nor, target_matrix);
+
+ DeformParams deform_params;
+ deform_params.taper = 2.0;
+ deform_params.clump.thickness = 0.15;
+ deform_params.curl.radius = 0.1;
+ deform_params.curl.angle = 0.2;
+ // TODO define proper curve scale, independent of subdivision!
+ //deform_fiber(deform_params, curve_param, 1.0, target_matrix, pos, nor);
+
+#ifdef FIBER_RIBBON
+ float ribbon_side = (float(gl_VertexID % 2) - 0.5) * ribbon_width;
+ {
+ vec4 view_nor = ModelViewMatrix * vec4(nor, 0.0);
+ view_offset = vec2(view_nor.y, -view_nor.x);
+ float L = length(view_offset);
+ if (L > 0.0) {
+ view_offset *= ribbon_side / (L * viewport_size);
+ }
+ }
+#else
+ view_offset = vec2(0.0);
+#endif
+}
+
+#endif /*HAIR_SHADER_FIBERS*/