Files
HelloVulkan/shaders/simple_shader.frag
T
atdunbg 2b875c9284 feat: OBJ 全量贴图接入——Material 加 AO 槽 + OBJ 工作流适配
- Material 槽位 4→5(+AO),参数 UBO binding 4→5;shader ambient
  diffuse 项乘 ao(IBL 遮蔽),AO 默认纹理白色
- OBJ 槽落点补全:map_Bump→HEIGHT(5)、map_Ns→SHININESS(7)、
  map_Ka→AMBIENT(3)(assimp OBJ importer 约定)——backpack 的
  法线贴图此前也从未加载
- MTL 导出遗漏(roughness.jpg/ao.jpg 在目录未被引用):约定文件名
  探测补槽(roughness/ao/occlusion × jpg/png),命中留 INFO 锚点
- 因子按工作流分支:glTF 用声明因子(MR 合图 B=金属度);
  OBJ metallic=0(specular 工作流无金属概念),roughness 有图→1,
  无图→Blinn-Phong Ns 换算 R=sqrt(2/(Ns+2))(补偿默认贴图 G=0.5)
2026-09-23 19:49:36 +08:00

198 lines
7.7 KiB
GLSL
Raw 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
const int MAX_LIGHTS = 4;
const float PI = 3.14159265359;
struct Light {
vec4 color; // rgb = 颜色,a = 强度
vec4 direction; // 方向光:xyz = 方向(已归一)
vec4 position; // 点光:xyz = 世界位置
vec4 params; // x = 类型(0=方向,1=点),y = 衰减半径
};
layout(location = 0) in vec2 fragUV;
layout(location = 1) in vec3 fragNormal;
layout(location = 2) in vec3 fragWorldPos;
layout(location = 3) in float fragViewDepth;
layout(location = 4) in vec2 fragVelocity;
layout(location = 5) in vec3 fragTangent;
layout(location = 0) out vec4 outColor;
layout(location = 1) out vec2 outVelocity; // TAA velocity 附件
layout(set = 0, binding = 0) uniform UniformBufferObject {
mat4 view;
mat4 proj;
mat4 lightSpace[3]; // CSM 各级联光空间 VP
vec4 cascadeSplits; // xyz = 级联 0..2 远边界(视图深度)
vec4 ambientColor;
Light lights[MAX_LIGHTS];
mat4 projNoJitter;
mat4 prevView;
mat4 prevProj;
vec4 cameraPos;
} ubo;
// 材质纹理槽(binding = Material::Slot)
layout(set = 1, binding = 0) uniform sampler2D albedoMap;
layout(set = 1, binding = 1) uniform sampler2D normalMap;
layout(set = 1, binding = 2) uniform sampler2D metallicRoughnessMap; // G=粗糙度 B=金属度
layout(set = 1, binding = 3) uniform sampler2D emissiveMap;
layout(set = 1, binding = 4) uniform sampler2D aoMap; // 环境光遮蔽(R 通道)
layout(set = 1, binding = 5) uniform MaterialParams {
vec4 factors; // x = metallic 因子,y = roughness 因子
} matParams;
layout(set = 2, binding = 0) uniform sampler2D shadowMap0; // CSM 级联 0(最近)
layout(set = 2, binding = 1) uniform sampler2D shadowMap1; // 级联 1
layout(set = 2, binding = 2) uniform sampler2D shadowMap2; // 级联 2(最远)
layout(set = 2, binding = 3) uniform samplerCube irradianceMap; // 漫反射环境光(IBL 阶段②)
layout(set = 2, binding = 4) uniform samplerCube prefilteredMap; // 镜面环境(mip 分 roughness,阶段③)
layout(set = 2, binding = 5) uniform sampler2D brdfLut; // BRDF 积分表 (NdotV, roughness) → (scale, bias)
// 第 0 盏方向光(KeyLight)的 CSM 阴影因子:0.3 影中 / 1.0 受光。
// 级联按视图深度选择;采样器均 clamp-to-border 白色:
// 光视锥外自动 1.0(无阴影),省出界分支。
float ShadowFactor(vec3 N, float viewDepth) {
int cascade = 2;
for (int i = 0; i < 3; ++i) {
if (viewDepth < ubo.cascadeSplits[i]) { cascade = i; break; }
}
vec4 ls = ubo.lightSpace[cascade] * vec4(fragWorldPos, 1.0);
vec3 proj = ls.xyz / ls.w;
vec2 uv = proj.xy * 0.5 + 0.5;
float closest;
if (cascade == 0) closest = texture(shadowMap0, uv).r;
else if (cascade == 1) closest = texture(shadowMap1, uv).r;
else closest = texture(shadowMap2, uv).r;
vec3 L = normalize(-ubo.lights[0].direction.xyz);
float bias = max(0.004 * (1.0 - dot(N, L)), 0.0012);
return (proj.z - bias > closest) ? 0.3 : 1.0;
}
// 法线贴图 → 世界空间法线(TBN 构建)。
// 无切线数据(零向量)或平法线贴图时退回几何法线。
vec3 MappedNormal(vec3 geomN, vec3 T, vec2 uv) {
if (dot(T, T) < 1e-4) return geomN;
vec3 N = geomN;
vec3 nT = normalize(T - N * dot(N, T)); // 正交化
vec3 B = cross(N, nT);
vec3 sampled = texture(normalMap, uv).xyz * 2.0 - 1.0;
if (dot(sampled, sampled) < 1e-4) return geomN;
return normalize(nT * sampled.x + B * sampled.y + N * sampled.z);
}
// ---- Cook-Torrance BRDF 各项 ----
float DistributionGGX(float NdotH, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
return a2 / (PI * d * d);
}
float GeometrySmith(float NdotV, float NdotL, float roughness) {
float r = roughness + 1.0;
float k = (r * r) / 8.0; // 解析光源用此 k(IBL 才用 IBL 版本)
float gv = NdotV / (NdotV * (1.0 - k) + k);
float gl = NdotL / (NdotL * (1.0 - k) + k);
return gv * gl;
}
vec3 FresnelSchlick(float cosTheta, vec3 F0) {
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}
// 粗糙度感知的 Fresnel(IBL 用):粗糙表面掠射边缘的 F 上升更平缓
vec3 FresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) {
vec3 Fmax = max(vec3(1.0 - roughness), F0);
return F0 + (Fmax - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}
void main() {
vec3 albedo = texture(albedoMap, fragUV).rgb;
vec4 mr = texture(metallicRoughnessMap, fragUV);
float roughness = clamp(mr.g * matParams.factors.y, 0.045, 1.0);
float metallic = clamp(mr.b * matParams.factors.x, 0.0, 1.0);
vec3 emissive = texture(emissiveMap, fragUV).rgb;
vec3 N = MappedNormal(normalize(fragNormal), fragTangent, fragUV);
vec3 V = normalize(ubo.cameraPos.xyz - fragWorldPos);
float NdotV = max(dot(N, V), 1e-4);
vec3 F0 = mix(vec3(0.04), albedo, metallic); // 电介质基准反射率 4%
vec3 Lo = vec3(0.0);
for (int i = 0; i < MAX_LIGHTS; ++i) {
// 按类型分支:方向光无衰减;点光方向指向光源 + 平方距离衰减
vec3 L;
float atten = 1.0;
if (ubo.lights[i].params.x > 0.5) {
vec3 toLight = ubo.lights[i].position.xyz - fragWorldPos;
float d = length(toLight);
if (d >= ubo.lights[i].params.y) continue;
L = toLight / d;
float a = clamp(1.0 - (d * d) /
(ubo.lights[i].params.y * ubo.lights[i].params.y),
0.0, 1.0);
atten = a * a;
} else {
L = normalize(-ubo.lights[i].direction.xyz);
}
vec3 radiance = ubo.lights[i].color.rgb * ubo.lights[i].color.a * atten;
float NdotL = max(dot(N, L), 0.0);
if (NdotL <= 0.0) continue;
// Cook-Torrance 镜面项
vec3 H = normalize(V + L);
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = max(dot(H, V), 0.0);
float D = DistributionGGX(NdotH, roughness);
float G = GeometrySmith(NdotV, NdotL, roughness);
vec3 F = FresnelSchlick(NdotH2, F0);
vec3 kS = F;
vec3 kD = (1.0 - kS) * (1.0 - metallic); // 金属不参与漫反射
vec3 specular = (D * G * F) /
max(4.0 * NdotV * NdotL, 1e-4);
// 阴影只来自第 0 盏方向光(CSM)
float shade = (i == 0 && ubo.lights[0].params.x < 0.5)
? ShadowFactor(N, fragViewDepth) : 1.0;
Lo += (kD * albedo / PI + specular) * radiance * NdotL * shade;
}
// 环境光:IBL split-sum(阶段②③)。
// 漫反射:N 采样 irradiance cube(半球 Lambert 预卷积)
// 镜面:R 采样 pre-filtered cube(mip = roughness 分级)
// × BRDF LUT 查表得 F 缩放/偏置
// ambientColor = 用户可调强度/色调乘子
vec3 F_amb = FresnelSchlickRoughness(NdotV, F0, roughness);
vec3 kD_amb = (1.0 - F_amb) * (1.0 - metallic);
vec3 irradiance = texture(irradianceMap, N).rgb;
float ao = texture(aoMap, fragUV).r;
vec3 diffuse = kD_amb * irradiance * albedo * ao;
vec3 R = reflect(-V, N);
const float kMaxPrefilterMip = 4.0; // 与 Cubemap::kPrefilterMips - 1 同步
vec3 prefilteredColor = textureLod(prefilteredMap, R,
roughness * kMaxPrefilterMip).rgb;
vec2 envBRDF = texture(brdfLut, vec2(NdotV, roughness)).rg;
vec3 specular = prefilteredColor * (F_amb * envBRDF.x + envBRDF.y);
vec3 ambient = (diffuse + specular) * ubo.ambientColor.rgb;
vec3 color = ambient + Lo + emissive;
outColor = vec4(color, 1.0);
outVelocity = fragVelocity;
}