Specular effects in the Ice Cave demo are implemented using the Blinn technique. This is a very efficient technique that produces good results.
The following code shows how to implement specular effects with the Blinn
technique:
// Returns intensity of a specular effect without taking into account shadows
float SpecularBlinn(float3 vert2Light, float3 viewDir, float3 normalVec, float4 power)
{
float3 floatDir = normalize(vert2Light - viewDir);
float specAngle = max(dot(floatDir, normalVec), 0.0);
return pow(specAngle, power);
}
A downside of the Blinn technique is that it can produce incorrect
results in certain circumstances. For example, specular effects can appear in regions
that are in shadow.
The following figure shows an example of a shadowed region with no specular
effects:
Figure 6-42 Shadowed region
The following figure shows an example of specular effects in a shadowed
region. These are incorrect:
Figure 6-43 Shadowed region with incorrect specular effects
The following figure shows an example of specular effects in a lit region. This
is correct:
Figure 6-44 Lit region with correct specular effects
The shadows should make the specular effects intensity either stronger or
weaker, depending on the light reaching the specular surface. All the information
required to correct the intensity of the specular effects is already in the Ice Cave
demo, so fixing it is relatively simple. The environment cubemap texture that is used
for reflection and shadow effects contains two types of information. The RGB channels
contain environment colors that are used for reflections. The alpha channel contains
opacity and this is used for shadows. You can use the alpha channel to determine the
specular intensity because the alpha channel represents holes in the cave that let light
in to the environment. The alpha channel is therefore used to ensure the specular effect
is applied only to surfaces that are lit.
To do this, calculate a corrected reflection vector in a fragment shader to
make the reflection effect. For more information about creating the corrected reflection
vector see
6.2 Implementing reflections with a local cubemap. Use
this vector to fetch the RGBA texel from a cubemap texture:
// Locally corrected static reflections
const half4 reflColor = SampleCubemapWithLocalCorrection(
ReflDirectionWS,
_ReflBBoxMinWorld,
_ReflBBoxMaxWorld,
input.vertexInWorld,
_ReflCubePosWorld,
_ReflCube);
reflColor
is in RGBA format where the RGB
components contain color data for reflections and the alpha channel contains the
intensity of the specular effect. In the Ice Cave demo, the alpha channel is multiplied
by the specular color, that is calculated with the Blinn technique:
half3 specular = _SpecularColor.rgb *
SpecularBlinn(
input.vertexToLight01InWorld,
viewDirInWorld,
normalInWorld,
_SpecularPower) *
reflColor.a;
The specular
value represents the final
specular color. You can add this to your lighting model.