struct VSInput { [[vk::location(0)]] float3 inPosition; [[vk::location(1)]] float4 inColor; [[vk::location(2)]] float3 inNormal; [[vk::location(3)]] float2 inUV; [[vk::location(4)]] float3 inTangent; [[vk::location(5)]] float3 inBitangent; }; struct VSOutput { float4 pos : SV_Position; float4 color : COLOR; float2 uv : TEXCOORD0; float3 normal : NORMAL; float3 fragPos : TEXCOORD1; // position monde float3 viewPos : TEXCOORD2; // position caméra }; // set 0 - Camera struct CameraUBO { float4x4 view; float4x4 projection; }; [[vk::binding(0, 0)]] ConstantBuffer camera; // set 1 - Transform struct TransformUBO { float4x4 model; }; [[vk::binding(0, 1)]] ConstantBuffer transform; // set 2 - Material [[vk::binding(1, 2)]] Sampler2D albedoTexture; // texture + sampler combinés, pas de warning [[vk::binding(2, 2)]] Sampler2D normalTexture; // deuxième texture si besoin struct LightData { float4 position; // w = type float4 direction; // w = range float4 color; // w = intensity float innerCosAngle; float outerCosAngle; float attenuation; float padding; }; struct LightsBuffer { LightData lights[32]; int lightCount; }; [[vk::binding(0, 3)]] StructuredBuffer lightsBuffer; [shader("vertex")] VSOutput vertMain(VSInput input) { VSOutput output; float4x4 modelMatrix = transform.model; float4 worldPos = mul(modelMatrix, float4(input.inPosition, 1.0)); output.pos = mul(camera.projection, mul(camera.view, worldPos)); output.color = input.inColor; output.uv = input.inUV; // Pour une mise à l'échelle uniforme, on peut utiliser directement la matrice 3x3 float3x3 normalMatrix = (float3x3)modelMatrix; // Si mises à l'échelle non-uniforme : // float3x3 normalMatrix = transpose(inverse((float3x3)modelMatrix)); output.normal = normalize(mul(normalMatrix, input.inNormal)); output.fragPos = worldPos.xyz; // Position de la caméra dans le monde float3x3 R = (float3x3)camera.view; output.viewPos = -mul(transpose(R), camera.view[3].xyz); return output; } float3 computeDirectional(LightData light, VSOutput vertIn) { // Normaliser la normale (l'interpolation peut la dénormaliser) float3 normal = normalize(vertIn.normal); // Direction de la lumière (utiliser les données de la lumière) float3 lightDir = normalize(-light.direction.xyz); // Direction de la vue float3 viewDir = normalize(vertIn.viewPos - vertIn.fragPos); // Diffuse (Lambertian) float diff = max(dot(normal, lightDir), 0.0); float3 diffuse = diff * light.color.xyz * light.color.w; // Specular (Blinn-Phong) float3 halfVec = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfVec), 0.0), 32.0); float3 specular = spec * light.color.xyz * light.color.w * 0.5; // Retourner la somme return (diffuse + specular) * light.attenuation; } float3 computePoint(LightData light, VSOutput vertIn) { float3 normal = normalize(vertIn.normal); float3 viewDir = normalize(vertIn.viewPos - vertIn.fragPos); // Direction de la lumière (du point lumineux vers le fragment) float3 lightDir = normalize(light.position.xyz - vertIn.fragPos); // Distance pour l'atténuation float distance = length(light.position.xyz - vertIn.fragPos); float attenuation = 1.0 / (1.0 + light.attenuation * distance * distance); // Diffuse float diff = max(dot(normal, lightDir), 0.0); float3 diffuse = diff * light.color.xyz * light.color.w; // Specular float3 halfVec = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfVec), 0.0), 32.0); float3 specular = spec * light.color.xyz * light.color.w * 0.5; return (diffuse + specular) * attenuation; } float3 computeSpot(LightData light, VSOutput vertIn) { float3 normal = normalize(vertIn.normal); float3 viewDir = normalize(vertIn.viewPos - vertIn.fragPos); // Direction du fragment vers la lumière float3 lightDir = normalize(light.position.xyz - vertIn.fragPos); // Distance et atténuation float distance = length(light.position.xyz - vertIn.fragPos); float attenuation = 1.0 / (1.0 + light.attenuation * distance * distance); // Spot light (cosinus de l'angle entre la direction du spot et la direction vers le fragment) float cosAngle = dot(-light.direction.xyz, lightDir); float spotFactor = smoothstep(light.outerCosAngle, light.innerCosAngle, cosAngle); // Diffuse float diff = max(dot(normal, lightDir), 0.0); float3 diffuse = diff * light.color.xyz * light.color.w; // Specular float3 halfVec = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfVec), 0.0), 32.0); float3 specular = spec * light.color.xyz * light.color.w * 0.5; return (diffuse + specular) * attenuation * spotFactor; } [shader("fragment")] float4 fragMain(VSOutput vertIn) : SV_Target { // Sample textures float3 albedo = albedoTexture.Sample(vertIn.uv).rgb; // Ambient float3 ambient = 0.03 * albedo; // Lighting accumulé float3 lighting = float3(0.0, 0.0, 0.0); // Récupérer les lumières LightsBuffer lb = lightsBuffer[0]; for (int i = 0; i < lb.lightCount; i++) { LightData light = lb.lights[i]; int lightType = (int)light.position.w; if (lightType == 0) lighting += computeDirectional(light, vertIn); else if (lightType == 1) lighting += computePoint(light, vertIn); else if (lightType == 2) lighting += computeSpot(light, vertIn); } // Résultat final float3 finalColor = (ambient + lighting) * albedo; // Tone mapping simple (optionnel) // finalColor = finalColor / (finalColor + 1.0); return float4(finalColor, 1.0); }