1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
// NOTE: The technique and some of this code is based on this tutorial:
// http://martindevans.me/game-development/2015/02/27/Drawing-Stuff-On-Other-Stuff-With-Deferred-Screenspace-Decals/
#include "lighting.sdr"
#include "normals.sdr"
#include "gamma.sdr"
out vec4 fragOut0; // Diffuse buffer
out vec4 fragOut1; // Normal buffer
out vec4 fragOut2; // Emissive buffer
flat in mat4 invModelMatrix;
flat in vec3 decalDirection;
flat in float normal_angle_cutoff;
flat in float angle_fade_start;
flat in float alpha_scale;
uniform sampler2D gDepthBuffer;
uniform sampler2D gNormalBuffer;
uniform sampler2DArray diffuseMap;
uniform sampler2DArray glowMap;
uniform sampler2DArray normalMap;
layout (std140) uniform decalGlobalData {
mat4 viewMatrix;
mat4 projMatrix;
mat4 invViewMatrix;
mat4 invProjMatrix;
vec2 viewportSize;
};
layout (std140) uniform decalInfoData {
int diffuse_index;
int glow_index;
int normal_index;
int diffuse_blend_mode;
int glow_blend_mode;
};
vec3 computeViewPosition(vec2 textureCoord) {
vec4 clipSpaceLocation;
vec2 normalizedCoord = textureCoord / viewportSize;
clipSpaceLocation.xy = normalizedCoord * 2.0f - 1.0f;
clipSpaceLocation.z = texelFetch(gDepthBuffer, ivec2(textureCoord), 0).r * 2.0f - 1.0f;
clipSpaceLocation.w = 1.0f;
vec4 homogenousLocation = invProjMatrix * clipSpaceLocation;
return homogenousLocation.xyz / homogenousLocation.w;
}
vec3 getPixelNormal(vec3 frag_position, vec2 tex_coord, inout float alpha, out vec3 binormal, out vec3 tangent) {
#ifdef USE_NORMAL_MAP
// If we can then we just use the existing normal buffer
vec3 normal = texelFetch(gNormalBuffer, ivec2(tex_coord), 0).xyz;
// If we use the normal map then we don't really need these values so we don't need to compute them here
binormal = vec3(0.0);
tangent = vec3(0.0);
#else
// Use some fancy screen-space derivates to determine the normal of the current pixel by looking at the surrounding pixels
vec3 pos_dx = dFdx(frag_position);
vec3 pos_dy = dFdy(frag_position);
vec3 normal = normalize(cross(pos_dx, pos_dy));
binormal = normalize(pos_dx);
tangent = normalize(pos_dy);
#endif
//Calculate angle between surface normal and decal direction
float angle = acos(dot(normal, decalDirection));
if (angle > normal_angle_cutoff) {
// The angle between surface normal and decal direction is too big
discard;
}
// Make a smooth alpha transition leading up to an edge
alpha = alpha * (1 - smoothstep(angle_fade_start, normal_angle_cutoff, angle));
return normal;
}
vec2 getDecalTexCoord(vec3 view_pos, inout float alpha) {
vec4 object_pos = invModelMatrix * invViewMatrix * vec4(view_pos, 1.0);
bvec3 invalidComponents = greaterThan(abs(object_pos.xyz), vec3(0.5));
bvec4 nanComponents = isnan(object_pos); // nan can happen some times if we have an infinite depth value
if (any(invalidComponents) || any(nanComponents)) {
// Fragment is out of the box
discard;
}
// Fade out the texture when it gets close to the top or bottom of the decal box
alpha = alpha * (1.0 - smoothstep(0.4, 0.5, abs(object_pos.z)));
return object_pos.xy + 0.5;
}
void main() {
vec3 frag_position = computeViewPosition(gl_FragCoord.xy);
float alpha = alpha_scale;
vec2 tex_coord = getDecalTexCoord(frag_position, alpha);
vec3 binormal;
vec3 tangent;
vec3 normal = getPixelNormal(frag_position, gl_FragCoord.xy, alpha, binormal, tangent);
vec4 diffuse_out = vec4(0.0);
vec4 emissive_out = vec4(0.0);
vec3 normal_out = vec3(0.0);
if (diffuse_index >= 0) {
// We have a valid diffuse map
vec4 color = texture(diffuseMap, vec3(tex_coord, float(diffuse_index)));
color.rgb = srgb_to_linear(color.rgb);
if (diffuse_blend_mode == 0) {
// Normal alpha blending
diffuse_out = vec4(color.rgb, color.a * alpha);
} else {
// Additive blending
diffuse_out = vec4(color.rgb * alpha, 1.0);
}
}
if (glow_index >= 0) {
// We have a valid glow map
vec4 color = texture(glowMap, vec3(tex_coord, float(glow_index)));
color.rgb = srgb_to_linear(color.rgb) * GLOW_MAP_SRGB_MULTIPLIER;
color.rgb *= GLOW_MAP_INTENSITY;
if (glow_blend_mode == 0) {
// Normal alpha blending
emissive_out = vec4(color.rgb + emissive_out.rgb * emissive_out.a, color.a * alpha);
} else {
// Additive blending
emissive_out.rgb += color.rgb * alpha;
}
}
if (normal_index >= 0) {
vec3 decalNormal = unpackNormal(texture(normalMap, vec3(tex_coord, float(normal_index))).ag);
mat3 tangentToView;
tangentToView[0] = tangent;
tangentToView[1] = binormal;
tangentToView[2] = normal;
normal_out = tangentToView * decalNormal * alpha;
}
fragOut0 = diffuse_out;
fragOut1 = vec4(normal_out, 0.0);
fragOut2 = emissive_out;
}
|