August 24, 2017
Hot Topics:

Environment Mapping Techniques

7.4 The Fresnel Effect and Chromatic Dispersion

You now know how to implement reflection and refraction. The next example combines them and throws in a few other extensions. You will learn about two new effects: the Fresnel effect and chromatic dispersion.

7.4.1 The Fresnel Effect

In general, when light reaches an interface between two materials, some light reflects off the surface at the interface, and some refracts through the surface. This phenomenon is known as the Fresnel effect (pronounced "freh-'nell"). The Fresnel equations describe how much light is reflected and how much is refracted. If you have ever wondered why you can see fish in a pond only when you're looking practically straight down, it's because of the Fresnel effect. At shallow angles, there is a lot of reflection and almost no refraction, so it is hard to see through the water's surface.

The Fresnel effect adds realism to your images, because it allows you to create objects that exhibit a mix of reflection and refraction, more like real-world objects.

The Fresnel equations, which quantify the Fresnel effect, are complicated. (You can learn more about them in most optics textbooks.) Once again, the idea here is to create images that look plausible, not necessarily to describe accurately the intricacies of the underlying physics. So, instead of using the equations themselves, we are going to use the empirical approximation in Equation 7-3, which gives good results with significantly less complication:

Equation 7-3. An Approximation of the Fresnel Equation

The concept underlying this equation is that when I and N are nearly coincident, the reflection coefficient should be 0 or nearly 0, indicating that most of the light should be refracted. As I and N diverge, the reflection coefficient should gradually increase and eventually abruptly increase (due to the exponentiation) to 1. When I and N are sufficiently divergent, almost all the light should be reflected, with little or none of it being refracted.

The range of the reflection coefficient is clamped to the range [0, 1], because we use the reflection coefficient to mix the reflected and refracted contributions according to the following formula (where C stands for color):

Figure 7-9. Understanding Chromatic Dispersion

7.4.2 Chromatic Dispersion

The earlier discussion of refraction was somewhat simplified. We mentioned that refraction depends on the surface normal, incident angle, and ratio of indices of refraction. In addition to these factors, the amount of refraction also depends on the wavelength of the incident light. For example, red light gets refracted more than blue light. This phenomenon is known as chromatic dispersion, and it is what happens when white light enters a prism and emerges as a rainbow.

Figure 7-9 illustrates chromatic dispersion conceptually. The incident illumination (assumed to be white) is split into several refracted rays. You will simulate what happens to the red, green, and blue components of the light, because these are the standard components of colors in computer graphics. You will use the refracted red, green, and blue rays to look up into the environment map, just as you did for a single ray in the refraction example.

Keep in mind that real light is a band of wavelengths rather than three particular and discrete wavelengths. Still, this approximation is effective enough to be useful.

Figure 7-10. The Fresnel Effect and Chromatic Dispersion

Combining the Fresnel effect with chromatic dispersion creates a rainbow effect, as if the rendered object were made of crystal, as shown in Figure 7-10. Plate 11, in the book's center insert, shows this image in color.

7.4.3 Application-Specified Parameters

Because we are now using a more complicated lighting model for our object's surface, the application needs to send extra uniform parameters to the vertex and fragment programs. These additional parameters are listed in Table 7-3.

The x, y, and z components in etaRatio, respectively, store the ratio of indices of refraction for red, green, and blue light. The fresnelPower, fresnelScale, and fresnelBias variables provide a way to shape the function that we use to approximate the Fresnel equations. Together, all the application-specified parameters define the material properties of your object.

Table 7-3. The C7E5v_dispersion Program Parameters

 Parameter Variable Name Type Ratio of indices of refraction for red, green, and blue light (packed into one float3) etaRatio float3 Fresnel power fresnelPower float Fresnel scale fresnelScale float Fresnel bias fresnelBias float

7.4.4 The Vertex Program

The C7E5v_dispersion vertex program in Example 7-5 calculates the reflected vector, along with red, green, and blue refracted vectors. In addition, you will use the approximation of Fresnel's formula to compute the reflection coefficient. All this information is then interpolated and received by the fragment program.

Calculating the Reflected Vector

The reflected vector calculation stays the same:

`R = reflect(I, N);`
Calculating the Refracted Vectors

You compute refracted vectors using an approach that is similar to the one that you used in the earlier refraction example. The difference is that now you have to calculate a refraction vector for each color component, instead of just one that applies equally to red, green, and blue:

`TRed   = refract(I, N, etaRatio.x);TGreen = refract(I, N, etaRatio.y);TBlue  = refract(I, N, etaRatio.z);`

Recall that the x, y, and z components in etaRatio respectively store the ratio of indices of refraction for red, green, and blue light.

