Cg Browser Effects Explained
Introduction
This document explains how the various shaders in the Cg Browser work. Some of the samples are designed to demonstrate simple techniques, such as transform, lighting, texture coordinate generation, and texture coordinate transformation. Others are intended to show the vast possibilities that vertex and pixel shaders enable. Some samples have been modified from existing SDK demos to show the advantages of using a programmable vertex shader over the traditional fixed-function T&L. The samples are designed to be educational, and provide inspiration for the uses of Cg along with programmable shaders.
Please note that although some demos might run on GeForce2 and MX-class products, the majority of shaders packaged with the Cg Browser require a GeForce3 or better graphics card to run.
Following is a list of shaders, in the order that they appear in the Cg Browser. Each link navigates to a description of the shader as well as an accompanying snapshot.
NV30 Shaders
Please note that these shaders are not included by default in the Cg Browser. Instructions for working with them are included in the Readme.txt file that accompanies each shader project.
| Improved Skinning | ![]() |
MultiPaint | ![]() |
Raytraced Refraction | ![]() |
| Improved Water | ![]() |
![]() |
Skin | ![]() |
NV2X Shaders
NV30 Shaders
| Improved Water | ![]() |
|
This demo gives the appearance that
the viewer is surrounded by a large grid of vertices (because of the free
rotation, but by switching to wireframe, or by increasing the frustum
angle, it becomes apparent that the vertices are a static mesh, with the
height, normal and texture coordinates being calculated on the fly based of
the direction and height of the viewer. This technique allows for very GPU
friendly water animations since the static mesh can be precomputed.
The vertices are displaced using sine waves, and in this example, a loop
is used to sum up five sine waves, to achieve realistic effects.
|
| Improved Skinning | ![]() |
|
This shader takes in a set of all the transformation
matrices that can affect a particular bone. Each bone also sends in a list
of matrices that affects it. There is then a simple loop that for each
vertex goes through each bone that affects that given vertex and
transforms it. This allows just one Cg program to do the entire skinning
for vertices affected by any number of bones, instead of having one
program for one bone, another program for two bones, and so
on.
|
| MultiPaint | ![]() |
|
MultiPaint presents a single-pass solution to a common
production problem -- mixing multiple kinds of materials on a single
polygonal surface. MultiPaint provides a simple BRDF that is still complex
enough to represent many common metallic and dielectric surfaces, then
controls all key factors of the variable BRDF through texturing. This
permits you to create multiple materials without switching shaders,
splitting your model, or resorting to multiple passes.
Uses for MutliPaint might include complex armor built of inlaid metals, woods, and stones, all modeled on a single simple poly mesh; buildings composed of multiple types of stone, glass, and metal, expressed as simple cubes; cloth with inlaid metallic threads; or as in this demo, metal partially-covered with peeling paint. Using multiple BRDFs is common in the offline world, but rarely optimized -- instead two different shaders may be evaluated and their results blended using a mask texture or chained through "if" statements. For maximum realtime performance, MultiPaint instead integrates all of the key parts of the BRDFs as multiple painted textures, so that only one pass through the shader is required to create the mixed appearance. This permits a single-pass shader containing diffuse, specular, and environmental lighting effects in a compact, fast-executing package. |
| Melting Paint | ![]() |
|
This shader uses an
environment map with procedurally modified texture lookups to create a
melting effect on the surface texture (the NVIDIA logo in this example).
The reflection vector is shifted using a noise function, giving the
appearance of a bumpy surface. The surface texture's texture
coordinates are shifted in a time-dependent manner, also based on a noise
texture.
|
| Raytraced Refraction | ![]() |
|
This shader presents a method
for adding high-quality details to small objects using a single-bounce ray
traced pass. In this example, the
polygonal surface is sampled and a refraction vector is calculated. This
vector is then intersected with a plane that is defined as being
perpendicular to the object's X axis. The intersection point is calculated
and used as texture indices for a painted Iris.
The demo permits varying the index of refraction, the depth and density of the lens. Note that the choice of geometry is arbitrary -- this sample is a sphere, but any polygonal model can be used.
|
| Skin | ![]() |
|
This effect demonstrates some techniques for rendering
skin ranging from simple Blinn-Phong Bump-Mapping to more-complex
Subsurface Scattering lighting models. It also illustrates the use of
"Rim" lighting and simple translucency for capturing some of the
more subtle properties of skin resulting from complex, non-local lighting
interactions. Finally, it shows how the various techniques can be combined
to produce compelling stylized skin.
|
NV2X Shaders
| Matrix Palette Skinning | ![]() |
|
This shader performs matrix palette skinning with vertex diffuse lighting. Each vertex has four indices which reference the four bones (input as uniform parameters) which influence this particular vertex, along with the weights of influence. We calculate the vertex position in world space as influenced by each bone (so, four separate positions are generated per vertex) and then we blend these four positions according to the weights to generate the final vertex position in world space. Since, in this particular mesh, the first two bones influencing each vertex are by far the most significant (have the largest weights), we only transform the normals by the first two bones and their respective weights, and then normalize the result. This normal is then used with the uniform parameter for the light vector in world space to calculate diffuse intensity.
|
| Simple Fog | ![]() |
|
This example renders a fogged landscape with two types of fog. It shows the interaction of fog calculations with pixel and vertex shader programs. The first fog factor is a traditional screen depth based fog, calculated from the Z component of the vertex after the model-view-project transform. This value is written to the shader oFog.x which is used in a blending stage after the pixel shader program. The second fog term is based on the model space landscape height and appears as the white fog in the valleys. You can change the scaling of this second term with the “<” and “>” keys. The value is passed into the pixel shader program, where it is added to the base texture color. The landscape height is generated as the sum of three blurred and scaled random noise fields. The blurring produces features of varying roughness, and the more blurred components are added in with a greater scale factor.
|
| Simple Lighting | ![]() |
|
This example shows basic diffuse and specular lighting calculations, based on the Phong illumination model. The diffuse term is calculated using the usual N dot L formulation, and the specular term uses the Blinn formulation of N dot H. This is also the example that is used in "A Brief Tutorial", which can be found in the Cg User's Manual.
|
| Bump Dot3 Diffuse Specular | ![]() |
|
This effect computes diffuse and specular lighting in two passes using a normal from a normal map. The first pass lays down the diffuse component, by transforming the light vector into tangent space in the vertex program and then using this light vector along with the normal and a decal texture to compute diffuse lighting. In a second pass, the half-angle vector is computed per-vertex, transformed into tangent space, and passed down in a texture coordinate. H dot N is then computed per-pixel, and the result is squared a number of times to achieve a higher specular power, then modulated by a self-shadowing term and added into the framebuffer.
|
| Bump Dot3x2 Diffuse Specular | ![]() |
|
This demonstrates diffuse and specular illumination on a bump mapped model. N · L and N · H are computed for each rendered pixel. The values of the dot-products are
then used as (u,v) coordinates to address an
irradiance map. This is a
texture which holds a diffuse color ramp in one axis, and a specular color ramp in the alpha channel of the other
axis. In this way, an
arbitrary response to the incident light direction is possible. You can achieve any specular exponent or arbitrary function by editing the
color ramps in a paint program. The demo includes right-button menu options for loading normal
map bump maps, base textures, and irradiance maps, as well as the ability
to visualize the incident light vector or each
texture map on its own.
|
| Bump Horizon Mapping | ![]() |
|
This effect does per-pixel bump mapping and makes every bump
cast a shadow using the horizon mapping technique. Based on the
height map, 8 horizon maps are precomputed corresponding to 8 main light
directions in the tangent plane. Every horizon map texel holds the
cosine of the angle between the normal and the light direction below which
this texel is shadowed by its neighboring texels. At run-time, we
assume that the surface is locally planar enough that the visibility
pre-computation holds true. For every pixel, we compare the actual
cosine of the angle between the normal and the current light direction to
the limit stored in the horizon map and determine if the pixel is in
shadow or not. The current light direction falls in-between two of
the 8 main light directions; the value of the limit is thus computed by
linear interpolation between those 2 main directions.
|
| Bump Reflection Mapping | ![]() |
|
This shader demonstrates how to perform the ubiquitous "bumpy shiny" effect in Cg, where a per-pixel normal is used to compute a reflection vector to lookup into a cubemap. The vertex program performs the bumpy-shiny setup, by passing a matrix to transform a vector from tangent space to "cube map space" (usually world space) as texture coordinates, along with an eye vector in world space. The pixel shader then computes the texCUBE_reflect_eye_dp3x3 function, which transforms the normal fetched from a normal map by the 3x3 interpolated matrix, computes a reflected vector using the eye vector and transformed normal, and uses this reflected vector to lookup into a cubemap.
|
| Detail Normal Maps | ![]() |
|
This demo shows a technique for combining normal maps at runtime. There is a "detail" normal map, which shows fine detail and fades in as you get closer to the surface, and a larger scale primary normal map. The vertex program does standard setup for tangent-space bump-mapping, and passes a light vector in tangent space to the pixel shader. At the pixel level, we combine the two normals together, which results in a non-unit length normal, and then renormalize the normal using a fast single iteration Newton-Raphson approximation. This normal is then used to calculate the diffuse intensity, which is then modulated by the base texture.
|
| Water Interaction | ![]() |
|
The demo is meant to show two main things: The coupling of two water
simulations allows a large area of procedural water to be created while
still allowing for unique local features. The reflection method used
performs the same basic calculation as DX6-style EMBM, but in this case
the 2x2 rotation matrix and base texture coordinates are calculated in a
vertex shader and can vary per-vertex. The water simulation is
performed entirely on the graphics hardware using pixel shaders.
Nearest-neighbor differencing and sampling at each texel is accomplished
using a combination of vertex and pixel shaders. |
| Grass Demo | ![]() |
|
This demo shows the construction and animation of leaves of grass being calculated on the GPU. Each leaf is created by a Bezier curve with a few control points that are procedurally moved around to accommodate the wind. The normals of the leaves is also calculated from this Bezier curve. The vertices being sent to the shader consist of one degenerate quad strip for each leaf, with all the vertices collapsed to the root point of the leaf, but each vertex has a different t parameter which determines where on the Bezier curve the vertex should be located.
|
| Sine Wave Perturbation | ![]() |
|
A flat grid of polygons is deformed by
a sine wave, sampled from the constant memory using a The input data for this shader is simply 2 floating-point values. The shader generates all normal, texture and color information itself. The menu-option allows rendering in wire frame, and left clicking allows rotating the mesh.
|
| Water Demo | ![]() |
|
This demo gives the appearance that the viewer is surrounded by a large grid of vertices (because of the free rotation, but by switching to wireframe, or by increasing the frustum angle, it becomes apparent that the vertices are a static mesh, with the height, normal and texture coordinates being calculated on he fly based of the direction and height of the viewer. This technique allows for very GPU friendly water animations since the static mesh can be precomputed.
|
| Depth Sprites | ![]() |
|
This
demo illustrates how texture shaders can be used
to create sprites with real depth data. The depth sprites differ from
normal sprites in that they can realistically intersect in 3D with other
depth sprites or standard 3D objects. The register combiners are used to
normalize the light vector, calculate its reflection off the normal mapped
surface, as well as calculate both diffuse and specular lighting equations.
|
| Lighting Demo | ![]() |
|
This
demo shows how to implement each of the standard OpenGL lighting types in
the vertex
shader. It includes examples of spotlights, local lights, and
infinite
lights.
|
| Dot3 Diffuse Specular | ![]() |
|
This shader performs diffuse and
specular lighting in two passes using an interpolated normal, and shows a
couple of different ways to perform per-fragment vector normalization. In
the first pass, diffuse lighting is calculated as follows: in the vertex
program, a light vector and normal are calculated and passed to the pixel
program. At the pixel level, the light vector is normalized using a
Newton-Raphson approximation and the light vector is normalized using a
normalization cubemap. The normal, light vector, and decal texture are
then used to compute the diffuse lighting term. In a second pass, the
vertex program calculates the half-angle vector and normal, and the pixel
program normalizes these two vectors, performs H dot N, and squares the
result a number of times to achieve a higher specular power. This value is
then added into the framebuffer.
|
| Flare | ![]() |
| This glow effect is produced by applying a
convolution filter to a low resolution texture which contains bright
pixels at sources of glow. First, a "glow source" texture is created by
rendering the object to a texture render target. For this rendering, a
mask channel, here stored in the alpha channel of the object's base
texture, determines which parts of the object contribute bright pixels to
the "glow source" texture. The rendered texture is then set as the input
texture for the convolution operation. The result of the convolution is accumulated
in another texture, the "glow" texture, which will contain the
brightness of the blurry glow for everything in the scene as viewed from
the camera's point of view. The scene is rendered normally, and the "glow"
texture is then rendered to the full screen with additive blending.
To perform the convolution of glow source pixels into bright blurry glow, several rendering passes may be used. On GeForce3 and GeForce4, each pass accumulates four samples of the convolution kernel. Each sample is multiplied by a coefficient to determine the shape of the glow, and this shape can be any arbitrary function. To save passes, a separable convolution is used which blurs first horizontally, and then blurs the horizontal blur vertically. This may or may not be close to a separable Gaussian convolution. If the desired blur size is NxN texels, this approach reduces the number of samples which must be accumulated from N*N to 2*N, a substantial savings. This approach sacrifices some flexibility in determining the shape of the blur. The effect runs from about 200 to 650 fps in windowed mode. Most of the time for rendering in the windowed mode is spent in blitting from backbuffer to the windowed area. To make this less significant, reduce the size of the window. This blit() time is not required for fullscreen mode, so the effect will run faster in a fullscreen application. |
| Procedural Terrain Demo | ![]() |
|
Reference: "Texturing and Modeling, A Procedural Approach", Ebert et. al
|
| Vertex Noise | ![]() |
|
Reference: http://mrl.nyu.edu/~perlin/doc/oscar.html
|
| Fresnel Reflection Demo | ![]() |
|
When light strikes a boundary between different media, for example, air and glass, some of the light gets refracted and some gets reflected. The amount of reflection depends on the ratio of refraction-indices of the two media, the polarization of the light, the wavelength of the light, and the angle of incidence of the light. Fresnel’s formula provides an accurate description of how much light reflects at the boundary: (1) R(q) = ⅛ (sin2(q - qt) / sin2(q + qt)) (1 + cos2(q - qt) / cos2(q + qt)) We are using two approaches to
approximate the Fresnel reflection formulae: per-pixel and
per-vertex. Both compute a
reflection vector per-vertex and use it to look up a reflection value
per-pixel via a cubic environment-map. The Fresnel reflection value then
blends this reflection-value with a material-color.
The per-pixel approximation derives the Fresnel reflection
value from a texture look-up.
It computes cos(q)
per-vertex -- cos(q)
= N·E
is readily available in the vertex-shader, as it is
required in computing the reflection-vector -- and uses the interpolated
per-pixel cos(q)
in a 1D texture look-up that encodes
R(arcos(x)). The per-vertex approximation of Fresnel reflection computes the
Fresnel term R(q) per
vertex. This per-vertex
term
R(q) is then
linearly interpolated and applied per-pixel. Encoding equation (1) in a
vertex-shader is straightforward, but also sub-optimal in terms of number
of vertex-shader instructions and thus performance. Approximating
R(q) as
(2)
R(q)
»
Ra(q)
= R(0) + (1-R(0)) (1-cos(q))5 The menu options allow switching between the per-pixel and per-vertex approximation, as well as switching to wire-frame rendering. Different indices of refraction are accessible via the menu or the +/- keys. |
| Refract Reflect Demo | ![]() |
|
This demo calculates a reflection
and a refraction vector based on the normal on the surface of the object
and modulates them based on Fresnel properties. See the white-paper “Fresnel
Reflection” on
http://www.nvidia.com/developer for more
information about Fresnel effects.
|
| Refractive Dispersion Demo | ![]() |
| This example attempts to simulate the
wavelength dependent nature of light refraction. In lens design this
effect is also known as chromatic aberration.
The code calculates three different refraction vectors for the red, green and blue wavelengths of light, each with slightly different indices of refraction. Each of these vectors is used to index into a cube map of the environment, and the resulting colors are modulated by red, green and blue and then summed to produce the rainbow effect. A reflection vector is also calculated, and used to index into the same cube map, making a total of four texture lookups. The reflection is modulated by a Fresnel approximation, which makes surfaces facing perpendicular to the viewer appear more reflective. Reference: http://www.botzilla.com/house/RayPS.html
|
| Hardware Shadow Maps | ![]() |
|
This example shows the usage of hardware shadow maps. A render-to-texture to a shadow map is done from the light's point-of-view, updating only depth (no color). This shadow map is then set as a texture when rendering the scene normally. The vertex shader computes the necessary texture coordinates to sample the correct texel from the shadow map given the current position, along with the current depth at that point to compare with the value in the shadow map. At the pixel level, a simple fetch from the shadow map will automatically perform a comparison between the value in the shadow map and the (r / q) texture coordinate, returning black when in shadow and white when in the light. This shadow term is then used to modulate the lighting. Learn more about shadow mapping at http://developer.nvidia.com/view.asp?IO=shadow_mapping.
|
| Soft Stencil Shadow Volumes | ![]() |
|
This demo shows a brute force approach to creating soft shadows in graphics hardware. It applies several stencil shadow volume passes to the scene from different light positions to approximate an area light. Each shadow volume pass creates a faint hard edged shadow, and the accumulation of many faint shadows blends into an accurate soft shadow. The technique is well suited to creating realistic soft shadows from area light sources, though the required number of passes may be prohibitive for real-time interactivity. Still, using hardware acceleration of the shadow volume creation and rendering greatly improves the speed at which frames may be rendered. Shadow volumes are created on the GPU by the same technique demonstrated in our "Stencil Shadow Volumes" demo. To learn more about shadow volumes, check out http://developer.nvidia.com/view.asp?IO=robust_shadow_volumes.
|
| Stencil Shadow Volumes | ![]() |
| This demo presents a technique for the
automatic creation of shadow volume geometry. A vertex shader is used to
extrude closed hull 3D models into shadow volumes. The shadow volumes are
used for stencil buffer shadow rendering, and the volumes themselves can
be viewed by hitting the 'SPACE BAR' in the demo. This technique
accurately renders self-shadowing objects and intersecting objects.
Without the use of a vertex shader, creation of the shadow volume geometry
would require costly CPU processing and additional memory. Using a vertex
shader, shadow volumes can be generated directly from 3D objects without
stalling the graphics pipeline.
In order for the vertex shader to extrude objects with sharp features to the proper shadow volume shape, additional triangles and vertices must be added along sharp edges. The demo code has a simple class to add such triangles and vertices to geometry where needed. This extra geometric data is required only when rendering the extruded shadow volumes. It can be skipped when rendering the objects normally to the screen. To learn more about shadow volumes, check out http://developer.nvidia.com/view.asp?IO=robust_shadow_volumes. |
| Anisotropic Lighting | ![]() |
|
This effect shows how arbitrary ad-hoc lighting models can be encoded in textures. Here, we have a texture with a bright diagonal area that encodes and anisotropic lighting-type effect. The vertex program calculates H dot N for the texture coordinate for one axis of a texture and L dot N for the other axis. End result is a bright stripe in areas where H dot N and L dot N are roughly equal.
|
| Grass Rendering | ![]() |
|
This example shows a volume rendering technique similar to the shell rendering technique from "Real-Time Fur Over Arbitrary Surfaces" by Lengyel et al, except by rendering four layers in a single pass, with eight total layers in only two passes. The first pass computes the texture coordinates for the first four steps along the eye vector in a vertex program. At the pixel level, we use these texture coordinates to look up into four textures representing the first four layers of grass, blend them together based on the density at each pixel, and output this intermediate color to the framebuffer. In a second pass, the final four steps along the eye ray are computed, the last four layers of grass texture are blended together, and the alpha blender is setup to correctly combine these two passes to result in the final rendering of eight layers of grass. An additional tweak is provided to cap the z-component of the eye vector in the vertex program, since this has a tendency to grow extremely large and cause aliasing as the surface is viewed edge-on.
|