Created
November 18, 2020 04:47
-
-
Save lyuma/ab525fdf76e3af5951bf99300110a099 to your computer and use it in GitHub Desktop.
decal shader for Godot Engine
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
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); | |
} | |
} |
Thanks for the explanation. Really helps 👍
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@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
it is transformed as part of the compilation process to
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:
This would be compiled into
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
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.