/* Godot 3 shader version by Belzecue [https://bsky.app/profile/belzecue.bsky.social]. Based on XorDev's ShaderToy. "Fractal Texturing" by @XorDev While creating a 3D game (https://twitter.com/XorDev/status/1578947873550389248), I came across a problem with my texture quality. I needed something that looked good up close or far away. That's when I developed what I call "fractal texturing". I'm sure it's been done before, but it's new to me and I thought it was quite neat so I'm sharing it here. The concept is quite simple: Instead of sampling a texture at one scale for all pixels, we'll sample at a different scale depending on the pixel's depth. Then by blending smoothly between the scales, we can produce a consistent level of detail. This isn't perfect for all textures (e.g. struggles with bricks) but it's perfect for many natural textures like dirt or grass and works well for my needs. Maybe you'll find a use for it also! Tutorial: https://mini.gmshaders.com/p/gm-shaders-mini-fractal-texturing-1408552 */ shader_type spatial; render_mode async_visible,blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx; const float exp1 = 2.718281828459; const vec4 world_camera_vec4 = vec4(vec3(0.0), 1.0); uniform sampler2D tex_main : hint_albedo; uniform float scale = 1.0; varying vec3 world_camera; /* //Samples at three scales, interpolating between them vec4 fractal_texture(sampler2D tex, vec2 uv, float depth) { //Find the pixel level of detail float LOD = log(depth); //Round LOD down float LOD_floor = floor(LOD); //Compute the fract part for interpolating float LOD_fract = LOD - LOD_floor; //Compute scaled uvs vec2 uv1 = uv / exp(LOD_floor - 1.0); vec2 uv2 = uv / exp(LOD_floor + 0.0); vec2 uv3 = uv / exp(LOD_floor + 1.0); //Sample at 3 scales vec4 tex0 = texture(tex, uv1); vec4 tex1 = texture(tex, uv2); vec4 tex2 = texture(tex, uv3); //Blend samples together return (tex1 + mix(tex0, tex2, LOD_fract)) * 0.5; } */ //Samples at three scales, interpolating between them (with mipmapping) vec4 fractal_texture_mip(sampler2D tex, vec2 uv, float depth) { //Find the pixel level of detail float LOD = log(depth); //Round LOD down float LOD_floor = floor(LOD); //Compute the fract part for interpolating float LOD_fract = LOD - LOD_floor; //Compute scaled uvs vec2 uv1 = uv / exp(LOD_floor - 1.0); vec2 uv2 = uv / exp(LOD_floor + 0.0); vec2 uv3 = uv / exp(LOD_floor + 1.0); //Compute continous derivitives vec2 dx = dFdx(uv) / depth * exp1; vec2 dy = dFdy(uv) / depth * exp1; //Sample at 3 scales vec4 tex0 = textureGrad(tex, uv1, dx, dy); vec4 tex1 = textureGrad(tex, uv2, dx, dy); vec4 tex2 = textureGrad(tex, uv3, dx, dy); //Blend samples together return (tex1 + mix(tex0, tex2, LOD_fract)) * 0.5; } void vertex() { world_camera = CAMERA_POSITION_WORLD; } void fragment() { //Center coordinates vec2 uv = UV-0.5; // Apply scale vec2 coords = uv * scale; //Compute pixel depth vec4 a = CAMERA_MATRIX * vec4(VERTEX, 1.0); float scale2 = distance(world_camera, a.xyz); float depth = length(vec3(uv, 1.0)) * scale2; vec4 col = fractal_texture_mip(tex_main, coords, depth); //Output results ALBEDO = col.rgb; }