#version 450 #extension GL_GOOGLE_cpp_style_line_directive : enable #extension GL_GOOGLE_include_directive : enable #include "include/light.glsl" const float M_PI = 3.141592653589793; const float c_MinRoughness = 0.04; #define ONLY_SH false layout (constant_id = 0) const int SHADOW_MAP_CASCADE_COUNT = 4; layout (constant_id = 1) const int enablePCF = 1; layout (set = 0, binding = 0) uniform UniformBufferObject { mat4 proj; mat4 view; vec3 camPos; float pad; } ubo; layout(set = 0, binding = 1) uniform UniformBufferMaterial { vec4 baseColorFactor; // 基础颜色系数 vec4 emissiveFactor; // 自发光系数 // -1.无贴图 0.使用UV0 1.使用UV1 int baseColorTextureSet; int physicalDescriptorTextureSet; int normalTextureSet; int occlusionTextureSet; int emissiveTextureSet; float metallicFactor; // 金属度 float roughnessFactor; // 粗糙度 float alphaMask; // 透明度裁剪 float alphaMaskCutoff; // 丢弃阈值 float emissiveStrength; // 自发光强度 vec2 pad; } material; layout(set = 0, binding = 2) uniform UniformBufferPass { float exposure; // 曝光度 float gamma; // gamma校正 float prefilteredCubeMipLevels; // 预过滤的立方体贴图级数 float scaleIBLAmbient; // ibl环境光系数 vec4 sh[9]; // 球谐漫反射 vec4 cascadeSplits; // 级联分割 float debugViewInputs; Light light[NUM_LIGHT]; } pass; layout (set = 0, binding = 3) uniform UniformBufferCascade { mat4 viewProj[SHADOW_MAP_CASCADE_COUNT]; } cascade; // 依赖pass #include "include/tonemap.glsl" // 预过滤镜面环境贴图(ibl) layout (set = 0, binding = 4) uniform samplerCube prefilteredMap; // BRDF 积分查找表 layout (set = 0, binding = 5) uniform sampler2D samplerBRDFLUT; // 阴影贴图 layout (set = 0, binding = 6) uniform sampler2DArray shadowMap; layout(set = 1, binding = 0) uniform sampler2D diffuseMap; // 漫反射 layout(set = 1, binding = 1) uniform sampler2D physicalMap; // 物理 layout(set = 1, binding = 2) uniform sampler2D normalMap; // 法线 layout(set = 1, binding = 3) uniform sampler2D aoMap; // 环境光遮蔽 layout(set = 1, binding = 4) uniform sampler2D emissiveMap; // 自发光 layout (location = 0) in vec3 inPosition; layout (location = 1) in vec3 inNormal; layout (location = 2) in vec2 inUV0; layout (location = 3) in vec2 inUV1; layout (location = 4) in vec4 inColor; layout (location = 5) in vec3 inViewPos; layout(location = 0) out vec4 outColor; // 计算中间值 struct PbrInfo { float NdotL; // 法线 · 光照方向 夹角余弦 float NdotV; // 法线 · 视角方向 夹角余弦 float NdotH; // 法线 · 半角向量 夹角余弦 float LdotH; // 光照方向 · 半角向量 夹角余弦 float VdotH; // 视角方向 · 半角向量 夹角余弦 float perceptualRoughness; // 感知粗糙度(美术编辑的原始值) float metalness; // 金属度 vec3 reflectance0; // 0度入射角 反射率(垂直入射) vec3 reflectance90; // 90度入射角 反射率(掠射入射) float alphaRoughness; // 物理粗糙度(perceptualRoughness²,用于光照计算) vec3 diffuseColor; // 漫反射颜色 vec3 specularColor; // 高光反射颜色 }; // 从顶点或贴图获取法线(世界坐标系) vec3 getNormal() { vec3 tangentNormal = texture(normalMap, material.normalTextureSet == 0 ? inUV0 : inUV1).xyz * 2.0 - 1.0; // dFdx的返回值表示当前片元在屏幕空间x方向上的局部偏导数 // 即当屏幕坐标x增加1个像素时,输入变量的变化率。 // 它通常在GPU的2x2像素块内计算,通过相邻像素的值差来获得。 vec3 q1 = dFdx(inPosition); vec3 q2 = dFdy(inPosition); vec2 st1 = dFdx(inUV0); vec2 st2 = dFdy(inUV0); // N 向上(模型表面法线) // T 向右(切线 纹理 U 方向) // B 向前(副切线 纹理 V 方向) vec3 N = normalize(inNormal); vec3 T = normalize(q1 * st2.t - q2 * st1.t); vec3 B = normalize(cross(N, T)); mat3 TBN = mat3(T, B, N); return normalize(TBN * tangentNormal); } vec4 SRGBtoLINEAR(vec4 srgbIn) { //#define MANUAL_SRGB 1 #ifdef MANUAL_SRGB #ifdef SRGB_FAST_APPROXIMATION vec3 linOut = pow(srgbIn.xyz,vec3(2.2)); #else //SRGB_FAST_APPROXIMATION vec3 bLess = step(vec3(0.04045),srgbIn.xyz); vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); #endif //SRGB_FAST_APPROXIMATION return vec4(linOut,srgbIn.w);; #else //MANUAL_SRGB return srgbIn; #endif //MANUAL_SRGB } // F 菲涅尔函数 vec3 specularReflection(PbrInfo pbrInputs) { // reflectance0:垂直入射反射率 // reflectance90:掠射入射反射率 // VdotH:视线与半角向量夹角 // 公式效果:角度越偏,反射越强 return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0); } // G 几何遮蔽函数 float geometricOcclusion(PbrInfo pbrInputs) { // 粗糙表面会自己遮挡自己 → 让高光更真实、不刺眼 // 越粗糙 → G 值越小 → 高光越暗 float NdotL = pbrInputs.NdotL; float NdotV = pbrInputs.NdotV; float r = pbrInputs.alphaRoughness; // 计算光方向的遮蔽 // 计算视线方向的遮蔽 // 相乘得到总遮蔽 float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL))); float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV))); return attenuationL * attenuationV; } // D 微表面分布函数 float microfacetDistribution(PbrInfo pbrInputs) { // 控制高光的形状与大小 // 光滑 → D 值高 → 小而亮的高光 // 粗糙 → D 值低 → 大而散的高光 float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness; float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0; return roughnessSq / (M_PI * f * f); } // 漫反射率,能量守恒所以除以PI vec3 diffuse(PbrInfo pbrInputs) { // 漫反射率 = 漫反射颜色 / π return pbrInputs.diffuseColor / M_PI; } #include "include/tocubemap.glsl" // 球谐函数 vec3 irradianceFromSH(vec3 i_normal) { vec3 normal = tocubemap(i_normal); vec3 result = pass.sh[0].xyz + pass.sh[1].xyz * normal.y + pass.sh[2].xyz * normal.z + pass.sh[3].xyz * normal.x + pass.sh[4].xyz * normal.y * normal.x + pass.sh[5].xyz * normal.y * normal.z + pass.sh[6].xyz * (1.0 - 3.0*normal.z*normal.z) + pass.sh[7].xyz * normal.z * normal.x + pass.sh[8].xyz * (normal.x*normal.x - normal.y*normal.y); return result * 0.282095; } // 计算IBL环境光照 vec3 getIBLContribution(PbrInfo pbrInputs, vec3 n, vec3 reflection) { vec3 cubeVec = tocubemap(reflection); float lod = (pbrInputs.perceptualRoughness * pass.prefilteredCubeMipLevels); // 从 BRDF LUT 获取缩放和偏移 vec3 brdf = (texture(samplerBRDFLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb; // 球谐函数 计算漫反射环境光 vec3 diffuseLight = irradianceFromSH(n); // 镜面反射:采样预过滤贴图 vec3 specularLight = tonemap(textureLod(prefilteredMap, cubeVec, lod)).rgb; // 漫反射 + 镜面反射 计算 vec3 diffuse = diffuseLight * pbrInputs.diffuseColor; vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y); // 环境光强度缩放 diffuse *= pass.scaleIBLAmbient; if(ONLY_SH) return diffuse; specular *= pass.scaleIBLAmbient; return diffuse + specular; } #define ambient 0.3 float textureProj(vec4 shadowCoord, vec2 offset, uint cascadeIndex) { float shadow = 1.0; float bias = 0.005; if ( shadowCoord.z > 0 && shadowCoord.z < 1.0 ) { float dist = texture(shadowMap, vec3(shadowCoord.st + offset, cascadeIndex)).r; // 从光看 dist // 同一个点,但是主相机看 shadowCoord.z // 如果光看到的更小,则说明被遮挡 if (shadowCoord.w > 0 && dist < shadowCoord.z - bias) { shadow = ambient; } } return shadow; } // 百分比渐进过滤 float filterPCF(vec4 sc, uint cascadeIndex) { ivec2 texDim = textureSize(shadowMap, 0).xy; float scale = 0.75; float dx = scale * 1.0 / float(texDim.x); float dy = scale * 1.0 / float(texDim.y); float shadowFactor = 0.0; int count = 0; int range = 1; for (int x = -range; x <= range; x++) { for (int y = -range; y <= range; y++) { shadowFactor += textureProj(sc, vec2(dx*x, dy*y), cascadeIndex); count++; } } return shadowFactor / count; } const mat4 biasMat = mat4( 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.5, 0.5, 0.0, 1.0 ); void main() { float perceptualRoughness; float metallic; vec3 diffuseColor; vec4 baseColor; // 基础反射值 vec3 f0 = vec3(0.04); // 透明度裁剪 if (material.alphaMask == 1.0f) { if (material.baseColorTextureSet > -1) { baseColor = SRGBtoLINEAR(texture(diffuseMap, material.baseColorTextureSet == 0 ? inUV0 : inUV1)) * material.baseColorFactor; } else { baseColor = material.baseColorFactor; } if (baseColor.a < material.alphaMaskCutoff) { discard; } } // 粗糙度(基础值) perceptualRoughness = material.roughnessFactor; // 金属度(基础值) metallic = material.metallicFactor; // 采样 粗糙度和金属度 if (material.physicalDescriptorTextureSet > -1) { // R 通道 = 环境光遮蔽 AO (Occlusion) // G 通道 = 粗糙度 (Roughness) // B 通道 = 金属度 (Metallic) vec4 mrSample = texture(physicalMap, material.physicalDescriptorTextureSet == 0 ? inUV0 : inUV1); perceptualRoughness = mrSample.g * perceptualRoughness; metallic = mrSample.b * metallic; } else { perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); metallic = clamp(metallic, 0.0, 1.0); } // 采样 基础颜色 if (material.baseColorTextureSet > -1) { baseColor = SRGBtoLINEAR(texture(diffuseMap, material.baseColorTextureSet == 0 ? inUV0 : inUV1)) * material.baseColorFactor; } else { baseColor = material.baseColorFactor; } baseColor *= inColor; diffuseColor = baseColor.rgb * (vec3(1.0) - f0); diffuseColor *= 1.0 - metallic; // 粗糙度的平方(人眼对粗糙的感受不是线性的) float alphaRoughness = perceptualRoughness * perceptualRoughness; //-------------------------------------------------------------------------- // 高光颜色 // 它决定了: // 物体反光有多亮 // 反光是什么颜色 // 金属 / 非金属的反光区别 vec3 specularColor = mix(f0, baseColor.rgb, metallic); // 计算反射率基准值 float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); // 计算90°掠射时的反射强度(菲涅尔上限) float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); // 定义菲涅尔的两个关键参数:垂直入射(R0) / 掠射入射(R90) vec3 specularEnvironmentR0 = specularColor.rgb; vec3 specularEnvironmentR90 = vec3(1.0) * reflectance90; //-------------------------------------------------------------------------- // 1. 获取最终表面法线(纹理法线 / 模型法线) vec3 n = (material.normalTextureSet > -1) ? getNormal() : normalize(inNormal); // 2. 计算PBR核心方向向量 vec3 v = normalize(ubo.camPos - inPosition); // 点 -> 相机 vec3 l = normalize(-pass.light[0].direction); // 点 -> 光源(平行光需要取反) vec3 h = normalize(l+v); // 半角向量:光线+视角的中间方向 // 相机 -> 点 转换为 点 -> 光 vec3 reflection = normalize(reflect(-v, n)); // 环境反射向量(用于环境贴图采样) // 3. 计算PBR必需的点积(全部钳位避免除零/负数) float NdotL = clamp(dot(n, l), 0.001, 1.0); // 法线·光线 float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0); // 法线·视角(取绝对值) float NdotH = clamp(dot(n, h), 0.0, 1.0); // 法线·半角向量 float LdotH = clamp(dot(l, h), 0.0, 1.0); // 光线·半角向量 float VdotH = clamp(dot(v, h), 0.0, 1.0); // 视角·半角向量 PbrInfo pbrInputs = PbrInfo( NdotL, NdotV, NdotH, LdotH, VdotH, perceptualRoughness, metallic, specularEnvironmentR0, specularEnvironmentR90, alphaRoughness, diffuseColor, specularColor ); // 计算参数 vec3 F = specularReflection(pbrInputs); float G = geometricOcclusion(pbrInputs); float D = microfacetDistribution(pbrInputs); const vec3 u_LightColor = pass.light[0].strength; // 计算光照结果 // 漫反射 vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs); // 高光部分 vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV); // 最终光照 = 漫反射 + 高光,受法线与灯光夹角影响 vec3 color = NdotL * u_LightColor * (diffuseContrib + specContrib); if(ONLY_SH) color = diffuseContrib; //----------------------------------阴影计算 // 返回所在级联 uint cascadeIndex = 0; for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; ++i) { if(inViewPos.z < pass.cascadeSplits[i]) { cascadeIndex = i + 1; } } // 所在贴图位置 vec4 shadowCoord = (biasMat * cascade.viewProj[cascadeIndex]) * vec4(inPosition, 1.0); // xy 纹理坐标 // z 点到光源的距离 shadowCoord.xyz /= shadowCoord.w; float shadow = 0; if (enablePCF == 1) { shadow = filterPCF(shadowCoord, cascadeIndex); } else { shadow = textureProj(shadowCoord, vec2(0.0), cascadeIndex); } // 只影响直射光 if(!ONLY_SH) color *= shadow; // 计算ibl光照 color += getIBLContribution(pbrInputs, n, reflection); const float u_OcclusionStrength = 1.0f; // 采样 环境光遮蔽 if (material.occlusionTextureSet > -1) { float ao = texture(aoMap, (material.occlusionTextureSet == 0 ? inUV0 : inUV1)).r; color = mix(color, color * ao, u_OcclusionStrength); } // 采样 自发光 vec3 emissive = material.emissiveFactor.rgb * material.emissiveStrength; if (material.emissiveTextureSet > -1) { emissive *= SRGBtoLINEAR(texture(emissiveMap, material.emissiveTextureSet == 0 ? inUV0 : inUV1)).rgb; }; if(!ONLY_SH) color += emissive; outColor = vec4(color, baseColor.a); // --------------------------------调试绘图------------------------------- if (pass.debugViewInputs > 0.0) { int index = int(pass.debugViewInputs); switch (index) { case 1: outColor.rgba = material.baseColorTextureSet > -1 ? texture(diffuseMap, material.baseColorTextureSet == 0 ? inUV0 : inUV1) : vec4(1.0f); break; case 2: outColor.rgb = (material.normalTextureSet > -1) ? texture(normalMap, material.normalTextureSet == 0 ? inUV0 : inUV1).rgb : inNormal; break; case 3: outColor.rgb = (material.occlusionTextureSet > -1) ? texture(aoMap, material.occlusionTextureSet == 0 ? inUV0 : inUV1).rrr : vec3(0.0f); break; case 4: outColor.rgb = (material.emissiveTextureSet > -1) ? texture(emissiveMap, material.emissiveTextureSet == 0 ? inUV0 : inUV1).rgb : vec3(0.0f); break; case 5: // 金属度 outColor.rgb = texture(physicalMap, inUV0).bbb; break; case 6: // 粗糙度 outColor.rgb = texture(physicalMap, inUV0).ggg; break; // PBR 光照方程 调试可视化 case 10: outColor.rgb = diffuseContrib; // 漫反射 break; case 11: outColor.rgb = F; // 菲涅尔 break; case 12: outColor.rgb = vec3(G); // 几何遮蔽 break; case 13: outColor.rgb = vec3(D); // 微表面分布 break; case 14: outColor.rgb = specContrib; // 高光 break; case 15:// 点 -> 光 outColor.rgb = reflection; // 反射向量 break; // 级联阴影 case 20: { switch(cascadeIndex) { case 0 : outColor.rgb *= vec3(1.0f, 0.25f, 0.25f); break; case 1 : outColor.rgb *= vec3(0.25f, 1.0f, 0.25f); break; case 2 : outColor.rgb *= vec3(0.25f, 0.25f, 1.0f); break; case 3 : outColor.rgb *= vec3(1.0f, 1.0f, 0.25f); break; } } break; case 21: outColor.rgb = shadowCoord.xxx; // 采样坐标 break; case 22: outColor.rgb = shadowCoord.zzz; //当前点 到光源的距离(深度) break; case 23: // 把屏幕UV直接当阴影贴图UV,全屏展示阴影贴图 vec2 uv = gl_FragCoord.xy / vec2(1280.0, 720.0); // 换成你屏幕分辨率 float depth = texture(shadowMap, vec3(uv, cascadeIndex)).r; // 直接把阴影贴图的深度值画出来 outColor = vec4(vec3(depth), 1.0); break; } } }