Created
February 15, 2025 13:25
-
-
Save belzecue/e067ec00ed7cb87ad0adc5e3a03ec248 to your computer and use it in GitHub Desktop.
Fractal texturing lets you use one texture to efficiently cover a large mesh and will stay sharp at any distance, especially close up. It also solves the problem of texture repetition really well. Give it a try below. I've even used it with a 64-pixel texture and it works great.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment