The implementation accompanying this article can act as a basis for several variations:
The destination contribution to the alpha blending can be changed from InvSrcAlpha to One. This creates an additive blending effect that more strongly shows where the lines overlap. This is most effective when line colors are not already in full intensity. This also has the advantage of eliminating any differences caused by draw order when lines of different colors overlap.
If the scene does not require intermixing 3D solid shaded objects with the rendered lines, then the vertex shader can be simplified even further since the need for tracking depth is eliminated.
The sample implementation uses zero-length lines to represent points. This could easily be special-cased to make them more efficient. By using a solid texture (or eliminating the texture altogether), you can create jaggy lines of an exact fixed width.
The sample implementation assumes that each collection of lines uses a single color. This color could be moved into the vertex structure to allow multiple colors to be used in a single draw call.
CONCLUSION
In this article, we have presented a simple and efficient method for rendering high-quality, anti-aliased lines. The technique has two key advantages over previously published methods. First, by taking advantage of the GPU’s texturing capability, we are able to avoid almost all per-fragment processing. Second, the technique allows for correct integration with 3D solid shaded scenes.
A
PPENDIXA: T
HES
HADERC
ODE// // A A Li nes .fx // //
// Gl obal vari abl es
//
fl oat4x4 g_m atW orl d; // W orl d m atri x for obj ec t
fl oat4x4 g_m atW orl dV i ewP roj ec ti on; // W orl d * V i ew * P roj ec ti on m atri x
fl oat4 g_Col or;
texture g_Fi l terT exture; s am pl er Fi l terT extureS am pl er = s am pl er_s tate {
T exture = <g_Fi l terT exture>;
Mi pFi l ter = None; // Im portant!
Mi nFi l ter = Li near; MagFi l ter = Li near; A ddres s U = Mi rror; A ddres s V = Mi rror; };
//
// V ertex s hader output s truc ture. // s truc t V S _OUT P UT {
fl oat4 pos i ti on : P OS IT ION;
fl oat4 textureUV : T E X COORD0; // vertex texture c oords }; //
// V ertex s hader for A A l i nes
// V S _OUT P UT
V S ( fl oat3 pos 0 : P OS IT ION0,
fl oat3 pos 1 : P OS IT ION1,
fl oat4 wei ghts : T E X COORD0,
fl oat radi us : T E X COORD1,
fl oat as pec t : T E X COORD2 )
{
V S _OUT P UT Output;
// T rans form the i nput poi nts .
fl oat4 p0 = m ul ( fl oat4( pos 0, 1.0f ), g_m atW orl dV i ewP roj ec ti on );
fl oat4 p1 = m ul ( fl oat4( pos 1, 1.0f ), g_m atW orl dV i ewP roj ec ti on );
// W arp trans form ed poi nts by as pec t rati o. fl oat4 w0 = p0; fl oat4 w1 = p1; w0.y /= as pec t; w1.y /= as pec t;
// Cal c vec tors between poi nts i n s c reen s pac e.
fl oat2 del ta2 = w1.xy / w1.z - w0.xy / w0.z;
fl oat3 del ta_p;
del ta_p.xy = del ta2; del ta_p.z = w1.z - w0.z;
//
// Cal c UV bas i s vec tors .
// // Cal c U
fl oat l en = l ength( del ta2 );
fl oat3 U = del ta_p / l en; // Create V orthogonal to U. fl oat3 V ; V .x = U.y; V .y = -U.x; V .z = 0;
// Cal c ul ate output pos i ti on bas ed on thi s
// vertex's wei ghts .
Output.pos i ti on = p0 * wei ghts .x + p1 * wei ghts .y;
// Cal c offs et part of pos i ti on.
fl oat3 offs et = U * wei ghts .z + V * wei ghts .w;
// A ppl y l i ne thi c knes s .
offs et.xy *= radi us ;
// Unwarp by i nvers e of as pec t rati o. offs et.y *= as pec t;
// Undo pers pec ti ve di vi de s i nc e the hardware wi l l do i t.
Output.pos i ti on.xy += offs et * Output.pos i ti on.z;
// S et up UV s . W e have to us e proj ec ted s am pl i ng rather
// than regul ar s am pl i ng bec aus e we don't want to get
// pers pec ti ve c orrec ti on. Output.textureUV .x = wei ghts .z; Output.textureUV .y = wei ghts .w; Output.textureUV .z = 0.0f; Output.textureUV .w = 1.0f; return Output; } // end of V S () // // P i xel s hader // fl oat 4 P S ( V S _OUT P UT In ) { fl oat4 res ul t;
res ul t = g_Col or * tex2Dproj ( Fi l terT extureS am pl er, In.textureUV ); return res ul t; } // end of P S () //
// A A l i nes tec hni que.
//
tec hni que A A Li nes { pas s P 0 { V ertexS hader = c om pi l e vs _2_0 V S (); P i xel S hader = c om pi l e ps _2_0 P S (); A l phaRef = 1;
A l phaT es tE nabl e = fal s e;
A l phaFunc = GreaterE qual ;
// S et up bl end renders tates
A l phaB l endE nabl e = true;
S rc B l end = S rc A l pha;
Des tB l end = InvS rc A l pha; // Change to "One" and us e a c ol or that's
not ful l y s aturated
B l endOp = A dd; // to s how overl appi ng areas m ore s trongl y.
Cul l Mode = None;
Li ghti ng = fal s e; ZE nabl e = true;
ZFunc = Les s E qual ;
ZW ri teE nabl e = fal s e; }
}
R
EFERENCES[McNamara et al. 00] Robert McNamara, Joel McCormack, and Norman P. Jouppi. “Prefiltered Antialiased Lines Using Half-Plane Distance Functions.” In Proceedings of the ACM SIGGRAPH/ EUROGRAPHICS Workshop on Graphics Hardware (Interlaken, Switzerland, August 21–22, 2000). S. N. Spencer, Ed. HWWS ‘00.
[Upstill 90] Steve Upstill (1990). The RenderMan Companion: A Programmer’s Guide to Realistic Computer Graphics. Reading, Mass: Addison- Wesley. ISBN 0-201-50868-0. OCLC 19741379
2.4 Fast Skin Shading
JOHN HABLE, GEORGE BORSHUKOV, AND JIM HEJL
INTRODUCTION
Rendering realistic skin is a difficult problem in computer graphics, and especially in real-time. Most real-time applications that re-create heads use normal maps and diffuse maps to create an object with the shape and color of a head. However, these applications often use the same generic lighting model for skin that they use for cardboard, concrete, and plastic. Consequently, most heads rendered in-game do not “feel” like skin because the lighting model does not accurately model how light affects skin.
Skin looks very different from a pure diffuse surface such as cardboard. One of the fundamental differences between skin and other lighting models is that light bounces around inside of skin. Skin has several layers with differing levels of translucency, whereas a “pure” diffuse model is based on the assumption that when light hits an object, the light scatters equally in all directions. This model is fast to compute, but in order to make CG skin look like real skin, it is necessary to take into account the way light bleeds throughout an object.
This article discusses an implementation of quickly simulating how light transfers underneath skin. This article contains few new advancements, but rather, it synthesizes other people’s great work and applies optimizations. Specifically, this article’s goal is to make realistic skin shading practical in typical games on the current generation of consoles, such as the PlayStation 3 and Xbox 360. For the theory of making realistic subsurface scattering, rather than adding new information, we will try to synthesize existing art into this standalone article. Then, we will discuss several new variations on these techniques that allow fast subsurface scattering to be used in several in-development games at Electronic Arts.
B
ACKGROUND ANDE
XISTINGA
RTThe complete theory of light transfer underneath skin is quite complicated and beyond the scope of this article. For a great discussion, see d’Eon and Luebke’s chapter in GPU Gems 3 [d’EonGPUGems07]. In brief, subsurface scattering happens in three steps. First, the light hits the skin. Second, light bounces around underneath the skin. Third, light exits the skin and is viewed by the camera. We simulate the first step by rendering diffuse light to a light map. The bouncing of the light is simulated by blurring the light map. Finally, light exiting is simulated by multiplying the blurred light by the diffuse map of the face.
While there are numerous techniques for skin shading, this article will talk about texture space diffusion. The idea is quite simple: Render the lights into UV space, blur those lights to simulate subsurface scattering, and render. The first use of this technique was by Borshukov and Lewis in the Matrix sequels [Borshukov03, Borshukov05]. They rendered the light to a map in texture-space, blurred that map, and used the blurred light in lighting calculations. To increase realism, they used different blur kernels for the red, green, and blue color channels, since light red, green, and blue light scatter differently in real skin. For the blur kernel, they used the formula 1/(1 + Radius)p, where p was chosen based on extensive photographic reference. They used 2.75 for red, 3.25 for green, and 3.50 for blue (Figure 2.4.1).
FIGURE 2.4.1 The formula 1/(1 + r)p when p is 3.50, 3.25, and 2.75. The bottom curve is for p = 3.50.
This idea was applied to real-time by Gosselin [Gosselin04], using a Poisson sampling, and Green [Green04], using a Gaussian blur. This work laid the foundation for the Adrianne and Doug Jones demos by NVIDIA [NVIDIA 06, NVIDIA 07] that set the current high bar for skin shading in real- time. The main technical leap in these demos was decomposing the dipole function into the addition of separable Gaussian blurs. These demos also showed several other features including finding a good set of shading parameters for real skin, compensating for UV stretching, popularizing a good specular model skin [Kelemen01], and making this information accessible.
In our opinion, the Doug Jones demo sets the clear standard for high-quality skin shading in real-time. The only problem is that “real-time” for a tech demo is quite different from being fast enough to use in a game in real-use cases. The Doug Jones demo fully taxes the processor of a high- end graphics card. In contrast, most games must run on less powerful consoles. Also, the commercial games must render an entire world, which only leaves a small fraction of time for skin shading. The premise of this article is that the Doug Jones demo is the high standard for skin shading, and the goal of this article is to show various ways to scale the Doug Jones demo down so that it is fast enough for a console but still retains as much quality as possible.