`void C7E5v_dispersion(float4 position : POSITION,                      float3 normal   : NORMAL,                  out float4 oPosition        : POSITION,                  out float  reflectionFactor : COLOR,                  out float3 R                : TEXCOORD0,                  out float3 TRed             : TEXCOORD1,                  out float3 TGreen           : TEXCOORD2,                  out float3 TBlue            : TEXCOORD3,              uniform float fresnelBias,              uniform float fresnelScale,              uniform float fresnelPower,              uniform float3 etaRatio,              uniform float3 eyePositionW,              uniform float4x4 modelViewProj,              uniform float4x4 modelToWorld){  oPosition = mul(modelViewProj, position);  // Compute position and normal in world space  float3 positionW = mul(modelToWorld, position).xyz;  float3 N = mul((float3x3)modelToWorld, normal);  N = normalize(N);  // Compute the incident, reflected, and refracted vectors  float3 I = positionW  eyePositionW;  R = reflect(I, N);  I = normalize(I);  TRed   = refract(I, N, etaRatio.x);  TGreen = refract(I, N, etaRatio.y);  TBlue  = refract(I, N, etaRatio.z);  // Compute the reflection factor  reflectionFactor = fresnelBias +                     fresnelScale * pow(1 + dot(I, N),                                        fresnelPower);}`

Example 7-5. The C7E5v_dispersion Vertex Program

Calculating the Reflection Coefficient

Translating Equation 7-3 into Cg code is straightforward. Use the dot and pow functions. The program outputs reflectionFactor as an interpolated color, as indicated by its associated COLOR semantic. Interpolated colors are automatically clamped to the range [0, 1], so there is no need to perform this clamping explicitly.

`  reflectionFactor = fresnelBias +                     fresnelScale * pow(1 + dot(I, N),                                        fresnelPower);`

7.4.5 The Fragment Program

The C7E6f_dispersion fragment program in Example 7-6 receives all the interpolated data for the reflected and refracted vectors, along with the reflection coefficient that is clamped to [0, 1]. The fragment program looks up the various reflected and refracted vectors in an environment map and blends the results appropriately. Notice that the program expects the same environment cube map texture for each of the four texture units. The application must bind the environment map to each of these four texture units, because the program is written to run on both basic and advanced fragment profiles. Recall that basic fragment profiles can only sample a given texture unit with that texture unit's corresponding texture coordinate set, so the environment map must be replicated. Advanced fragment profiles do not have this limitation, so a single environmentMap cube map sampler would suffice.

Performing the Texture Lookups

First, the program performs four cube map lookups—one for the reflected color, and one for each component of the three refracted colors:

`// Fetch the reflected environment colorfloat4 reflectedColor = texCUBE(environmentMap0, R);// Compute the refracted environment colorfloat4 refractedColor;refractedColor.r = texCUBE(environmentMap1, TRed).r;refractedColor.g = texCUBE(environmentMap2, TGreen).g;refractedColor.b = texCUBE(environmentMap3, TBlue).b;`

For each of the three refracted texture lookups, the program uses swizzling to extract only the matching color component. That is, you extract the red component of the texture value sampled at TRed, the green component of the texture value sampled at TGreen, and the blue component of the texture value sampled at TBlue. The program then combines the respective r, g, and b components of refractedColor.

`void C7E6f_dispersion(float reflectionFactor : COLOR,                      float3 R               : TEXCOORD0,                      float3 TRed            : TEXCOORD1,                      float3 TGreen          : TEXCOORD2,                      float3 TBlue           : TEXCOORD3,                  out float4 color : COLOR,              uniform samplerCUBE environmentMap0,              uniform samplerCUBE environmentMap1,              uniform samplerCUBE environmentMap2,              uniform samplerCUBE environmentMap3){  // Fetch the reflected environment color  float4 reflectedColor = texCUBE(environmentMap0, R);  // Compute the refracted environment color  float4 refractedColor;  refractedColor.r = texCUBE(environmentMap1, TRed).r;  refractedColor.g = texCUBE(environmentMap2, TGreen).g;  refractedColor.b = texCUBE(environmentMap3, TBlue).b;  refractedColor.a = 1;  // Compute the final color  color = lerp(refractedColor,  reflectedColor,  reflectionFactor);}`

Example 7-6. The C7E6f_dispersion Fragment Program

Computing the Final Result

Finally, the program blends the reflected and refracted colors according to the fraction given by the reflection factor:

`color = lerp(refractedColor,             reflectedColor,             reflectionFactor);`

And there you have it: the Fresnel effect with chromatic dispersion.

Page 4 of 5

Comment and Contribute

(Maximum characters: 1200). You have characters left.

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Most Popular Developer Stories

Thanks for your registration, follow us on our social networks to keep up-to-date