/ chmod777_includes / shaders / draw_unit_custom.fs.glsl
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  }