Générer le rendu de la scène avec un test de profondeurRender the scene with depth testing

Créez un effet d’ombre en ajoutant un test de profondeur à votre nuanceur de vertex (ou géométrie) et votre nuanceur de pixels.Create a shadow effect by adding depth testing to your vertex (or geometry) shader and your pixel shader. Partie 3 de la Procédure pas à pas : implémenter des volumes d’ombre à l’aide de tampons de profondeur dans Direct3D 11.Part 3 of Walkthrough: Implement shadow volumes using depth buffers in Direct3D 11.

Inclure une transformation pour un tronc de cône lumineuxInclude transformation for light frustum

Votre nuanceur de vertex a besoin de calculer la position transformée de l’espace lumineux pour chaque vertex.Your vertex shader needs to compute the transformed light space position for each vertex. Indiquez le modèle d’espace lumineux, l’affichage et les matrices de projection à l’aide d’un tampon constant.Provide the light space model, view, and projection matrices using a constant buffer. Vous pouvez également utiliser ce tampon constant pour indiquer la position et la normale de la lumière pour les calculs d’éclairage.You can also use this constant buffer to provide the light position and normal for lighting calculations. La position transformée dans l’espace lumineux est utilisée lors du test de profondeur.The transformed position in light space will be used during the depth test.

PixelShaderInput main(VertexShaderInput input)
{
    PixelShaderInput output;
    float4 pos = float4(input.pos, 1.0f);

    // Transform the vertex position into projected space.
    float4 modelPos = mul(pos, model);
    pos = mul(modelPos, view);
    pos = mul(pos, projection);
    output.pos = pos;

    // Transform the vertex position into projected space from the POV of the light.
    float4 lightSpacePos = mul(modelPos, lView);
    lightSpacePos = mul(lightSpacePos, lProjection);
    output.lightSpacePos = lightSpacePos;

    // Light ray
    float3 lRay = lPos.xyz - modelPos.xyz;
    output.lRay = lRay;
    
    // Camera ray
    output.view = eyePos.xyz - modelPos.xyz;

    // Transform the vertex normal into world space.
    float4 norm = float4(input.norm, 1.0f);
    norm = mul(norm, model);
    output.norm = norm.xyz;
    
    // Pass through the color and texture coordinates without modification.
    output.color = input.color;
    output.tex = input.tex;

    return output;
}

Ensuite, le nuanceur de pixels utilise la position de l’espace lumineux interpolée indiquée par le nuanceur de vertex pour tester si le pixel est dans l’ombre.Next, the pixel shader will use the interpolated light space position provided by the vertex shader to test whether the pixel is in shadow.

Tester si la position est dans le tronc de cône lumineuxTest whether the position is in the light frustum

Tout d’abord, vérifiez que le pixel se trouve dans le tronc de cône de l’affichage de la lumière en normalisant les coordonnées X et Y.First, check that the pixel is in the view frustum of the light by normalizing the X and Y coordinates. S’ils sont tous les deux dans la plage [ 0, 1, ] il est possible que le pixel soit une ombre.If they are both within the range [0, 1] then it's possible for the pixel to be in shadow. Sinon, vous pouvez ignorer le test de profondeur.Otherwise you can skip the depth test. Un nuanceur peut le tester rapidement en appelant Saturate et en comparant le résultat à la valeur d’origine.A shader can test for this quickly by calling Saturate and comparing the result against the original value.

// Compute texture coordinates for the current point's location on the shadow map.
float2 shadowTexCoords;
shadowTexCoords.x = 0.5f + (input.lightSpacePos.x / input.lightSpacePos.w * 0.5f);
shadowTexCoords.y = 0.5f - (input.lightSpacePos.y / input.lightSpacePos.w * 0.5f);
float pixelDepth = input.lightSpacePos.z / input.lightSpacePos.w;

float lighting = 1;

// Check if the pixel texture coordinate is in the view frustum of the 
// light before doing any shadow work.
if ((saturate(shadowTexCoords.x) == shadowTexCoords.x) &&
    (saturate(shadowTexCoords.y) == shadowTexCoords.y) &&
    (pixelDepth > 0))
{

Test de profondeur par rapport au mappage d’ombreDepth test against the shadow map

Utilisez une fonction de comparaison d’exemples (soit SampleCmp, soit SampleCmpLevelZero) pour tester la profondeur du pixel dans l’espace lumineux par rapport au mappage de profondeur.Use a sample comparison function (either SampleCmp or SampleCmpLevelZero) to test the pixel's depth in light space against the depth map. Calculez la valeur de la profondeur de l’espace lumineux normalisé, à savoir z / w, puis passez la valeur à la fonction de comparaison.Compute the normalized light space depth value, which is z / w, and pass the value to the comparison function. Puisque nous utilisons un test de comparaison LessOrEqual pour l’échantillon, la fonction intrinsèque renvoie zéro quand le test de comparaison est satisfaisant ; cela indique que le pixel est dans l’ombre.Since we use a LessOrEqual comparison test for the sampler, the intrinsic function returns zero when the comparison test passes; this indicates that the pixel is in shadow.

// Use an offset value to mitigate shadow artifacts due to imprecise 
// floating-point values (shadow acne).
//
// This is an approximation of epsilon * tan(acos(saturate(NdotL))):
float margin = acos(saturate(NdotL));
#ifdef LINEAR
// The offset can be slightly smaller with smoother shadow edges.
float epsilon = 0.0005 / margin;
#else
float epsilon = 0.001 / margin;
#endif
// Clamp epsilon to a fixed range so it doesn't go overboard.
epsilon = clamp(epsilon, 0, 0.1);

// Use the SampleCmpLevelZero Texture2D method (or SampleCmp) to sample from 
// the shadow map, just as you would with Direct3D feature level 10_0 and
// higher.  Feature level 9_1 only supports LessOrEqual, which returns 0 if
// the pixel is in the shadow.
lighting = float(shadowMap.SampleCmpLevelZero(
    shadowSampler,
    shadowTexCoords,
    pixelDepth + epsilon
    )
    );

Calculer l’éclairage avec ou sans l’ombreCompute lighting in or out of shadow

Si le pixel n’est pas dans l’ombre, le nuanceur de pixels doit calculer l’éclairage direct et l’ajouter à la valeur du pixel.If the pixel is not in shadow, the pixel shader should compute direct lighting and add it to the pixel value.

return float4(input.color * (ambient + DplusS(N, L, NdotL, input.view)), 1.f);
float3 DplusS(float3 N, float3 L, float NdotL, float3 view)
{
    const float3 Kdiffuse = float3(.5f, .5f, .4f);
    const float3 Kspecular = float3(.2f, .2f, .3f);
    const float exponent = 3.f;

    // Compute the diffuse coefficient.
    float diffuseConst = saturate(NdotL);

    // Compute the diffuse lighting value.
    float3 diffuse = Kdiffuse * diffuseConst;

    // Compute the specular highlight.
    float3 R = reflect(-L, N);
    float3 V = normalize(view);
    float3 RdotV = dot(R, V);
    float3 specular = Kspecular * pow(saturate(RdotV), exponent);

    return (diffuse + specular);
}

Sinon, le nuanceur de pixels doit calculer la valeur du pixel à l’aide de l’éclairage ambiant.Otherwise, the pixel shader should compute the pixel value using ambient lighting.

return float4(input.color * ambient, 1.f);

Dans la partie suivante de cette procédure pas à pas, découvrez comment prendre en charge les mappages d’ombre sur une gamme de matériel.In the next part of this walkthrough, learn how to Support shadow maps on a range of hardware.