Files
2026-09-16 14:07:40 +08:00

542 lines
16 KiB
GLSL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}
}
}