-
-
Save lyuma/ab525fdf76e3af5951bf99300110a099 to your computer and use it in GitHub Desktop.
shader_type spatial; | |
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx; | |
uniform vec4 albedo : hint_color; | |
uniform sampler2D texture_albedo : hint_albedo; | |
uniform vec3 uv1_scale; | |
uniform vec3 uv1_offset; | |
uniform float specular; | |
uniform float metallic; | |
uniform float roughness : hint_range(0,1); | |
uniform sampler2D decal_texture; | |
uniform vec3 decal_scale = vec3(1.0,1.0,0.0); | |
uniform vec3 decal_offset = vec3(0.0,0.0,0.0); | |
uniform float decal_upper_fade = 1.0; | |
uniform float decal_lower_fade = 0.5; | |
uniform vec4 decal_modulate : hint_color = vec4(1.0,1.0,1.0,1.0); | |
uniform float decal_albedo_mix = 1.0; | |
uniform mat4 decal_xform; | |
varying vec3 world_vertex; | |
void vertex() { | |
UV=UV*uv1_scale.xy+uv1_offset.xy; | |
world_vertex = (WORLD_MATRIX * vec4(VERTEX, 1.0)).xyz; | |
} | |
void fragment() { | |
vec2 base_uv = UV; | |
vec4 albedo_tex = texture(texture_albedo,base_uv); | |
ALBEDO = albedo.rgb * albedo_tex.rgb; | |
METALLIC = metallic; | |
ROUGHNESS = roughness; | |
SPECULAR = specular; | |
vec3 vertex_ddx = dFdx(world_vertex); | |
vec3 vertex_ddy = dFdy(world_vertex); | |
vec3 uv_local = (decal_xform * vec4(world_vertex, 1.0)).xyz; | |
if (!(any(lessThan(uv_local, vec3(0.0, -1.0, 0.0))) || any(greaterThan(uv_local, vec3(1.0))))) { | |
//not out of decal | |
//we need ddx/ddy for mipmaps, so simulate them | |
vec2 ddx = (decal_xform * vec4(vertex_ddx, 0.0)).xz; | |
vec2 ddy = (decal_xform * vec4(vertex_ddy, 0.0)).xz; | |
float fade = pow(1.0 - (uv_local.y > 0.0 ? uv_local.y : -uv_local.y), uv_local.y > 0.0 ? decal_upper_fade : decal_lower_fade); | |
//has albedo | |
vec4 decal_albedo = textureGrad(decal_texture, uv_local.xz * decal_scale.xy + decal_offset.xy, ddx * decal_scale.xy, ddy * decal_scale.xy); | |
decal_albedo *= decal_modulate; | |
decal_albedo.a *= fade; | |
ALBEDO = mix(ALBEDO, decal_albedo.rgb, decal_albedo.a * decal_albedo_mix); | |
} | |
} |
@torchesburn The key is the ddx and ddy arguments passed into the textureGrad
call .
When you perform a traditional texture()
call, it actually takes just the texture and the uv.
What really happens behind the scene is when you write
vec4 albedo = texture(tex, uv);
it is transformed as part of the compilation process to
vec4 albedo = textureGrad(tex, uv, dFdx(uv), dFdy(uv));
As you can see, it is taking the uv coordinate you pass in. And if this is a discontinuous function, the result would be disastrous, because that discontinuity would show up as a very large change in the UV which will make the GPU think you are far away and sample the lowest mipmap for the surrounding 2 pixels.
This sort of issue is common if you do environment maps. Let's take this example:
vec4 albedo = texture(tex, fract(uv))
This would be compiled into
vec4 albedo = textureGrad(tex, fract(uv), dFdx(fract(uv)), dFdy(fract(uv)))
and you can see how this would create a problem where the fract function wraps around from 0.9999 to 0.0001.
In this example, you would solve it by instead using
vec4 albedo = textureGrad(tex, fract(uv), dFdx(uv), dFdy(uv))
Hope this kind of answers the question. There is a bit of math involved in terms of using the original world_vertex
coordinates to calculate the derivative, and then transforming it into the local space so it can be used.
Thanks for the explanation. Really helps 👍
How does this deal with UV Seams?