Realistic Tree shader
Tree shader, can be used both for realistic and stylized trees based on the settings, it’s recommended to use the same material for both trunk and leaves.
for a tree model to work it needs to have the vertex colors of the trunk set to RGB 1.0, 0.0, 0.0
the main scattering function works by checking the distance from the center of the leaves and sampling a gradient with that
Shader code
shader_type spatial;
render_mode cull_disabled, ambient_light_disabled;
group_uniforms leaf_material;
uniform sampler2D leaf_texture : source_color, repeat_disable, filter_linear_mipmap;
uniform sampler2D leaf_normal : hint_normal;
uniform float leaf_normal_map_depth = -2.0;
uniform float leaf_alpha_scissors_threshold : hint_range(0.0, 1.0, 0.01) = 0.4;
uniform float leaf_roughness : hint_range(0.0, 1.0, 0.01) = 0.8;
uniform float leaf_specular : hint_range(0.0, 1.0, 0.01) = 1.0;
group_uniforms leaf_scattering;
uniform sampler2D leaf_scattering_texture : source_color, repeat_disable, filter_linear_mipmap;
uniform sampler2D light_gradient : repeat_disable;
uniform float distance_from_center_factor;
uniform vec3 distance_from_center_factor_offset = vec3(0.0);
uniform vec3 distance_from_center_factor_scale = vec3(1.0);
uniform float directional_shadows_power : hint_range(0.0, 1.0, 0.01) = 0.15;
uniform float edge_scattering_power = 1.1;
uniform float edge_scattering_minimum_power = 0.15;
uniform float distance_from_center_power = 1.4;
uniform float back_scattering_minimum_power = 0.25;
uniform float back_scattering_power = 0.6;
uniform float back_scattering_curve = 7.0;
uniform vec3 leaf_center_position = vec3(0.0);
uniform float scattering_exposure = 1.0;
group_uniforms trunk_textures;
uniform sampler2D trunk_albedo;
uniform sampler2D trunk_normal;
group_uniforms wind;
uniform sampler2D wind_texture : repeat_enable, filter_linear;
uniform float wind_speed = 1.0;
uniform float wind_scale = 1.0;
uniform float wind_power = 1.0;
group_uniforms randomization;
uniform sampler2D random_gradient;
uniform float random_scale_power = 0.1;
varying float distance_factor;
varying vec3 vertex_color;
varying vec3 screen_vertex;
varying vec3 rand;
float random (vec2 uv){
return fract(sin(dot(uv,vec2(12.9898,78.233)))*43758.5453123);
}
void vertex() {
distance_factor = 1.0 - clamp(length(VERTEX.xyz * distance_from_center_factor_scale + distance_from_center_factor_offset) / distance_from_center_factor, 0.0, 1.0);
vertex_color = COLOR.rgb;
//small scale wind
VERTEX.y += vertex_color.g * texture(wind_texture,(VERTEX.xz + NODE_POSITION_WORLD.xz * wind_scale) * 0.005 + TIME * 0.07).b * (1.0 - distance_factor) * UV.y * 2.0 * wind_power;
//large scale wind
vec2 wind_pos = (NODE_POSITION_WORLD.xz) * 0.1 * wind_scale + TIME * 0.4 * wind_speed;
VERTEX.xz += (sin(wind_pos) * 1.5 + sin(wind_pos * 2.0357) + sin(wind_pos * 4.13565) * 0.5) * pow(VERTEX.y * 0.02, 2.0) * wind_power;
screen_vertex = mat3(MODEL_MATRIX) * mat3(VIEW_MATRIX) * (VERTEX - leaf_center_position);
float rand_float = random(NODE_POSITION_WORLD.xz);
VERTEX *= (rand_float - 0.5) * random_scale_power + 1.0;
rand = texture(random_gradient, vec2(rand_float)).rgb;
}
void fragment() {
if (vertex_color == vec3(1.0, 0.0, 0.0)) {
ALBEDO = texture(trunk_albedo, UV).rgb;
NORMAL_MAP = texture(trunk_normal, UV).rgb;
} else {
vec4 leaf_color = texture(leaf_texture, UV);
NORMAL_MAP = texture(leaf_normal, UV).rgb;
NORMAL_MAP_DEPTH = leaf_normal_map_depth;
ALBEDO = leaf_color.rgb * rand;
ALPHA = leaf_color.a;
ALPHA_SCISSOR_THRESHOLD = leaf_alpha_scissors_threshold;
ROUGHNESS = leaf_roughness;
}
}
float DistributionGGX(float cos_theta_m, float alpha)
{
float alpha2 = alpha * alpha;
float d = 1.0 + (alpha2 - 1.0) * cos_theta_m * cos_theta_m;
return alpha2 / (PI * d * d);
}
float GeometryGGX(float NdotL, float NdotV, float alpha)
{
return 0.5 / mix(2.0 * NdotL * NdotV, NdotL + NdotV, alpha);
}
vec3 SchlickBaseReflectivity(float metallic, float specular, vec3 albedo)
{
float dielectric = 0.04 * specular * specular;
return mix(vec3(dielectric), albedo, vec3(metallic));
}
float SchlickFresnel(float u)
{
float m = 1.0 - u;
float m2 = m * m;
return m2 * m2 * m;
}
void light() {
float light_dot = dot(NORMAL, LIGHT);
float camera_light_dot = dot(VIEW, LIGHT);
//BASE LIGHT DIRECTIONALITY
if (vertex_color != vec3(1.0, 0.0, 0.0)) {
DIFFUSE_LIGHT = vec3(clamp(-abs(light_dot * 0.4 - 0.2) + 0.3, edge_scattering_minimum_power, 1.0)) * LIGHT_COLOR * edge_scattering_power;
} else {
DIFFUSE_LIGHT = vec3(clamp(light_dot, 0.2, 1.0)) * LIGHT_COLOR * 0.5;
}
//DISTANCE FACTOR, OFFSET BY LIGHT DIRECTION
float factor = distance_factor - dot(LIGHT, normalize(screen_vertex)) * 0.3;
vec3 light_color = texture(light_gradient,vec2(factor)).rgb;
DIFFUSE_LIGHT *= clamp(ATTENUATION, directional_shadows_power, 1.0) * light_color * distance_from_center_power;
float subsurface_scattering_factor = clamp(-light_dot * 2.0 - 1.0, 0.0, 1.0);
vec3 subsurface_scattering_color = LIGHT_COLOR * texture(leaf_scattering_texture, UV).rgb * light_color;
DIFFUSE_LIGHT += vertex_color.g * (subsurface_scattering_factor * subsurface_scattering_color) * clamp(-camera_light_dot * (1.0 + back_scattering_curve) - back_scattering_curve, back_scattering_minimum_power, 1.0) * back_scattering_power;
DIFFUSE_LIGHT *= scattering_exposure;
// STANDARD SCHLICK SPECULAR
vec3 lightColor = LIGHT_COLOR / PI;
vec3 half = normalize(VIEW + LIGHT);
float NdotL = max(light_dot, 0.0);
float NdotV = max(dot(NORMAL, VIEW), 0.0);
float NdotH = max(dot(NORMAL, half), 0.0);
float LdotH = max(dot(LIGHT, half), 0.0);
float ggxAlpha = ROUGHNESS * ROUGHNESS;
float D = DistributionGGX(NdotH, ggxAlpha);
float G = GeometryGGX(NdotL, NdotV, ggxAlpha);
vec3 f0 = SchlickBaseReflectivity(METALLIC, SPECULAR_AMOUNT, ALBEDO);
float LdotH5 = SchlickFresnel(LdotH);
float f90 = clamp(50.0 * f0.g, 0.0, 1.0);
vec3 F = f0 + (f90 - f0) * LdotH5;
vec3 specularBRDF = max(NdotL * D * G * F, 0.0);
SPECULAR_LIGHT = specularBRDF * LIGHT_COLOR * ATTENUATION;
}


