forward.frag 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /* forward.frag -- Fragment shader used for forward shading
  2. *
  3. * Copyright (c) 2025-2026 Le Juez Victor
  4. *
  5. * This software is provided 'as-is', without any express or implied warranty.
  6. * For conditions of distribution and use, see accompanying LICENSE file.
  7. */
  8. #version 330 core
  9. /* === Extensions === */
  10. #extension GL_ARB_texture_cube_map_array : enable
  11. /* === Includes === */
  12. #include "../include/math.glsl"
  13. #include "../include/pbr.glsl"
  14. /* === Varyings === */
  15. smooth in vec3 vPosition;
  16. smooth in vec2 vTexCoord;
  17. flat in vec3 vEmission;
  18. smooth in vec4 vColor;
  19. smooth in mat3 vTBN;
  20. in vec4 vPosLightSpace[NUM_FORWARD_LIGHTS];
  21. /* === Uniforms === */
  22. uniform sampler2D uAlbedoMap;
  23. uniform sampler2D uEmissionMap;
  24. uniform sampler2D uNormalMap;
  25. uniform sampler2D uOrmMap;
  26. uniform sampler2DArrayShadow uShadowDirTex;
  27. uniform sampler2DArrayShadow uShadowSpotTex;
  28. uniform samplerCubeArrayShadow uShadowOmniTex;
  29. uniform samplerCubeArray uIrradianceTex;
  30. uniform samplerCubeArray uPrefilterTex;
  31. uniform sampler2D uBrdfLutTex;
  32. uniform float uNormalScale;
  33. uniform float uOcclusion;
  34. uniform float uRoughness;
  35. uniform float uMetalness;
  36. uniform vec3 uViewPosition;
  37. #if defined(PROBE)
  38. uniform bool uProbeInterior;
  39. #endif // PROBE
  40. /* === Blocks === */
  41. #include "../include/blocks/light.glsl"
  42. #include "../include/blocks/env.glsl"
  43. /* === Constants === */
  44. #define SHADOW_SAMPLES 8
  45. const vec2 VOGEL_DISK[8] = vec2[8](
  46. vec2(0.250000, 0.000000),
  47. vec2(-0.319290, 0.292496),
  48. vec2(0.048872, -0.556877),
  49. vec2(0.402444, 0.524918),
  50. vec2(-0.738535, -0.130636),
  51. vec2(0.699605, -0.445031),
  52. vec2(-0.234004, 0.870484),
  53. vec2(-0.446271, -0.859268)
  54. );
  55. /* === Fragments === */
  56. layout(location = 0) out vec4 FragColor;
  57. /* === Shadow functions === */
  58. float ShadowDir(int i, float cNdotL, mat2 diskRot)
  59. {
  60. Light light = uLights[i];
  61. vec4 p = vPosLightSpace[i];
  62. vec3 projCoords = p.xyz / p.w * 0.5 + 0.5;
  63. float bias = light.shadowSlopeBias * (1.0 - cNdotL);
  64. bias = max(bias, light.shadowDepthBias * projCoords.z);
  65. float compareDepth = projCoords.z - bias;
  66. float shadow = 0.0;
  67. for (int j = 0; j < SHADOW_SAMPLES; ++j) {
  68. vec2 offset = diskRot * VOGEL_DISK[j] * light.shadowSoftness;
  69. shadow += texture(uShadowDirTex, vec4(projCoords.xy + offset, float(light.shadowLayer), compareDepth));
  70. }
  71. shadow /= float(SHADOW_SAMPLES);
  72. vec3 distToBorder = min(projCoords, 1.0 - projCoords);
  73. float edgeFade = smoothstep(0.0, 0.15, min(distToBorder.x, min(distToBorder.y, distToBorder.z)));
  74. shadow = mix(1.0, shadow, edgeFade);
  75. return shadow;
  76. }
  77. float ShadowSpot(int i, float cNdotL, mat2 diskRot)
  78. {
  79. Light light = uLights[i];
  80. vec4 p = vPosLightSpace[i];
  81. vec3 projCoords = p.xyz / p.w * 0.5 + 0.5;
  82. float bias = light.shadowSlopeBias * (1.0 - cNdotL);
  83. bias = max(bias, light.shadowDepthBias * projCoords.z);
  84. float compareDepth = projCoords.z - bias;
  85. float shadow = 0.0;
  86. for (int j = 0; j < SHADOW_SAMPLES; ++j) {
  87. vec2 offset = diskRot * VOGEL_DISK[j] * light.shadowSoftness;
  88. shadow += texture(uShadowSpotTex, vec4(projCoords.xy + offset, float(light.shadowLayer), compareDepth));
  89. }
  90. return shadow / float(SHADOW_SAMPLES);
  91. }
  92. float ShadowOmni(int i, float cNdotL, mat2 diskRot)
  93. {
  94. Light light = uLights[i];
  95. vec3 lightToFrag = vPosition - light.position;
  96. float currentDepth = length(lightToFrag);
  97. float bias = light.shadowSlopeBias * (1.0 - cNdotL * 0.5);
  98. bias = max(bias, light.shadowDepthBias * currentDepth);
  99. float compareDepth = (currentDepth - bias) / light.far;
  100. mat3 OBN = M_OrthonormalBasis(lightToFrag / currentDepth);
  101. float shadow = 0.0;
  102. for (int j = 0; j < SHADOW_SAMPLES; ++j) {
  103. vec2 diskOffset = diskRot * VOGEL_DISK[j] * light.shadowSoftness;
  104. shadow += texture(uShadowOmniTex, vec4(OBN * vec3(diskOffset.xy, 1.0), float(light.shadowLayer)), compareDepth);
  105. }
  106. return shadow / float(SHADOW_SAMPLES);
  107. }
  108. /* === User override === */
  109. #include "../include/user/scene.frag"
  110. /* === Main function === */
  111. void main()
  112. {
  113. /* Sample material maps */
  114. SceneFragment(vTexCoord, vTBN, 0.0);
  115. vec3 ORM = vec3(OCCLUSION, ROUGHNESS, METALNESS);
  116. mat3 TBN = mat3(TANGENT, BITANGENT, NORMAL);
  117. float dielectric = (1.0 - METALNESS);
  118. /* Compute F0 (reflectance at normal incidence) and diffuse coefficient */
  119. vec3 F0 = PBR_ComputeF0(METALNESS, 0.5, ALBEDO);
  120. vec3 kD = dielectric * ALBEDO;
  121. /* Sample normal and compute view direction vector */
  122. vec3 N = normalize(TBN * M_NormalScale(texture(uNormalMap, vTexCoord).rgb * 2.0 - 1.0, uNormalScale));
  123. if (!gl_FrontFacing) N = -N; // Flip for back facing triangles with double sided meshes
  124. vec3 V = normalize(uViewPosition - vPosition);
  125. /* Compute the dot product of the normal and view direction */
  126. float NdotV = dot(N, V);
  127. float cNdotV = max(NdotV, 1e-4); // Clamped to avoid division by zero
  128. /* Loop through all light sources accumulating diffuse and specular light */
  129. vec3 diffuse = vec3(0.0);
  130. vec3 specular = vec3(0.0);
  131. for (int i = 0; i < uNumLights; i++)
  132. {
  133. Light light = uLights[i];
  134. /* Compute light direction */
  135. vec3 L = vec3(0.0);
  136. if (light.type == LIGHT_DIR) L = -light.direction;
  137. else L = normalize(light.position - vPosition);
  138. /* Compute the dot product of the normal and light direction */
  139. float NdotL = dot(N, L);
  140. if (NdotL <= 0.0) continue;
  141. float cNdotL = min(NdotL, 1.0); // clamped NdotL
  142. /* Compute the halfway vector between the view and light directions */
  143. vec3 H = normalize(V + L);
  144. float LdotH = max(dot(L, H), 0.0);
  145. float cLdotH = min(LdotH, 1.0);
  146. float NdotH = max(dot(N, H), 0.0);
  147. float cNdotH = min(NdotH, 1.0);
  148. /* Compute light color energy */
  149. vec3 lightColE = light.color * light.energy;
  150. /* Compute diffuse lighting */
  151. vec3 diffLight = L_Diffuse(cLdotH, cNdotV, cNdotL, ROUGHNESS);
  152. diffLight *= lightColE * dielectric;
  153. /* Compute specular lighting */
  154. vec3 specLight = L_Specular(F0, cLdotH, cNdotH, cNdotV, cNdotL, ROUGHNESS);
  155. specLight *= lightColE * light.specular;
  156. /* Calculating a random rotation matrix for shadow debanding */
  157. float r = M_TAU * M_HashIGN(gl_FragCoord.xy);
  158. float sr = sin(r), cr = cos(r);
  159. mat2 diskRot = mat2(vec2(cr, -sr), vec2(sr, cr));
  160. /* Apply shadow factor if the light casts shadows */
  161. float shadow = 1.0;
  162. if (light.shadowLayer >= 0) {
  163. switch (light.type) {
  164. case LIGHT_DIR: shadow = ShadowDir(i, cNdotL, diskRot); break;
  165. case LIGHT_SPOT: shadow = ShadowSpot(i, cNdotL, diskRot); break;
  166. case LIGHT_OMNI: shadow = ShadowOmni(i, cNdotL, diskRot); break;
  167. }
  168. }
  169. /* Apply attenuation based on the distance from the light */
  170. if (light.type != LIGHT_DIR) {
  171. float dist = length(light.position - vPosition);
  172. float atten = 1.0 - clamp(dist / light.range, 0.0, 1.0);
  173. shadow *= atten * light.attenuation;
  174. }
  175. /* Apply spotlight effect if the light is a spotlight */
  176. if (light.type == LIGHT_SPOT) {
  177. float theta = dot(L, -light.direction);
  178. float epsilon = (light.innerCutOff - light.outerCutOff);
  179. shadow *= smoothstep(0.0, 1.0, (theta - light.outerCutOff) / epsilon);
  180. }
  181. /* Accumulate the diffuse and specular lighting contributions */
  182. diffuse += diffLight * shadow;
  183. specular += specLight * shadow;
  184. }
  185. /* Compute ambient */
  186. #if defined(PROBE)
  187. if (uProbeInterior) E_ComputeAmbientColor(diffuse, kD, OCCLUSION);
  188. else E_ComputeAmbientOnly(diffuse, specular, kD, ORM, F0, vPosition, N, V, cNdotV);
  189. #else
  190. E_ComputeAmbientAndProbes(diffuse, specular, kD, ORM, F0, vPosition, N, V, cNdotV);
  191. #endif
  192. /* Compute the final fragment color */
  193. FragColor = vec4(ALBEDO * diffuse + specular + EMISSION, ALPHA);
  194. }