draw_unit_custom.fs.glsl
1 #version 420 2 #extension GL_ARB_uniform_buffer_object : require 3 #extension GL_ARB_shading_language_420pack: require 4 5 // File: draw_unit_custom.fs.glsl 6 // Author: chmod777 7 // Originally based on a shader by Beherith and Ivand (gfx_drawunitshape_gl4.lua) 8 9 /* 10 Copyright (C) 2024 chmod777 11 12 This program is free software: you can redistribute it and/or modify it under 13 the terms of the GNU Affero General Public License version 3 as published by the 14 Free Software Foundation. 15 16 This program is distributed in the hope that it will be useful, but WITHOUT ANY 17 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 18 PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 19 20 You should have received a copy of the GNU Affero General Public License along 21 with this program. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24 // Resources 25 // https://learnopengl.com/PBR/Lighting 26 // Recoil Files 27 // modelmaterials_gl4/templates/defaultMaterialTemplate.lua 28 29 layout(location = 0) out vec4 color; 30 31 // gl.UnitShapeTextures 32 layout (binding = 0) uniform sampler2D tex0; // albedo 33 layout (binding = 1) uniform sampler2D tex1; // emission, metalic, roughness 34 35 // customparams.normaltex 36 layout (binding = 3) uniform sampler2D normaltex; 37 38 39 const float PI = 3.14159265359; 40 41 #define NORM2SNORM(value) (value * 2.0 - 1.0) 42 #define SNORM2NORM(value) (value * 0.5 + 0.5) 43 44 const float GAMMA = 2.2; 45 const vec3 LUMA = vec3(0.2126, 0.7152, 0.0722); 46 47 const mat3 RGB2YCBCR = mat3( 48 0.2126, -0.114572, 0.5, 49 0.7152, -0.385428, -0.454153, 50 0.0722, 0.5, -0.0458471); 51 const mat3 YCBCR2RGB = mat3( 52 1.0, 1.0, 1.0, 53 0.0, -0.187324, 1.8556, 54 1.5748, -0.468124, -5.55112e-17); 55 56 const float BRIGHTNESS_FACTOR = 6.0; 57 58 // const float TONEMAP_A = 4.85; 59 // const float TONEMAP_B = 0.75; 60 // const float TONEMAP_C = 3.5; 61 // const float TONEMAP_D = 0.85; 62 // const float TONEMAP_E = 1.0; 63 const float ENV_AMBIENT = 0.15; 64 const float SUN_MULT = 1.0; 65 const float EXPOSURE_MULT = 1.0; 66 67 #define smoothclamp(v, v0, v1) ( mix(v0, v1, smoothstep(v0, v1, v)) ) 68 69 vec3 getFlatNormal(); 70 71 // https://learnopengl.com/PBR/Lighting ---------------------------------------- 72 vec3 getNormalFromMap(); 73 float DistributionGGX(vec3 N, vec3 H, float roughness); 74 float GeometrySchlickGGX(float NdotV, float roughness); 75 float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness); 76 vec3 fresnelSchlick(float cosTheta, vec3 F0); 77 // https://learnopengl.com/PBR/Lighting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 78 79 80 vec3 LINEARtoSRGB(vec3 c) { 81 if (GAMMA == 1.0) 82 return c; 83 84 float invGamma = 1.0 / GAMMA; 85 return pow(c, vec3(invGamma)); 86 } 87 vec3 SRGBtoLINEAR(vec3 c) { 88 if (GAMMA == 1.0) 89 return c; 90 91 return pow(c, vec3(GAMMA)); 92 } 93 94 //__ENGINEUNIFORMBUFFERDEFS__ 95 96 in DataVS { 97 vec2 uv; 98 vec3 currentTeamColor; 99 100 vec3 camEye; 101 vec3 camTarget; 102 103 // vec4 modelPosition; 104 // vec3 modelNormal; 105 // vec3 modelTangent; 106 // vec3 modelBitangent; 107 108 vec4 worldPosition; 109 vec3 worldNormal; 110 // vec3 worldTangent; 111 // vec3 worldBitangent; 112 113 // vec4 viewPosition; 114 // vec3 viewNormal; 115 // vec3 viewTangent; 116 // vec3 viewBitangent; 117 118 // mat3 modelTBN; 119 mat3 worldTBN; 120 // mat3 viewTBN; 121 } IN; 122 123 void main() { 124 vec4 color0 = texture(tex0, IN.uv.xy); 125 vec4 color1 = texture(tex1, IN.uv.xy); 126 127 float teamcolor_mask = color0.a; 128 129 float emission = color1.r; 130 float metallic = color1.g; 131 float roughness = color1.b; 132 133 vec3 albedo = color0.rgb; 134 albedo = mix(albedo, IN.currentTeamColor.rgb, teamcolor_mask); 135 albedo = SRGBtoLINEAR(albedo); 136 137 // vec3 N = IN.worldNormal; 138 // vec3 N = getFlatNormal(); 139 // vec3 N = normalize(IN.worldTBN * NORM2SNORM(texture(normaltex, IN.uv.xy).rgb)); 140 vec3 N = getNormalFromMap(); 141 142 vec3 V = normalize(IN.camEye - IN.worldPosition.xyz); 143 144 vec3 F0 = vec3(0.04); 145 146 // Linear Material 147 // F0 = vec3(0.95, 0.93, 0.88); // silver 148 F0 = vec3(0.91, 0.92, 0.92); // aluminium 149 // F0 = vec3(0.56, 0.57, 0.58); // iron 150 151 F0 = mix(F0, albedo, metallic); 152 153 // calculate per-light radiance 154 vec3 L = normalize(sunDir.xyz); 155 vec3 H = normalize(V + L); 156 float NdotL = max(dot(N, L), 0.0); 157 158 float attenuation = 1.0; 159 160 float lightStrength = 1.0; 161 vec3 lightColor = sunDiffuseModel.rgb * lightStrength; 162 163 vec3 radiance = lightColor * attenuation; 164 165 // cook-torrance brdf 166 float NDF = DistributionGGX(N, H, roughness); 167 float G = GeometrySmith(N, V, L, roughness); 168 vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); 169 170 vec3 maxSun = mix( 171 sunSpecularModel.rgb, 172 sunDiffuseModel.rgb, 173 step( 174 dot(sunSpecularModel.rgb, LUMA), 175 dot(sunDiffuseModel.rgb, LUMA) 176 ) 177 ); 178 179 vec3 numerator = NDF * G * F; 180 float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; 181 vec3 specular = numerator / denominator; 182 specular *= maxSun; 183 specular *= NdotL; // * shadowMult; 184 185 vec3 kS = F; 186 vec3 kD = vec3(1.0) - kS; 187 kD *= 1.0 - metallic; 188 189 vec3 dirContrib = maxSun * (kD * albedo /* PI */) * NdotL; // * shadowMult; 190 191 vec3 ambient = mix(vec3(0.0), sunAmbientModel.rgb, ENV_AMBIENT); 192 193 color = vec4(0.0, 0.0, 0.0, 1.0); 194 color.rgb = dirContrib * BRIGHTNESS_FACTOR; 195 color.rgb += specular; 196 color.rgb += albedo*ambient; 197 color.rgb += albedo*emission; 198 color.rgb *= EXPOSURE_MULT; 199 color.rgb = LINEARtoSRGB(color.rgb); 200 201 // TODO animate emission 202 // float time = timeInfo.g; // vec4 timeInfo: gameFrame, gameSeconds, drawFrame, frameTimeOffset 203 // emission = clamp(emission*sin(time), 0.0, 1.0); 204 205 // color.rgb = N; 206 } 207 208 209 vec3 getFlatNormal() 210 { 211 vec3 Q1 = dFdx(IN.worldPosition.xyz); 212 vec3 Q2 = dFdy(IN.worldPosition.xyz); 213 return normalize(cross(Q1, Q2)); 214 } 215 216 // https://learnopengl.com/PBR/Lighting ---------------------------------------- 217 vec3 getNormalFromMap() 218 { 219 vec3 tangentNormal = texture(normaltex, IN.uv).xyz * 2.0 - 1.0; 220 221 vec3 Q1 = dFdx(IN.worldPosition.xyz); 222 vec3 Q2 = dFdy(IN.worldPosition.xyz); 223 vec2 st1 = dFdx(IN.uv); 224 vec2 st2 = dFdy(IN.uv); 225 226 vec3 N = normalize(cross(Q1, Q2)); // flat normal 227 // N = IN.worldNormal; 228 // N = normalize(IN.worldNormal); 229 vec3 T = clamp(normalize(Q1*st2.t - Q2*st1.t), 0.000001, 1.0); 230 vec3 B = normalize(cross(N, T)); 231 mat3 TBN = mat3(T, B, N); 232 233 return normalize(TBN * tangentNormal); 234 } 235 float DistributionGGX(vec3 N, vec3 H, float roughness) 236 { 237 float a = roughness*roughness; 238 float a2 = a*a; 239 float NdotH = max(dot(N, H), 0.0); 240 float NdotH2 = NdotH*NdotH; 241 242 float num = a2; 243 float denom = (NdotH2 * (a2 - 1.0) + 1.0); 244 denom = PI * denom * denom; 245 246 return num / denom; 247 } 248 float GeometrySchlickGGX(float NdotV, float roughness) 249 { 250 float r = (roughness + 1.0); 251 float k = (r*r) / 8.0; 252 253 float num = NdotV; 254 float denom = NdotV * (1.0 - k) + k; 255 256 return num / denom; 257 } 258 float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) 259 { 260 float NdotV = max(dot(N, V), 0.0); 261 float NdotL = max(dot(N, L), 0.0); 262 float ggx2 = GeometrySchlickGGX(NdotV, roughness); 263 float ggx1 = GeometrySchlickGGX(NdotL, roughness); 264 265 return ggx1 * ggx2; 266 } 267 vec3 fresnelSchlick(float cosTheta, vec3 F0) 268 { 269 return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); 270 }