179 lines
6.5 KiB
Plaintext
179 lines
6.5 KiB
Plaintext
|
shader_type spatial;
|
||
|
render_mode blend_mix, depth_draw_never, cull_back, depth_test_disable;
|
||
|
|
||
|
uniform sampler2D border_mask : hint_white;
|
||
|
|
||
|
uniform sampler2D albedo : hint_albedo;
|
||
|
uniform vec4 albedo_tint : hint_color = vec4(1.0);
|
||
|
|
||
|
uniform sampler2D emission : hint_black;
|
||
|
uniform vec4 emission_tint : hint_color = vec4(vec3(0.0), 1.0);
|
||
|
uniform float emission_strength = 1.0;
|
||
|
|
||
|
uniform sampler2D occlusion : hint_white;
|
||
|
uniform float occlusion_strength = 1.0;
|
||
|
|
||
|
uniform sampler2D specular : hint_white;
|
||
|
uniform float specular_strength = 1.0;
|
||
|
|
||
|
uniform sampler2D metallic : hint_black;
|
||
|
uniform float metallic_strength = 1.0;
|
||
|
|
||
|
uniform sampler2D normal_map : hint_normal;
|
||
|
|
||
|
uniform int current_frame = 0;
|
||
|
uniform int flipbook_columns_count = 1;
|
||
|
|
||
|
uniform float current_frame_blend = 0.0;
|
||
|
|
||
|
uniform bool use_normal_map = false;
|
||
|
|
||
|
varying vec3 decal_half_scale;
|
||
|
varying vec3 decal_right;
|
||
|
varying vec3 decal_up;
|
||
|
varying vec3 decal_forward;
|
||
|
|
||
|
//Checks if the given point is in the decal's boundaries using an oriented bounding box defined by the decal's tranform
|
||
|
bool is_point_in_decal_bounds(vec3 point, vec3 decal_position)
|
||
|
{
|
||
|
vec3 scale = decal_half_scale * 2.0;
|
||
|
vec3 p = point - decal_position;
|
||
|
return abs(dot(p, decal_right)) <= scale.x && abs(dot(p, decal_forward)) <= scale.y && abs(dot(p, decal_up)) <= scale.z;
|
||
|
}
|
||
|
|
||
|
void vertex()
|
||
|
{
|
||
|
vec3 world_position = (WORLD_MATRIX*vec4(0.0, 0.0, 0.0, 1.0)).xyz;
|
||
|
UV = world_position.xy;
|
||
|
UV2 = vec2(world_position.z,0.0);
|
||
|
|
||
|
decal_right = (WORLD_MATRIX*vec4(1.0, 0.0, 0.0, 1.0)).xyz - world_position;
|
||
|
decal_forward = (WORLD_MATRIX*vec4(0.0, 0.0, -1.0, 1.0)).xyz - world_position;
|
||
|
decal_up = (WORLD_MATRIX*vec4(0.0, 1.0, 0.0, 1.0)).xyz - world_position;
|
||
|
decal_half_scale = vec3(length(decal_right), length(decal_forward), length(decal_up)) / 2.0;
|
||
|
decal_right = normalize(decal_right);
|
||
|
decal_forward = normalize(decal_forward);
|
||
|
decal_up = normalize(decal_up);
|
||
|
|
||
|
//Override the projector mesh's normals in order to render the decal with mostly correct lighting
|
||
|
NORMAL = vec3(0.0, 0.0, 1.0);
|
||
|
TANGENT = vec3(1.0, 0.0, 0.0);
|
||
|
BINORMAL = vec3(0.0, 1.0, 0.0);
|
||
|
}
|
||
|
|
||
|
void fragment ()
|
||
|
{
|
||
|
//Compute world position using the depth buffer
|
||
|
float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
|
||
|
vec3 ndc = vec3(SCREEN_UV, depth) * 2.0 - 1.0;
|
||
|
vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
|
||
|
view.xyz /= view.w;
|
||
|
vec4 world = CAMERA_MATRIX * INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
|
||
|
vec3 world_position = world.xyz / world.w;
|
||
|
|
||
|
vec3 decal_position = vec3(UV.xy, UV2.x);
|
||
|
|
||
|
if(is_point_in_decal_bounds(world_position, decal_position))
|
||
|
{
|
||
|
//If the world position is within the decal's boundaries, we can compute it's uv coordinates
|
||
|
vec4 local_position = (vec4(world_position - decal_position, 0.0)) * WORLD_MATRIX;
|
||
|
|
||
|
vec2 flipbook_frame_index = vec2(float(current_frame % flipbook_columns_count), float(current_frame / flipbook_columns_count));
|
||
|
vec2 frame_size = vec2(1.0/float(flipbook_columns_count));
|
||
|
vec2 uv_coords = (vec2(local_position.x, -local_position.y) / (4.0*(decal_half_scale.xy * 2.0 * decal_half_scale.xy))) - vec2(0.5);
|
||
|
|
||
|
//This is used to fix some blending issues on the decal's edges, border mask is a white texture with a 1px transparent border on all sides
|
||
|
float border_alpha = texture(border_mask, uv_coords).a;
|
||
|
|
||
|
//Offset UVs to handle flipbook animation
|
||
|
uv_coords = uv_coords / float(flipbook_columns_count);
|
||
|
uv_coords -= float(flipbook_columns_count - 1) * frame_size - flipbook_frame_index * frame_size;
|
||
|
|
||
|
//Hacky stuff, to get UVs, correct lighting and normal mapping working, i need to get the fragment's local position in the light shader
|
||
|
//Unfortunately, we can't use varying to pass values between the fragment and light shaders
|
||
|
//To work around this limitation, i put the data i need in the TRANSMISSION built-in.
|
||
|
//Also, due to some limitation caused by how this shader works, PBR lighting isn't supported.
|
||
|
TRANSMISSION = vec3(1.0) - local_position.xyz / 100.0;
|
||
|
|
||
|
ALBEDO = texture(albedo, uv_coords).rgb * albedo_tint.rgb;
|
||
|
EMISSION = texture(emission, uv_coords).rgb * emission_tint.rgb * emission_strength;
|
||
|
ALPHA = texture(albedo, uv_coords).a * albedo_tint.a * border_alpha;
|
||
|
}else{
|
||
|
ALPHA = 0.0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//taken from https://stackoverflow.com/questions/22442304/glsl-es-dfdx-dfdy-analog
|
||
|
float dfd(vec2 p)
|
||
|
{
|
||
|
return p.x * p.x - p.y;
|
||
|
}
|
||
|
|
||
|
//taken from http://www.thetenthplanet.de/archives/1180 -- Modified for ES2!
|
||
|
mat3 cotangent_frame(vec3 N, vec3 p, vec2 uv)
|
||
|
{
|
||
|
vec2 ps = vec2(1.0 / uv.x, 1.0 / uv.y);
|
||
|
float c = dfd(uv);
|
||
|
vec3 dp1 = vec3(dfd(uv + p.x) - c);
|
||
|
vec3 dp2 = vec3(dfd(uv + p.y) - c);
|
||
|
vec2 duv1 = vec2(dfd(uv));
|
||
|
vec2 duv2 = vec2(dfd(uv));
|
||
|
|
||
|
vec3 dp2perp = cross( dp2, N );
|
||
|
vec3 dp1perp = cross( N, dp1 );
|
||
|
|
||
|
vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
|
||
|
vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
|
||
|
|
||
|
float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );
|
||
|
return mat3( T * invmax, B * invmax, N );
|
||
|
}
|
||
|
|
||
|
vec3 perturb_normal(vec3 N, vec3 V, vec2 texcoord )
|
||
|
{
|
||
|
vec3 map = texture(normal_map, texcoord ).rgb;
|
||
|
map = map * 255./127. - 128./127.;
|
||
|
map.x *= 1.0; //Normal direction is flipped in GLES2
|
||
|
map.y *= 1.0;
|
||
|
map.z *= -1.0;
|
||
|
//mat3 TBN = cotangent_frame(N, V, texcoord); //Makes normals look worse in GLES2
|
||
|
return normalize(map);
|
||
|
}
|
||
|
|
||
|
void light ()
|
||
|
{
|
||
|
//Get back the data from the fragment shader
|
||
|
vec3 data = (vec3(1.0) - TRANSMISSION) * 100.0;
|
||
|
|
||
|
//Recompute UV coordinates
|
||
|
vec2 uv_coords = vec2(data.x, -data.y);
|
||
|
|
||
|
vec2 flipbook_frame_index = vec2(float(current_frame % flipbook_columns_count), float(current_frame / flipbook_columns_count));
|
||
|
vec2 frame_size = vec2(1.0/float(flipbook_columns_count));
|
||
|
uv_coords = (uv_coords.xy / (4.0*(decal_half_scale.xy * 2.0 * decal_half_scale.xy))) - vec2(0.5);
|
||
|
uv_coords = uv_coords / float(flipbook_columns_count);
|
||
|
uv_coords -= float(flipbook_columns_count - 1) * frame_size - flipbook_frame_index * frame_size;
|
||
|
|
||
|
//Normal mapping
|
||
|
vec3 normal = NORMAL;
|
||
|
if(use_normal_map == true)
|
||
|
{
|
||
|
normal = perturb_normal(NORMAL, VIEW, uv_coords);
|
||
|
}
|
||
|
|
||
|
float n_dot_l = clamp(dot(normal, LIGHT), 0.0, 1.0);
|
||
|
|
||
|
//Specular lighting
|
||
|
vec3 view_dir = normalize(CAMERA_MATRIX[3].xyz - data);
|
||
|
vec3 reflection_dir = reflect(-LIGHT, normal);
|
||
|
float spec = pow(max(dot(view_dir, reflection_dir), 0.0), 32);
|
||
|
vec3 specular_light = specular_strength * spec * LIGHT_COLOR;
|
||
|
|
||
|
//Diffuse lighting
|
||
|
vec3 albedo_color = ALBEDO * n_dot_l;
|
||
|
albedo_color = albedo_color * mix(1.0, texture(occlusion, uv_coords).r, occlusion_strength);
|
||
|
|
||
|
DIFFUSE_LIGHT += albedo_color * ATTENUATION * LIGHT_COLOR;
|
||
|
SPECULAR_LIGHT = specular_light * texture(specular, uv_coords).r;
|
||
|
}
|