void point_texco_remap_square(vec3 vin, out vec3 vout) { vout = vin * 2.0 - 1.0; } void point_texco_clamp(vec3 vin, sampler2D ima, out vec3 vout) { vec2 half_texel_size = 0.5 / vec2(textureSize(ima, 0).xy); vout = clamp(vin, half_texel_size.xyy, 1.0 - half_texel_size.xyy); } void point_map_to_sphere(vec3 vin, out vec3 vout) { float len = length(vin); float v, u; if (len > 0.0) { if (vin.x == 0.0 && vin.y == 0.0) { u = 0.0; } else { u = (1.0 - atan(vin.x, vin.y) / M_PI) / 2.0; } v = 1.0 - acos(vin.z / len) / M_PI; } else { v = u = 0.0; } vout = vec3(u, v, 0.0); } void point_map_to_tube(vec3 vin, out vec3 vout) { float u, v; v = (vin.z + 1.0) * 0.5; float len = sqrt(vin.x * vin.x + vin.y * vin[1]); if (len > 0.0) { u = (1.0 - (atan(vin.x / len, vin.y / len) / M_PI)) * 0.5; } else { v = u = 0.0; } vout = vec3(u, v, 0.0); } /* 16bits floats limits. Higher/Lower values produce +/-inf. */ #define safe_color(a) (clamp(a, -65520.0, 65520.0)) void node_tex_image_linear(vec3 co, sampler2D ima, out vec4 color, out float alpha) { color = safe_color(texture(ima, co.xy)); alpha = color.a; } void node_tex_image_linear_no_mip(vec3 co, sampler2D ima, out vec4 color, out float alpha) { color = safe_color(textureLod(ima, co.xy, 0.0)); alpha = color.a; } void node_tex_image_nearest(vec3 co, sampler2D ima, out vec4 color, out float alpha) { ivec2 pix = ivec2(fract(co.xy) * textureSize(ima, 0).xy); color = safe_color(texelFetch(ima, pix, 0)); alpha = color.a; } /* @arg f: signed distance to texel center. */ void cubic_bspline_coefs(vec2 f, out vec2 w0, out vec2 w1, out vec2 w2, out vec2 w3) { vec2 f2 = f * f; vec2 f3 = f2 * f; /* Bspline coefs (optimized) */ w3 = f3 / 6.0; w0 = -w3 + f2 * 0.5 - f * 0.5 + 1.0 / 6.0; w1 = f3 * 0.5 - f2 * 1.0 + 2.0 / 3.0; w2 = 1.0 - w0 - w1 - w3; } void node_tex_image_cubic_ex( vec3 co, sampler2D ima, float do_extend, out vec4 color, out float alpha) { vec2 tex_size = vec2(textureSize(ima, 0).xy); co.xy *= tex_size; /* texel center */ vec2 tc = floor(co.xy - 0.5) + 0.5; vec2 w0, w1, w2, w3; cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3); #if 1 /* Optimized version using 4 filtered tap. */ vec2 s0 = w0 + w1; vec2 s1 = w2 + w3; vec2 f0 = w1 / (w0 + w1); vec2 f1 = w3 / (w2 + w3); vec4 final_co; final_co.xy = tc - 1.0 + f0; final_co.zw = tc + 1.0 + f1; if (do_extend == 1.0) { final_co = clamp(final_co, vec4(0.5), tex_size.xyxy - 0.5); } final_co /= tex_size.xyxy; color = safe_color(textureLod(ima, final_co.xy, 0.0)) * s0.x * s0.y; color += safe_color(textureLod(ima, final_co.zy, 0.0)) * s1.x * s0.y; color += safe_color(textureLod(ima, final_co.xw, 0.0)) * s0.x * s1.y; color += safe_color(textureLod(ima, final_co.zw, 0.0)) * s1.x * s1.y; #else /* Reference bruteforce 16 tap. */ color = texelFetch(ima, ivec2(tc + vec2(-1.0, -1.0)), 0) * w0.x * w0.y; color += texelFetch(ima, ivec2(tc + vec2(0.0, -1.0)), 0) * w1.x * w0.y; color += texelFetch(ima, ivec2(tc + vec2(1.0, -1.0)), 0) * w2.x * w0.y; color += texelFetch(ima, ivec2(tc + vec2(2.0, -1.0)), 0) * w3.x * w0.y; color += texelFetch(ima, ivec2(tc + vec2(-1.0, 0.0)), 0) * w0.x * w1.y; color += texelFetch(ima, ivec2(tc + vec2(0.0, 0.0)), 0) * w1.x * w1.y; color += texelFetch(ima, ivec2(tc + vec2(1.0, 0.0)), 0) * w2.x * w1.y; color += texelFetch(ima, ivec2(tc + vec2(2.0, 0.0)), 0) * w3.x * w1.y; color += texelFetch(ima, ivec2(tc + vec2(-1.0, 1.0)), 0) * w0.x * w2.y; color += texelFetch(ima, ivec2(tc + vec2(0.0, 1.0)), 0) * w1.x * w2.y; color += texelFetch(ima, ivec2(tc + vec2(1.0, 1.0)), 0) * w2.x * w2.y; color += texelFetch(ima, ivec2(tc + vec2(2.0, 1.0)), 0) * w3.x * w2.y; color += texelFetch(ima, ivec2(tc + vec2(-1.0, 2.0)), 0) * w0.x * w3.y; color += texelFetch(ima, ivec2(tc + vec2(0.0, 2.0)), 0) * w1.x * w3.y; color += texelFetch(ima, ivec2(tc + vec2(1.0, 2.0)), 0) * w2.x * w3.y; color += texelFetch(ima, ivec2(tc + vec2(2.0, 2.0)), 0) * w3.x * w3.y; #endif alpha = color.a; } void node_tex_image_cubic(vec3 co, sampler2D ima, out vec4 color, out float alpha) { node_tex_image_cubic_ex(co, ima, 0.0, color, alpha); } void node_tex_image_cubic_extend(vec3 co, sampler2D ima, out vec4 color, out float alpha) { node_tex_image_cubic_ex(co, ima, 1.0, color, alpha); } void node_tex_image_smart(vec3 co, sampler2D ima, out vec4 color, out float alpha) { /* use cubic for now */ node_tex_image_cubic_ex(co, ima, 0.0, color, alpha); } void tex_box_sample_linear( vec3 texco, vec3 N, sampler2D ima, out vec4 color1, out vec4 color2, out vec4 color3) { /* X projection */ vec2 uv = texco.yz; if (N.x < 0.0) { uv.x = 1.0 - uv.x; } color1 = texture(ima, uv); /* Y projection */ uv = texco.xz; if (N.y > 0.0) { uv.x = 1.0 - uv.x; } color2 = texture(ima, uv); /* Z projection */ uv = texco.yx; if (N.z > 0.0) { uv.x = 1.0 - uv.x; } color3 = texture(ima, uv); } void tex_box_sample_nearest( vec3 texco, vec3 N, sampler2D ima, out vec4 color1, out vec4 color2, out vec4 color3) { /* X projection */ vec2 uv = texco.yz; if (N.x < 0.0) { uv.x = 1.0 - uv.x; } ivec2 pix = ivec2(uv.xy * textureSize(ima, 0).xy); color1 = texelFetch(ima, pix, 0); /* Y projection */ uv = texco.xz; if (N.y > 0.0) { uv.x = 1.0 - uv.x; } pix = ivec2(uv.xy * textureSize(ima, 0).xy); color2 = texelFetch(ima, pix, 0); /* Z projection */ uv = texco.yx; if (N.z > 0.0) { uv.x = 1.0 - uv.x; } pix = ivec2(uv.xy * textureSize(ima, 0).xy); color3 = texelFetch(ima, pix, 0); } void tex_box_sample_cubic( vec3 texco, vec3 N, sampler2D ima, out vec4 color1, out vec4 color2, out vec4 color3) { float alpha; /* X projection */ vec2 uv = texco.yz; if (N.x < 0.0) { uv.x = 1.0 - uv.x; } node_tex_image_cubic_ex(uv.xyy, ima, 0.0, color1, alpha); /* Y projection */ uv = texco.xz; if (N.y > 0.0) { uv.x = 1.0 - uv.x; } node_tex_image_cubic_ex(uv.xyy, ima, 0.0, color2, alpha); /* Z projection */ uv = texco.yx; if (N.z > 0.0) { uv.x = 1.0 - uv.x; } node_tex_image_cubic_ex(uv.xyy, ima, 0.0, color3, alpha); } void tex_box_sample_smart( vec3 texco, vec3 N, sampler2D ima, out vec4 color1, out vec4 color2, out vec4 color3) { tex_box_sample_cubic(texco, N, ima, color1, color2, color3); } void node_tex_image_box(vec3 texco, vec3 N, vec4 color1, vec4 color2, vec4 color3, sampler2D ima, float blend, out vec4 color, out float alpha) { /* project from direction vector to barycentric coordinates in triangles */ N = abs(N); N /= dot(N, vec3(1.0)); /* 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 * EDIT: Now there is only 4 if's. */ float limit = 0.5 + 0.5 * blend; vec3 weight; weight = N.xyz / (N.xyx + N.yzz); weight = clamp((weight - 0.5 * (1.0 - blend)) / max(1e-8, blend), 0.0, 1.0); /* test for mixes between two textures */ if (N.z < (1.0 - limit) * (N.y + N.x)) { weight.z = 0.0; weight.y = 1.0 - weight.x; } else if (N.x < (1.0 - limit) * (N.y + N.z)) { weight.x = 0.0; weight.z = 1.0 - weight.y; } else if (N.y < (1.0 - limit) * (N.x + N.z)) { weight.y = 0.0; weight.x = 1.0 - weight.z; } else { /* last case, we have a mix between three */ weight = ((2.0 - limit) * N + (limit - 1.0)) / max(1e-8, blend); } color = weight.x * color1 + weight.y * color2 + weight.z * color3; alpha = color.a; } void tex_clip_linear(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha) { vec2 tex_size = vec2(textureSize(ima, 0).xy); vec2 minco = min(co.xy, 1.0 - co.xy); minco = clamp(minco * tex_size + 0.5, 0.0, 1.0); float fac = minco.x * minco.y; color = mix(vec4(0.0), icolor, fac); alpha = color.a; } void tex_clip_nearest(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha) { vec4 minco = vec4(co.xy, 1.0 - co.xy); color = (any(lessThan(minco, vec4(0.0)))) ? vec4(0.0) : icolor; alpha = color.a; } void tex_clip_cubic(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha) { vec2 tex_size = vec2(textureSize(ima, 0).xy); co.xy *= tex_size; /* texel center */ vec2 tc = floor(co.xy - 0.5) + 0.5; vec2 w0, w1, w2, w3; cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3); /* TODO Optimize this part. I'm sure there is a smarter way to do that. * Could do that when sampling? */ #define CLIP_CUBIC_SAMPLE(samp, size) \ (float(all(greaterThan(samp, vec2(-0.5)))) * float(all(lessThan(ivec2(samp), itex_size)))) ivec2 itex_size = textureSize(ima, 0).xy; float fac; fac = CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, -1.0), itex_size) * w0.x * w0.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(0.0, -1.0), itex_size) * w1.x * w0.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(1.0, -1.0), itex_size) * w2.x * w0.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(2.0, -1.0), itex_size) * w3.x * w0.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, 0.0), itex_size) * w0.x * w1.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(0.0, 0.0), itex_size) * w1.x * w1.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(1.0, 0.0), itex_size) * w2.x * w1.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(2.0, 0.0), itex_size) * w3.x * w1.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, 1.0), itex_size) * w0.x * w2.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(0.0, 1.0), itex_size) * w1.x * w2.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(1.0, 1.0), itex_size) * w2.x * w2.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(2.0, 1.0), itex_size) * w3.x * w2.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, 2.0), itex_size) * w0.x * w3.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(0.0, 2.0), itex_size) * w1.x * w3.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(1.0, 2.0), itex_size) * w2.x * w3.y; fac += CLIP_CUBIC_SAMPLE(tc + vec2(2.0, 2.0), itex_size) * w3.x * w3.y; #undef CLIP_CUBIC_SAMPLE color = mix(vec4(0.0), icolor, fac); alpha = color.a; } void tex_clip_smart(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha) { tex_clip_cubic(co, ima, icolor, color, alpha); } void node_tex_image_empty(vec3 co, out vec4 color, out float alpha) { color = vec4(0.0); alpha = 0.0; }