deferred shading을 먼저해서 사실 다중 광원은 별 거 없었다.
lighting pass fragment shader code
#version 450
layout(input_attachment_index = 0, binding = 0) uniform subpassInput positionAttachment;
layout(input_attachment_index = 1, binding = 1) uniform subpassInput normalAttachment;
layout(input_attachment_index = 2, binding = 2) uniform subpassInput albedoAttachment;
layout(input_attachment_index = 3, binding = 3) uniform subpassInput pbrAttachment;
struct Light {
vec3 position;
vec3 direction;
vec3 color;
float intensity;
float innerCutoff;
float outerCutoff;
uint type;
};
layout(binding = 4) uniform LightingInfo {
Light lights[16];
vec3 cameraPos;
uint numLights;
float ambientStrength;
};
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor;
// Fresnel-Schlick Approximation
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
// Normal Distribution Function (NDF)
float distributionGGX(vec3 N, vec3 H, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
float denominator = (NdotH2 * (a2 - 1.0) + 1.0);
return a2 / (3.14159265359 * denominator * denominator);
}
// Geometry Function
float geometrySchlickGGX(float NdotV, float roughness) {
float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;
return NdotV / (NdotV * (1.0 - k) + k);
}
float geometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
return geometrySchlickGGX(NdotV, roughness) * geometrySchlickGGX(NdotL, roughness);
}
void main() {
vec3 fragPosition = subpassLoad(positionAttachment).rgb;
vec3 fragNormal = normalize(subpassLoad(normalAttachment).rgb);
vec3 albedo = subpassLoad(albedoAttachment).rgb;
vec4 pbr = subpassLoad(pbrAttachment);
float roughness = pbr.r;
float metallic = pbr.g;
float ao = pbr.b;
vec3 N = fragNormal;
vec3 V = normalize(cameraPos - fragPosition);
vec3 finalColor = vec3(0.0);
vec3 ambient = ambientStrength * albedo * ao;
finalColor += ambient;
for (uint i = 0; i < numLights; ++i) {
vec3 L;
float attenuation = 1.0;
if (lights[i].type == 0) { // Point Light
L = normalize(lights[i].position - fragPosition);
float distance = length(lights[i].position - fragPosition);
float constant = 1.0;
float linear = 0.09;
float quadratic = 0.032;
attenuation = 1.0 / (constant + linear * distance + quadratic * (distance * distance));
}
else if (lights[i].type == 1) { // Spot Light
L = normalize(lights[i].position - fragPosition);
float distance = length(lights[i].position - fragPosition);
float constant = 1.0;
float linear = 0.09;
float quadratic = 0.032;
attenuation = 1.0 / (constant + linear * distance + quadratic * (distance * distance));
float theta = dot(L, normalize(-lights[i].direction));
float epsilon = max(lights[i].innerCutoff - lights[i].outerCutoff, 0.001);
attenuation *= clamp((theta - lights[i].outerCutoff) / epsilon, 0.0, 1.0);
}
else { // Directional Light
L = normalize(-lights[i].direction);
attenuation = 1.0;
}
vec3 H = normalize(V + L);
vec3 F0 = mix(vec3(0.04), albedo, metallic);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
float NDF = distributionGGX(N, H, roughness);
float G = geometrySmith(N, V, L, roughness);
vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001;
vec3 specular = numerator / denominator;
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
float NdotL = max(dot(N, L), 0.0);
vec3 diffuse = kD * albedo / 3.14159265359;
vec3 radiance = lights[i].color * lights[i].intensity * NdotL * attenuation;
finalColor += (diffuse + specular) * radiance;
}
outColor = vec4(clamp(finalColor, 0.0, 1.0), 1.0);
}
pbr은 워낙 잘 알려져있어
코드는 gpt에게 물어보면 잘 알려준다.
point light 하나만 적용되는 코드에서
for 문을 돌면서 타입을 봐서 색을 누적시키는 형식으로 변경
shader에 맞춰 구조체 변경해주고
uniform buffer와 descriptorset변경 해주다가
알게 된 사실인데
shader에 배열 넣는게 별 거 아니라
그냥 더 큰 구조체 넣어주는 형식이더라
그래서 사실상 바뀌는 게 거의 없었다.
결과물
다중광원의 light object의 색을 광원색에 맞추면 참 이쁠 거 같은데
지금은 전부 default material이라 귀찮아서 안해줬다.
요즘 느끼는 게 엄청 힘들게 해준거는 당연한 부분이고
쉽게 쉽게 추가할 수 있는 건 이쁘게 보이는 거 같다.
이제 진짜 그림자 해보자
'Computer Graphics > Vulkan' 카테고리의 다른 글
Vulkan - 14. 점광원에 대한 그림자 적용 (shadow cubemap) (0) | 2025.01.26 |
---|---|
Vulkan - 13. shadow map 생성, 다중 그림자 적용 (Spot light, Directional light) (0) | 2025.01.22 |
Vulkan - 11. gltf 형식의 모델 로드, normal map 수정 (0) | 2025.01.14 |
Vulkan - 10. pbr 적용 (0) | 2025.01.07 |
Vulkan - 9. pbr 적용 전략 (0) | 2025.01.03 |