| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- $input v_position, v_texcoord0
- /*
- * Copyright 2022 Preetish Kakkar. All rights reserved.
- * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
- */
- /*
- Most of the code is inspired/ported from https://github.com/mmikk/hextile-demo/blob/main/hextile-demo/shader_lighting.hlsl
- The basic idea behind the algorithm is to use tiling & blending schema but instead of regular linear blending, the algorithm uses blending operator that prevents visual artifacts caused by linear blending
- We partition the uv-space on a triangle grid and compute the local triangle and the barycentric coordinates inside the triangle. We use a hash function to associate a random offset with each vertex of the triangle
- grid and use this random offset to fetch the example texture.
- Finally, we blend the result using the barycentric coordinates as blending weights.
- */
- #include "../common/common.sh"
- #define M_PI 3.1415926535897932384626433832795
- SAMPLER2D(s_trx_d, 0);
- uniform vec4 u_params;
- #ifndef fmod
- #define fmod(x, y) (x - y * trunc(x / y))
- #endif
- #define moduleOper(a, b) a - (float(b) * floor(a/float(b)))
- #define u_showWeights u_params.x
- #define u_tileRate u_params.y
- #define u_tileRotStrength u_params.z
- #define u_useRegularTiling u_params.w
- vec3 Gain3(vec3 x, float r)
- {
- // increase contrast when r>0.5 and
- // reduce contrast if less
- float k = log(1.0 - r) / log(0.5);
- vec3 s = 2.0 * step(0.5, x);
- vec3 m = 2.0 * (1.0 - s);
- vec3 res = 0.5 * s + 0.25 * m * pow(max(vec3_splat(0.0), s + x * m), vec3_splat(k));
- return res.xyz / (res.x + res.y + res.z);
- }
- mat2 LoadRot2x2(vec2 idx, float rotStrength)
- {
- float angle = abs(idx.x * idx.y) + abs(idx.x + idx.y) + M_PI;
- // remap to +/-pi
- //angle = fmod(angle, 2.0*M_PI);
- if (angle < 0.0) angle += 2.0 * M_PI;
- if (angle > M_PI) angle -= 2.0 * M_PI;
- angle *= rotStrength;
- float cs = cos(angle);
- float si = sin(angle);
- return mat2(cs, -si, si, cs);
- }
- vec2 MakeCenST(vec2 Vertex)
- {
- mat2 invSkewMat = mat2(1.0, 0.5, 0.0, 1.0 / 1.15470054);
- return mul(invSkewMat, Vertex) / (2.0 * sqrt(3.0));
- }
- vec3 ProduceHexWeights(vec3 W, vec2 vertex1, vec2 vertex2, vec2 vertex3)
- {
- vec3 res = vec3_splat(0.0);
- float v1 = moduleOper(((vertex1.x - vertex1.y)), 3.0);
- if (v1 < 0.0) v1 += 3.0;
- float vh = v1 < 2.0 ? (v1 + 1.0) : 0.0;
- float vl = v1 > 0.0 ? (v1 - 1.0) : 2.0;
- float v2 = vertex1.x < vertex3.x ? vl : vh;
- float v3 = vertex1.x < vertex3.x ? vh : vl;
- res.x = v3 == 0.0 ? W.z : (v2 == 0.0 ? W.y : W.x);
- res.y = v3 == 1.0 ? W.z : (v2 == 1.0 ? W.y : W.x);
- res.z = v3 == 2.0 ? W.z : (v2 == 2.0 ? W.y : W.x);
- return res;
- }
- vec2 hash(vec2 p)
- {
- vec2 r = mul(mat2(127.1, 311.7, 269.5, 183.3), p);
- return fract(sin(r) * 43758.5453);
- }
- // Given a point in UV, compute local triangle barycentric coordinates and vertex IDs
- void TriangleGrid(out float w1, out float w2, out float w3,
- out vec2 vertex1, out vec2 vertex2, out vec2 vertex3,
- vec2 uv)
- {
- // Scaling of the input
- uv *= 2.0 * sqrt(3.0); // controls the size of the input with respect to the size of the tiles.
- // Skew input space into simplex triangle grid
- const mat2 gridToSkewedGrid =
- mat2(1.0, -0.57735027, 0.0, 1.15470054);
- vec2 skewedCoord = mul(gridToSkewedGrid, uv);
- vec2 baseId = floor(skewedCoord);
- vec3 temp = vec3(fract(skewedCoord), 0.0);
- temp.z = 1.0 - temp.x - temp.y;
- float s = step(0.0, -temp.z);
- float s2 = 2.0 * s - 1.0;
- w1 = -temp.z * s2;
- w2 = s - temp.y * s2;
- w3 = s - temp.x * s2;
- vertex1 = baseId + vec2(s, s);
- vertex2 = baseId + vec2(s, 1.0 - s);
- vertex3 = baseId + vec2(1.0 - s, s);
- }
- void hex2colTex(out vec4 color, out vec3 weights, vec2 uv,
- float rotStrength, float r)
- {
- // compute uv derivatives
- vec2 dSTdx = dFdx(uv), dSTdy = dFdy(uv);
- // Get triangle info
- float w1, w2, w3;
- vec2 vertex1, vertex2, vertex3;
- TriangleGrid(w1, w2, w3, vertex1, vertex2, vertex3, uv);
- mat2 rot1 = LoadRot2x2(vertex1, rotStrength);
- mat2 rot2 = LoadRot2x2(vertex2, rotStrength);
- mat2 rot3 = LoadRot2x2(vertex3, rotStrength);
- vec2 cen1 = MakeCenST(vertex1);
- vec2 cen2 = MakeCenST(vertex2);
- vec2 cen3 = MakeCenST(vertex3);
- // assign random offset to each triangle vertex
- // this is used later to fetch from texture
- vec2 uv1 = mul(uv - cen1, rot1) + cen1 + hash(vertex1);
- vec2 uv2 = mul(uv - cen2, rot2) + cen2 + hash(vertex2);
- vec2 uv3 = mul(uv - cen3, rot3) + cen3 + hash(vertex3);
- // Fetch input
- // We could simply use texture2D function, however, the screen space derivatives could be broken
- // since we are using random offsets, we use texture2DGrad to make sure that we pass correct derivatives explicitly.
- vec4 c1 = texture2DGrad(s_trx_d, uv1,
- mul(dSTdx, rot1), mul(dSTdy, rot1));
- vec4 c2 = texture2DGrad(s_trx_d, uv2,
- mul(dSTdx, rot2), mul(dSTdy, rot2));
- vec4 c3 = texture2DGrad(s_trx_d, uv3,
- mul(dSTdx, rot3), mul(dSTdy, rot3));
- // use luminance as weight
- vec3 Lw = vec3(0.299, 0.587, 0.114);
- vec3 Dw = vec3(dot(c1.xyz, Lw), dot(c2.xyz, Lw), dot(c3.xyz, Lw));
- Dw = mix(vec3_splat(1.0), Dw, 0.6); // 0.6 is fall off constant
- vec3 W = Dw * pow(vec3(w1, w2, w3), vec3_splat(7.0)); // 7 is g_exp
- W /= (W.x + W.y + W.z);
- if (r != 0.5) W = Gain3(W, r);
- // blend weights with color linearly
- // histogram preserving blending will be better but requires precompution step to create histogram texture
- color = W.x * c1 + W.y * c2 + W.z * c3;
- weights = ProduceHexWeights(W.xyz, vertex1, vertex2, vertex3);
- }
- float GetTileRate()
- {
- return 0.05 * u_tileRate;
- }
- void FetchColorAndWeight(out vec3 color, out vec3 weights, vec2 uv)
- {
- vec4 col4;
- hex2colTex(col4, weights, uv, u_tileRotStrength, 0.7);
- color = col4.xyz;
- }
- void main()
- {
- // actual world space position
- vec3 surfPosInWorld = v_position.xyz;
- vec3 sp = GetTileRate() * surfPosInWorld;
- vec2 uv0 = vec2(sp.x, sp.z);
- if(u_useRegularTiling > 0.0)
- {
- gl_FragColor = vec4(texture2D(s_trx_d, uv0.xy));
- }
- else
- {
- vec3 color, weights;
- FetchColorAndWeight(color, weights, uv0);
- if (u_showWeights > 0.0)
- {
- gl_FragColor = vec4(weights, 1.0);
- }
- else
- {
- gl_FragColor = vec4(color, 1.0);
- }
- }
- }
|