It's free to join Gamasutra!|Have a question? Want to know who runs this site? Here you go.|Targeting the game development market with your product or service? Get info on advertising here.||For altering your contact information or changing email subscription preferences.
Registered members can log in here.Back to the home page.

Search articles, jobs, buyers guide, and more.

By Philippe Beaudoin
[Author's Bio]
and Juan Guardado
[Author's Bio]
Gamasutra
August 1, 2002

Traditional Techniques

Power Function on the Pixel Shader

Applications

The Pixel Shader

Printer Friendly Version
   



This feature is an excerpt from Direct3D ShaderX: Vertex and Pixel Shader Tips and Tricks, edited by Wolfgang Engel.

[Purchase Book]

Letters to the Editor:
Write a letter
View all letters


Features

A Non-Integer Power Function on the Pixel Shader

The Pixel Shader
Let's put all this together into a single pixel shader. First, each texture stage and interpolated color is assigned as follows:


Table 3. Pixel shader register usage for Phong shading.

It should be noted that, since interpolated colors do not support negative values, we place the sign corrected light vector in color 0. This can be written as L' = 0.5(L + (1,1,1) ).

We need to have access to KAmbc and LAmbc that are constant from one pixel to the next. Due to the nature of the equation, we can precompute the product of these coefficients. Therefore, we store the resulting vector (KAmbR*LAmb, KAmbG*LAmbG, KAmbB*LAmbB ) in the first three components of constant register c0.

We also need the values of constant coefficients LDiffc and LSpecc. These are stored in the first three components of registers c1 and c2, respectively.

Finally, we need to pick the approximation exponent m used for the power function. In our example, we use m = 8. We also use _x4 as the multiply instruction modifier for each instruction of the power function approximation. Since we wish to use a value of n varying per pixel, we must precompute k-1/m = 0.074325. We store this value in the alpha channel of constant register c0. Looking at Table 2, we find that the maximal n that can be achieved is 116.34. Also, using the function TranslateTexture of Listing 10 we can convert per texel values of n into values of A' to be placed in the alpha component of texture 0.

The pixel shader that computes per pixel Phong shading then becomes:


ps.1.1
; v1 RGB: light vector (L') (sign corrected)
; c0 RGB: ambient light (Kamb*Lamb), A: 1/MaxAB(logM) = 0.074325
; c1 RGB: diff light (Ldiff), A: 0
; c2 RGB: spec light (Lspec), A: 0

  tex t0     ; diffuse texture (Mdiff) + A' (exponent')
  tex t1     ; normal map (N')
  tex t2     ; specular texture (Mspec)
  tex t3     ; normalised halfway vector (H)

  dp3_sat      r0.rgb, v1_bx2, t1_bx2   ; L'.N'
+ sub          r0.a, c0.a, t0.a         ; B'

  dp3_sat      r1, t3_bx2, t1_bx2       ; H.N'

  mad r0.rgb,  r0, c1, c0               ; Kamb*Lamb + Ld*L'.N'
+ mad_x4_sat   r1.a, t0.a, r1.a, r0.a   ; p0 = k0*max(A'x+B', 0)

  mul          r1.rgb, t2, c2           ; Ms*Ls
+ mul_x4       r1.a, r1.a, r1.a         ; p1 = p0*p0

  mul_x4       r1.a, r1.a, r1.a         ; p2 = p1*p1
  mul_x4       r1.a, r1.a, r1.a         ; p = p2*p2 = H.N'^n

  mul          r1.rgb, r1, r1.a         ; Ms*Ls*H.N'^n
  mad          r0, r0, t0, r1           ; Ma + Md*Ld*L'.N' + Ms*Ls*H.N'^n

Listing 14. Code for Phong shading with various features
(pixel shader versions 1.1, 1.2 and 1.3)

Summary

We have presented a method of approximating a non-integer power function on a pixel shader by using as few texture stages as possible and gracefully degrading in accuracy depending on the desired exponent and available number of blend stages. Furthermore, the technique can be used for single or multiple channels, thus adapting nicely to individual shader requirements.

The method has several advantages over traditional exponentiation techniques that use either a texture look-up or a series of sequential multiplications. Texture look-ups are only accurate for pixel shader versions 1.4 and greater, and even then will require two phases. Sequential multiplications need a large number of stages to compute high power-of-two exponents and even more for non-power-of-two. Additionally, since the multiplications are inherently uniform during the entire shader, they do not allow for a smooth variation in power.

A couple of applications were suggested, and Phong shading in particular is covered in the results below. We believe such a useful technique can be applied to many other algorithms that require a power function, especially considering that it can be abstracted to any effect requiring a sharp yet smooth transition, such as a spotlight's cone falloff.

The per pixel variation of the exponent, which is a handy extension to the basic principle, can provide important visual cues for surfaces whose specularity varies, such as for a material including both metallic and organic features. Its main disadvantages are that m constrains the lower bound of the specular exponent n, as explained in the mathematical details section, and that one component of a texture must be used to encode the exponent. The latter, however, is expected of any technique that varies the exponent per pixel.

The shading code of Listing 14 was applied to red spheres with different properties, such as faceted per pixel exponent maps, wrinkled normal maps, and orange specular maps. The results can be seen in Figure 12. Due to the many iterative multiplications, there is a large accumulation of error that manifests itself as banding. Generally speaking, the greater the exponent, the more banding will be evident, however, this is mostly noticeable on smooth surfaces, such as those expressed with uniform normal maps. The banding artifacts are less significant when using normal maps that contain some perturbation because the specular falloff is quite sharp due to the abrupt changes in the normal. Therefore, visual artifacts are reduced as detail in the normal map is increased.

Banding can also result from reduced instruction counts. If additional instructions are available, we recommend using them to maximize precision. Note that since the Phong shading algorithm presented only requires exponentiation of a single component, the instructions easily fit into the scalar pipeline of the pixel shader, which reduces the number of dedicated stages. The shader code of Listing 14 consumes two stages purely for exponentiation purposes, but instructions in the vector pipeline can be co-issued at these stages if desired.

For example, the specular texture can be removed if the diffuse and specular materials are represented by a single texture. We can then use a cube map at that stage to represent the surrounding environment, even encoding a Fresnel term in the remaining colour register. The math computations can easily be accommodated within the two remaining vector instructions.

Finally, the images in Figure 12 were rendered on hardware with 8 bits of fractional precision. Other hardware is available which has more than 8 bits of fractional precision and will suffer less from the banding artifacts.

We hope you found this trick helpful and that you will find many more uses for an approximating power function in your shading endeavors.





Figure 12. Per pixel Phong shading on spheres. Clockwise from upper left, uniform normal and exponent maps, uniform normal map and faceted exponent map, noisy normal and exponent maps with uniform orange specular material, and noisy normal and exponent maps with uniform white specular material.

______________________________________________________

[Back To] Traditional Techniques


join | contact us | advertise | write | my profile
news | features | companies | jobs | resumes | education | product guide | projects | store



Copyright © 2003 CMP Media LLC

privacy policy
| terms of service