Rendern der Szene mit TiefentestRender the scene with depth testing

Erstellen Sie einen Schatteneffekt, indem Sie dem Vertex-Shader (bzw. Geometry-Shader) und dem Pixel-Shader einen Tiefentest hinzufügen.Create a shadow effect by adding depth testing to your vertex (or geometry) shader and your pixel shader. Teil 3 von Exemplarische Vorgehensweise: Implementieren von Schattenvolumes mithilfe von Tiefenpuffern in Direct3D 11.Part 3 of Walkthrough: Implement shadow volumes using depth buffers in Direct3D 11.

Einfügen der Transformation für Licht-FrustumInclude transformation for light frustum

Ihr Vertex-Shader muss für jeden Scheitelpunkt (Vertex) die transformierte Position im Lichtraum berechnen.Your vertex shader needs to compute the transformed light space position for each vertex. Geben Sie die Modell-, Ansichts- und Projektionsmatrizen für den Lichtraum mithilfe eines Konstantenpuffers an.Provide the light space model, view, and projection matrices using a constant buffer. Sie können diesen Konstantenpuffer auch verwenden, um die Lichtposition und Normale für Lichtberechnungen bereitzustellen.You can also use this constant buffer to provide the light position and normal for lighting calculations. Die transformierte Position im Lichtraum wird während des Tiefentests verwendet.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;
}

Als Nächstes wird vom Pixelshader die vom Vertex-Shader bereitgestellte interpolierte Lichtraumposition verwendet, um zu testen, ob das Pixel im Schatten liegt.Next, the pixel shader will use the interpolated light space position provided by the vertex shader to test whether the pixel is in shadow.

Testen, ob sich die Position im Licht-Frustum befindetTest whether the position is in the light frustum

Prüfen Sie zuerst, ob sich das Pixel im Ansichts-Frustum des Lichts befindet, indem Sie die X- und Y-Koordinaten normalisieren.First, check that the pixel is in the view frustum of the light by normalizing the X and Y coordinates. Wenn beide im Bereich von 0 liegen [ , ] ist es möglich, dass das Pixel im Schatten ist.If they are both within the range [0, 1] then it's possible for the pixel to be in shadow. Andernfalls können Sie den Tiefentest überspringen.Otherwise you can skip the depth test. Mit einem Shader kann dies ohne viel Zeitaufwand getestet werden, indem Saturate aufgerufen und das Ergebnis mit dem Originalwert verglichen wird.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))
{

Tiefentest unter Verwendung der SchattenmapDepth test against the shadow map

Verwenden Sie eine Samplevergleichsfunktion (entweder SampleCmp oder SampleCmpLevelZero), um die Tiefe des Pixels im Lichtraum unter Verwendung der Tiefenmap zu testen.Use a sample comparison function (either SampleCmp or SampleCmpLevelZero) to test the pixel's depth in light space against the depth map. Berechnen Sie den normalisierten Lichtraum-Tiefenwert, für den z / w gilt, und übergeben Sie den Wert an die Vergleichsfunktion.Compute the normalized light space depth value, which is z / w, and pass the value to the comparison function. Da wir für den Sampler einen LessOrEqual-Vergleichstest verwenden, wird von der systeminternen Funktion null zurückgegeben, wenn der Vergleichstest bestanden wurde; dies gibt an, dass das Pixel im Schatten liegt.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
    )
    );

Berechnen der Beleuchtung innerhalb und außerhalb des SchattensCompute lighting in or out of shadow

Wenn das Pixel nicht im Schatten liegt, kann der Pixelshader die direkte Beleuchtung berechnen und dem Pixelwert hinzufügen.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);
}

Andernfalls sollte der Pixelshader den Pixelwert mithilfe des Umgebungslichts berechnen.Otherwise, the pixel shader should compute the pixel value using ambient lighting.

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

Der nächste Teil dieser exemplarischen Vorgehensweise beschäftigt sich mit dem Unterstützen von Schattenmaps für unterschiedliche Hardware.In the next part of this walkthrough, learn how to Support shadow maps on a range of hardware.