mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
feat: Implement PBR shaders and enhance atmospheric effects in rendering
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
"conan": {}
|
||||
},
|
||||
"include": [
|
||||
"build/build/Release/generators/CMakePresets.json"
|
||||
"build/build/Release/generators/CMakePresets.json",
|
||||
"build/Release/generators/CMakePresets.json"
|
||||
]
|
||||
}
|
||||
@@ -60,5 +60,17 @@
|
||||
"device_extensions": [
|
||||
"VK_KHR_swapchain"
|
||||
],
|
||||
"atmospherics": {
|
||||
"ambient_strength": 0.01,
|
||||
"fog_density": 0.003,
|
||||
"fog_color": [0.05, 0.05, 0.08],
|
||||
"gamma": 2.2,
|
||||
"enable_tone_mapping": true,
|
||||
"enable_shadows": true,
|
||||
"enable_ssgi": true,
|
||||
"enable_volumetric_lighting": true,
|
||||
"pbr_roughness": 0.3,
|
||||
"pbr_metallic": 0.1
|
||||
},
|
||||
"config_file": "config/seed_runtime.json"
|
||||
}
|
||||
|
||||
@@ -538,7 +538,7 @@ local function create_static_cube(position, scale, color)
|
||||
vertices = vertices,
|
||||
indices = cube_indices,
|
||||
compute_model_matrix = compute_model_matrix,
|
||||
shader_key = "solid", -- Use solid color shader for room objects
|
||||
shader_key = "pbr", -- Use PBR shader for realistic materials and lighting
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
169
shaders/pbr.frag
Normal file
169
shaders/pbr.frag
Normal file
@@ -0,0 +1,169 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 1) in vec3 fragWorldPos;
|
||||
layout(location = 2) in vec3 fragNormal;
|
||||
layout(location = 3) in vec2 fragTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
// Material properties
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
} pc;
|
||||
|
||||
// Lighting uniforms
|
||||
layout(set = 0, binding = 0) uniform sampler2D shadowMap;
|
||||
|
||||
// Material parameters (can be extended to use textures)
|
||||
const vec3 MATERIAL_ALBEDO = vec3(0.8, 0.8, 0.8);
|
||||
const float MATERIAL_ROUGHNESS = 0.3;
|
||||
const float MATERIAL_METALLIC = 0.1;
|
||||
|
||||
// Atmospheric parameters
|
||||
const vec3 FOG_COLOR = vec3(0.05, 0.05, 0.08);
|
||||
const float FOG_DENSITY = 0.003;
|
||||
const float AMBIENT_STRENGTH = 0.01; // Much darker ambient
|
||||
|
||||
// Light properties
|
||||
const vec3 LIGHT_COLOR = vec3(1.0, 0.9, 0.6);
|
||||
const float LIGHT_INTENSITY = 1.2;
|
||||
const vec3 LIGHT_POSITIONS[8] = vec3[8](
|
||||
vec3(13.0, 4.5, 13.0),
|
||||
vec3(-13.0, 4.5, 13.0),
|
||||
vec3(13.0, 4.5, -13.0),
|
||||
vec3(-13.0, 4.5, -13.0),
|
||||
vec3(0.0, 4.5, 13.0),
|
||||
vec3(0.0, 4.5, -13.0),
|
||||
vec3(13.0, 4.5, 0.0),
|
||||
vec3(-13.0, 4.5, 0.0)
|
||||
);
|
||||
|
||||
// PBR functions
|
||||
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
|
||||
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
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 num = a2;
|
||||
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
|
||||
denom = 3.14159 * denom * denom;
|
||||
|
||||
return num / denom;
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float NdotV, float roughness) {
|
||||
float r = (roughness + 1.0);
|
||||
float k = (r * r) / 8.0;
|
||||
|
||||
float num = NdotV;
|
||||
float denom = NdotV * (1.0 - k) + k;
|
||||
|
||||
return num / denom;
|
||||
}
|
||||
|
||||
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);
|
||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
|
||||
float calculateShadow(vec3 worldPos) {
|
||||
vec4 lightSpacePos = pc.lightViewProj * vec4(worldPos, 1.0);
|
||||
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
if (projCoords.z > 1.0) return 0.0;
|
||||
|
||||
float closestDepth = texture(shadowMap, projCoords.xy).r;
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
float shadow = 0.0;
|
||||
vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
|
||||
for(int x = -1; x <= 1; ++x) {
|
||||
for(int y = -1; y <= 1; ++y) {
|
||||
float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
||||
shadow += currentDepth - 0.005 > pcfDepth ? 1.0 : 0.0;
|
||||
}
|
||||
}
|
||||
shadow /= 9.0;
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
vec3 calculateLighting(vec3 worldPos, vec3 normal, vec3 viewDir) {
|
||||
vec3 F0 = vec3(0.04);
|
||||
F0 = mix(F0, MATERIAL_ALBEDO, MATERIAL_METALLIC);
|
||||
|
||||
vec3 Lo = vec3(0.0);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
vec3 lightDir = normalize(LIGHT_POSITIONS[i] - worldPos);
|
||||
vec3 halfway = normalize(viewDir + lightDir);
|
||||
|
||||
float distance = length(LIGHT_POSITIONS[i] - worldPos);
|
||||
float attenuation = 1.0 / (distance * distance);
|
||||
vec3 radiance = LIGHT_COLOR * LIGHT_INTENSITY * attenuation;
|
||||
|
||||
float NDF = DistributionGGX(normal, halfway, MATERIAL_ROUGHNESS);
|
||||
float G = GeometrySmith(normal, viewDir, lightDir, MATERIAL_ROUGHNESS);
|
||||
vec3 F = fresnelSchlick(max(dot(halfway, viewDir), 0.0), F0);
|
||||
|
||||
vec3 kS = F;
|
||||
vec3 kD = vec3(1.0) - kS;
|
||||
kD *= 1.0 - MATERIAL_METALLIC;
|
||||
|
||||
float NdotL = max(dot(normal, lightDir), 0.0);
|
||||
|
||||
vec3 numerator = NDF * G * F;
|
||||
float denominator = 4.0 * max(dot(normal, viewDir), 0.0) * NdotL + 0.0001;
|
||||
vec3 specular = numerator / denominator;
|
||||
|
||||
Lo += (kD * MATERIAL_ALBEDO / 3.14159 + specular) * radiance * NdotL;
|
||||
}
|
||||
|
||||
return Lo;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 normal = normalize(fragNormal);
|
||||
vec3 viewDir = normalize(pc.cameraPos - fragWorldPos);
|
||||
|
||||
// Ambient lighting
|
||||
vec3 ambient = AMBIENT_STRENGTH * MATERIAL_ALBEDO;
|
||||
|
||||
// Direct lighting with PBR
|
||||
vec3 lighting = calculateLighting(fragWorldPos, normal, viewDir);
|
||||
|
||||
// Shadow calculation
|
||||
float shadow = calculateShadow(fragWorldPos);
|
||||
lighting *= (1.0 - shadow * 0.7); // Soften shadows
|
||||
|
||||
// Combine lighting
|
||||
vec3 finalColor = ambient + lighting;
|
||||
|
||||
// Fog
|
||||
float fogFactor = 1.0 - exp(-FOG_DENSITY * length(pc.cameraPos - fragWorldPos));
|
||||
finalColor = mix(finalColor, FOG_COLOR, fogFactor);
|
||||
|
||||
// Simple tone mapping
|
||||
finalColor = finalColor / (finalColor + vec3(1.0));
|
||||
|
||||
// Gamma correction
|
||||
finalColor = pow(finalColor, vec3(1.0 / 2.2));
|
||||
|
||||
outColor = vec4(finalColor, 1.0);
|
||||
}
|
||||
BIN
shaders/pbr.frag.spv
Normal file
BIN
shaders/pbr.frag.spv
Normal file
Binary file not shown.
29
shaders/pbr.vert
Normal file
29
shaders/pbr.vert
Normal file
@@ -0,0 +1,29 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inNormal;
|
||||
layout(location = 2) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec3 fragWorldPos;
|
||||
layout(location = 2) out vec3 fragNormal;
|
||||
layout(location = 3) out vec2 fragTexCoord;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 lightViewProj;
|
||||
vec3 cameraPos;
|
||||
float time;
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
vec4 worldPos = pc.model * vec4(inPosition, 1.0);
|
||||
gl_Position = pc.proj * pc.view * worldPos;
|
||||
|
||||
fragWorldPos = worldPos.xyz;
|
||||
fragNormal = mat3(pc.model) * inNormal;
|
||||
fragTexCoord = inTexCoord;
|
||||
fragColor = vec3(0.7, 0.7, 0.7); // Default color, can be from vertex color or texture
|
||||
}
|
||||
BIN
shaders/pbr.vert.spv
Normal file
BIN
shaders/pbr.vert.spv
Normal file
Binary file not shown.
6
shaders/shadow.frag
Normal file
6
shaders/shadow.frag
Normal file
@@ -0,0 +1,6 @@
|
||||
#version 450
|
||||
|
||||
void main() {
|
||||
// Empty fragment shader for shadow mapping
|
||||
// Depth is automatically written
|
||||
}
|
||||
BIN
shaders/shadow.frag.spv
Normal file
BIN
shaders/shadow.frag.spv
Normal file
Binary file not shown.
14
shaders/shadow.vert
Normal file
14
shaders/shadow.vert
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inNormal;
|
||||
layout(location = 2) in vec2 inTexCoord;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
mat4 lightViewProj;
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
gl_Position = pc.lightViewProj * pc.model * vec4(inPosition, 1.0);
|
||||
}
|
||||
BIN
shaders/shadow.vert.spv
Normal file
BIN
shaders/shadow.vert.spv
Normal file
Binary file not shown.
65
shaders/ssgi.frag
Normal file
65
shaders/ssgi.frag
Normal file
@@ -0,0 +1,65 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D sceneColor;
|
||||
layout(set = 0, binding = 1) uniform sampler2D normalBuffer;
|
||||
layout(set = 0, binding = 2) uniform sampler2D depthBuffer;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 invProj;
|
||||
mat4 invView;
|
||||
vec3 cameraPos;
|
||||
float intensity;
|
||||
} pc;
|
||||
|
||||
const int NUM_SAMPLES = 16;
|
||||
const float SAMPLE_RADIUS = 0.5;
|
||||
|
||||
// Reconstruct world position from depth
|
||||
vec3 worldPosFromDepth(float depth, vec2 texCoord) {
|
||||
vec4 clipSpace = vec4(texCoord * 2.0 - 1.0, depth, 1.0);
|
||||
vec4 viewSpace = pc.invProj * clipSpace;
|
||||
viewSpace /= viewSpace.w;
|
||||
vec4 worldSpace = pc.invView * viewSpace;
|
||||
return worldSpace.xyz;
|
||||
}
|
||||
|
||||
vec3 ssao(vec2 texCoord) {
|
||||
float depth = texture(depthBuffer, texCoord).r;
|
||||
if (depth >= 1.0) return vec3(1.0); // Skybox
|
||||
|
||||
vec3 worldPos = worldPosFromDepth(depth, texCoord);
|
||||
vec3 normal = normalize(texture(normalBuffer, texCoord).rgb * 2.0 - 1.0);
|
||||
|
||||
float occlusion = 0.0;
|
||||
|
||||
for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
// Generate sample position in hemisphere around normal
|
||||
float angle = (float(i) / float(NUM_SAMPLES)) * 6.283185;
|
||||
vec2 offset = vec2(cos(angle), sin(angle)) * SAMPLE_RADIUS;
|
||||
|
||||
vec2 sampleTexCoord = texCoord + offset / textureSize(depthBuffer, 0);
|
||||
float sampleDepth = texture(depthBuffer, sampleTexCoord).r;
|
||||
|
||||
if (sampleDepth < depth - 0.01) { // Occluded
|
||||
occlusion += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
occlusion = 1.0 - (occlusion / float(NUM_SAMPLES));
|
||||
return vec3(occlusion);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 sceneColor = texture(sceneColor, inTexCoord).rgb;
|
||||
vec3 ao = ssao(inTexCoord);
|
||||
|
||||
// Apply ambient occlusion
|
||||
vec3 finalColor = sceneColor * (0.3 + 0.7 * ao); // Mix AO with direct lighting
|
||||
|
||||
outColor = vec4(finalColor, 1.0);
|
||||
}
|
||||
BIN
shaders/ssgi.frag.spv
Normal file
BIN
shaders/ssgi.frag.spv
Normal file
Binary file not shown.
46
shaders/volumetric.frag
Normal file
46
shaders/volumetric.frag
Normal file
@@ -0,0 +1,46 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D sceneColor;
|
||||
layout(set = 0, binding = 1) uniform sampler2D depthBuffer;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec2 lightScreenPos;
|
||||
float time;
|
||||
float intensity;
|
||||
} pc;
|
||||
|
||||
const int NUM_SAMPLES = 100;
|
||||
const float DECAY_BASE = 0.96815;
|
||||
const float WEIGHT_BASE = 0.58767;
|
||||
const float EXPOSURE = 0.2;
|
||||
|
||||
void main() {
|
||||
vec2 texCoord = inTexCoord;
|
||||
vec2 deltaTexCoord = (texCoord - pc.lightScreenPos);
|
||||
deltaTexCoord *= 1.0 / float(NUM_SAMPLES) * 0.5; // Scale for effect
|
||||
|
||||
vec3 color = texture(sceneColor, texCoord).rgb;
|
||||
|
||||
// Only apply god rays if we're looking towards the light
|
||||
float centerDistance = length(pc.lightScreenPos - vec2(0.5, 0.5));
|
||||
if (centerDistance < 0.8) { // Light is visible on screen
|
||||
vec3 godRayColor = vec3(0.0);
|
||||
float weight = WEIGHT_BASE;
|
||||
|
||||
for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
texCoord -= deltaTexCoord;
|
||||
vec3 sampleColor = texture(sceneColor, texCoord).rgb;
|
||||
godRayColor += sampleColor * weight;
|
||||
weight *= DECAY_BASE;
|
||||
}
|
||||
|
||||
color += godRayColor * EXPOSURE * pc.intensity;
|
||||
}
|
||||
|
||||
outColor = vec4(color, 1.0);
|
||||
}
|
||||
BIN
shaders/volumetric.frag.spv
Normal file
BIN
shaders/volumetric.frag.spv
Normal file
Binary file not shown.
Reference in New Issue
Block a user