|
| 1 | +// https://www.shadertoy.com/view/4djSRW |
| 2 | +vec2 mx_hextile_hash(vec2 p) |
| 3 | +{ |
| 4 | + vec3 p3 = fract(vec3(p.x, p.y, p.x) * vec3(0.1031, 0.1030, 0.0973)); |
| 5 | + p3 += dot(p3, vec3(p3.y, p3.z, p3.x) + 33.33); |
| 6 | + return fract((vec2(p3.x, p3.x) + vec2(p3.y, p3.z)) * vec2(p3.z, p3.y)); |
| 7 | +} |
| 8 | + |
| 9 | +// Christophe Schlick. “Fast Alternatives to Perlin’s Bias and Gain Functions”. |
| 10 | +// In Graphics Gems IV, Morgan Kaufmann, 1994, pages 401–403. |
| 11 | +// https://dept-info.labri.fr/~schlick/DOC/gem2.html |
| 12 | +float mx_schlick_gain(float x, float r) |
| 13 | +{ |
| 14 | + float rr = clamp(r, 0.001, 0.999); // to avoid glitch |
| 15 | + float a = (1.0 / rr - 2.0) * (1.0 - 2.0 * x); |
| 16 | + return (x < 0.5) ? x / (a + 1.0) : (a - x) / (a - 1.0); |
| 17 | +} |
| 18 | + |
| 19 | +struct HextileData |
| 20 | +{ |
| 21 | + vec2 coord1; |
| 22 | + vec2 coord2; |
| 23 | + vec2 coord3; |
| 24 | + vec3 weights; |
| 25 | + float rot_radian1; |
| 26 | + float rot_radian2; |
| 27 | + float rot_radian3; |
| 28 | + vec2 ddx1; |
| 29 | + vec2 ddx2; |
| 30 | + vec2 ddx3; |
| 31 | + vec2 ddy1; |
| 32 | + vec2 ddy2; |
| 33 | + vec2 ddy3; |
| 34 | +}; |
| 35 | + |
| 36 | +// Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics |
| 37 | +// Techniques (JCGT), vol. 11, no. 2, 77-94, 2022 |
| 38 | +// http://jcgt.org/published/0011/03/05/ |
| 39 | +HextileData mx_hextile_coord( |
| 40 | + vec2 coord, |
| 41 | + float rotation, |
| 42 | + vec2 rotation_range, |
| 43 | + float scale, |
| 44 | + vec2 scale_range, |
| 45 | + float offset, |
| 46 | + vec2 offset_range) |
| 47 | +{ |
| 48 | + float sqrt3_2 = sqrt(3.0) * 2.0; |
| 49 | + |
| 50 | + // scale coord to maintain the original fit |
| 51 | + vec2 st = coord * sqrt3_2; |
| 52 | + |
| 53 | + // skew input space into simplex triangle grid |
| 54 | + // (1, 0, -tan(30), 2*tan(30)) |
| 55 | + mat2 to_skewed = mat2(1.0, 0.0, -0.57735027, 1.15470054); |
| 56 | + vec2 st_skewed = to_skewed * st; |
| 57 | + |
| 58 | + // barycentric weights |
| 59 | + vec2 st_frac = fract(st_skewed); |
| 60 | + vec3 temp = vec3(st_frac.x, st_frac.y, 0.0); |
| 61 | + temp.z = 1.0 - temp.x - temp.y; |
| 62 | + |
| 63 | + float s = step(0.0, -temp.z); |
| 64 | + float s2 = 2.0 * s - 1.0; |
| 65 | + |
| 66 | + float w1 = -temp.z * s2; |
| 67 | + float w2 = s - temp.y * s2; |
| 68 | + float w3 = s - temp.x * s2; |
| 69 | + |
| 70 | + // vertex IDs |
| 71 | + ivec2 base_id = ivec2(floor(st_skewed)); |
| 72 | + int si = int(s); |
| 73 | + ivec2 id1 = base_id + ivec2(si, si); |
| 74 | + ivec2 id2 = base_id + ivec2(si, 1 - si); |
| 75 | + ivec2 id3 = base_id + ivec2(1 - si, si); |
| 76 | + |
| 77 | + // tile center |
| 78 | + mat2 inv_skewed = mat2(1.0, 0.0, 0.5, 1.0 / 1.15470054); |
| 79 | + vec2 ctr1 = inv_skewed * vec2(id1) / vec2(sqrt3_2); |
| 80 | + vec2 ctr2 = inv_skewed * vec2(id2) / vec2(sqrt3_2); |
| 81 | + vec2 ctr3 = inv_skewed * vec2(id3) / vec2(sqrt3_2); |
| 82 | + |
| 83 | + // reuse hash for performance |
| 84 | + vec2 seed_offset = vec2(0.12345); // to avoid some zeros |
| 85 | + vec2 rand1 = mx_hextile_hash(vec2(id1) + seed_offset); |
| 86 | + vec2 rand2 = mx_hextile_hash(vec2(id2) + seed_offset); |
| 87 | + vec2 rand3 = mx_hextile_hash(vec2(id3) + seed_offset); |
| 88 | + |
| 89 | + // randomized rotation matrix |
| 90 | + vec2 rr = mx_radians(rotation_range); |
| 91 | + float rv1 = mix(rr.x, rr.y, rand1.x * rotation); |
| 92 | + float rv2 = mix(rr.x, rr.y, rand2.x * rotation); |
| 93 | + float rv3 = mix(rr.x, rr.y, rand3.x * rotation); |
| 94 | + float sin_r1 = sin(rv1); |
| 95 | + float sin_r2 = sin(rv2); |
| 96 | + float sin_r3 = sin(rv3); |
| 97 | + float cos_r1 = cos(rv1); |
| 98 | + float cos_r2 = cos(rv2); |
| 99 | + float cos_r3 = cos(rv3); |
| 100 | + mat2 rm1 = mat2(cos_r1, -sin_r1, sin_r1, cos_r1); |
| 101 | + mat2 rm2 = mat2(cos_r2, -sin_r2, sin_r2, cos_r2); |
| 102 | + mat2 rm3 = mat2(cos_r3, -sin_r3, sin_r3, cos_r3); |
| 103 | + |
| 104 | + // randomized scale |
| 105 | + vec2 sr = scale_range; |
| 106 | + vec2 scale1 = vec2(mix(1.0, mix(sr.x, sr.y, rand1.y), scale)); |
| 107 | + vec2 scale2 = vec2(mix(1.0, mix(sr.x, sr.y, rand2.y), scale)); |
| 108 | + vec2 scale3 = vec2(mix(1.0, mix(sr.x, sr.y, rand3.y), scale)); |
| 109 | + |
| 110 | + // randomized offset |
| 111 | + vec2 offset1 = mix(vec2(offset_range.x), vec2(offset_range.y), rand1 * offset); |
| 112 | + vec2 offset2 = mix(vec2(offset_range.x), vec2(offset_range.y), rand2 * offset); |
| 113 | + vec2 offset3 = mix(vec2(offset_range.x), vec2(offset_range.y), rand3 * offset); |
| 114 | + |
| 115 | + HextileData tile_data; |
| 116 | + tile_data.weights = vec3(w1, w2, w3); |
| 117 | + tile_data.rot_radian1 = rv1; |
| 118 | + tile_data.rot_radian2 = rv2; |
| 119 | + tile_data.rot_radian3 = rv3; |
| 120 | + |
| 121 | + // get coord |
| 122 | + tile_data.coord1 = ((coord - ctr1) * rm1 / scale1) + ctr1 + offset1; |
| 123 | + tile_data.coord2 = ((coord - ctr2) * rm2 / scale2) + ctr2 + offset2; |
| 124 | + tile_data.coord3 = ((coord - ctr3) * rm3 / scale3) + ctr3 + offset3; |
| 125 | + |
| 126 | + // derivatives |
| 127 | + vec2 ddx = dFdx(coord); |
| 128 | + vec2 ddy = dFdy(coord); |
| 129 | + tile_data.ddx1 = ddx * rm1 / scale1; |
| 130 | + tile_data.ddx2 = ddx * rm2 / scale2; |
| 131 | + tile_data.ddx3 = ddx * rm3 / scale3; |
| 132 | + tile_data.ddy1 = ddy * rm1 / scale1; |
| 133 | + tile_data.ddy2 = ddy * rm2 / scale2; |
| 134 | + tile_data.ddy3 = ddy * rm3 / scale3; |
| 135 | + |
| 136 | + return tile_data; |
| 137 | +} |
0 commit comments