Cg - The Cg Tutorial Ch8. Bump Mapping 筆記
1. Benefits of bump mapping include
① A higher level of visual complexity in a scene, without adding more geometry.
② Simplified content authoring, because you can encode surface detail in textures as opposed
to requiring artists to design highly detailed 3D models.
③ The ability to apply different bump maps to different instances of the same model to give each
instance a distinct surface appearance.
2. To allow texture-filtering hardware for unsigned colors to operate correctly, signed texture values
in the [-1, 1] range are range-compressed to the unsigned [0, 1] range with a simple scale and bias.
colorComponent = 0.5 * normalComponent + 0.5;
normalComponent = 2 * (colorComponent - 0.5);
Recent GPUs have no performance penalty for expanding range-compressed normals from textures.
3. Most normal maps are derived from what is known as a height field. A height field stores a single
unsigned component per texel rather than using three components to store a vector.
In the Height Field, darker regions of the height field are lower; lighter regions are higher.
4. How to generate Normal Maps from Height Fields?
① The normal vector is the normalized version of the cross product of two difference vectors.
i. (1, 0, Hr – Hg ), where Hg is the height of the given texel and Hr is the height of the texel
directly to the right of the given texel.
ii.(0, 1, Ha – Hg ), where Ha is the height of the texel directly above the given texel.
② (Hg – Hr , Hg – Ha , 1)
normal = ──────────────────────── // normalized
((Hg – Ha)2 + (Hg – Hr)2 + 1)^2
③ This normal is signed and must be range-compressed to be stored in an unsigned RGB texture.
p.s. Due to the nature of the conversion process from height field to normal map, the z component
is always positive and often either 1.0 or nearly 1.0. The z component is stored in the blue
component conventionally, and range compression converts positive z values to the [0.5, 1] range.
Thus, the predominant color of range-compressed normal maps stored in an RGB texture is blue.
5. The normalization cube map—a fast way to normalize vectors supplied as texture coordinates—
works on all GPUs, whether or not they support advanced fragment programmability.
For example,
float3 expand(float3 v)
{
return (v-0.5)*2; // Expand a range-compressed vector
}
void C8E2f_bumpSurf(float2 normalMapTexCoord : TEXCOORD0,
float3 lightDir : TEXCOORD1,
out float4 color : COLOR,
uniform sampler2D normalMap,
uniform samplerCUBE normalizeCube)
{
// Normalizes light vector with normalization cube map
float3 lightTex = texCUBE(normalizeCube, lightDir).xyz; // 1
float3 light = expand(lightTex); // 2
// 1+2 equal to: float3 light = normalize(lightDir);
// Sample and expand the normal map texture
float3 normalTex = tex2D(normalMap, normalMapTexCoord).xyz;
float3 normal = expand(normalTex);
// Diffuse lighting
color = dot(normal,light);
}
p.s. Even on GPUs supporting advanced fragment profiles, using normalization cube maps is often
faster than implementing normalization with math operations, because GPU designers highly
optimize texture accesses.
*if float3 a = (3, 1.5, 0.9), 經過texCUBE(normalizeCube, a)之後會得到b = (0.93, 0.72, 0.63)
然後再將b取expand就會得到c = (0.83, 0.43, 0.26) = normalized a
6. Object-Space Bump Mapping vs Texture-Space Bump Mapping
1. Benefits of bump mapping include
① A higher level of visual complexity in a scene, without adding more geometry.
② Simplified content authoring, because you can encode surface detail in textures as opposed
to requiring artists to design highly detailed 3D models.
③ The ability to apply different bump maps to different instances of the same model to give each
instance a distinct surface appearance.
2. To allow texture-filtering hardware for unsigned colors to operate correctly, signed texture values
in the [-1, 1] range are range-compressed to the unsigned [0, 1] range with a simple scale and bias.
colorComponent = 0.5 * normalComponent + 0.5;
normalComponent = 2 * (colorComponent - 0.5);
Recent GPUs have no performance penalty for expanding range-compressed normals from textures.
3. Most normal maps are derived from what is known as a height field. A height field stores a single
unsigned component per texel rather than using three components to store a vector.
In the Height Field, darker regions of the height field are lower; lighter regions are higher.
4. How to generate Normal Maps from Height Fields?
① The normal vector is the normalized version of the cross product of two difference vectors.
i. (1, 0, Hr – Hg ), where Hg is the height of the given texel and Hr is the height of the texel
directly to the right of the given texel.
ii.(0, 1, Ha – Hg ), where Ha is the height of the texel directly above the given texel.
② (Hg – Hr , Hg – Ha , 1)
normal = ──────────────────────── // normalized
((Hg – Ha)2 + (Hg – Hr)2 + 1)^2
③ This normal is signed and must be range-compressed to be stored in an unsigned RGB texture.
p.s. Due to the nature of the conversion process from height field to normal map, the z component
is always positive and often either 1.0 or nearly 1.0. The z component is stored in the blue
component conventionally, and range compression converts positive z values to the [0.5, 1] range.
Thus, the predominant color of range-compressed normal maps stored in an RGB texture is blue.
5. The normalization cube map—a fast way to normalize vectors supplied as texture coordinates—
works on all GPUs, whether or not they support advanced fragment programmability.
For example,
float3 expand(float3 v)
{
return (v-0.5)*2; // Expand a range-compressed vector
}
void C8E2f_bumpSurf(float2 normalMapTexCoord : TEXCOORD0,
float3 lightDir : TEXCOORD1,
out float4 color : COLOR,
uniform sampler2D normalMap,
uniform samplerCUBE normalizeCube)
{
// Normalizes light vector with normalization cube map
float3 lightTex = texCUBE(normalizeCube, lightDir).xyz; // 1
float3 light = expand(lightTex); // 2
// 1+2 equal to: float3 light = normalize(lightDir);
// Sample and expand the normal map texture
float3 normalTex = tex2D(normalMap, normalMapTexCoord).xyz;
float3 normal = expand(normalTex);
// Diffuse lighting
color = dot(normal,light);
}
p.s. Even on GPUs supporting advanced fragment profiles, using normalization cube maps is often
faster than implementing normalization with math operations, because GPU designers highly
optimize texture accesses.
*if float3 a = (3, 1.5, 0.9), 經過texCUBE(normalizeCube, a)之後會得到b = (0.93, 0.72, 0.63)
然後再將b取expand就會得到c = (0.83, 0.43, 0.26) = normalized a
6. Object-Space Bump Mapping vs Texture-Space Bump Mapping
① 利用normal map來儲存object space normal,即在implement bump mapping時,直接使用這些normal來
取代object space上原本的normal,此方法稱為object-space bump mapping。此方法限制了normal map
必須要根據所設定好的、準備要拿來implement bump mapping的geometry而計算出來,當遇到不同的
geometry結構的object或者是object在移動時更動了vertex position,則normal map就必須再根據當下
的geometry重新計算,也就是說,一個normal map只能給一種固定位置、固定形狀的object來做bump mapping用
② Texture-space bump mapping is to transform vectors from object space to the normal map's texture space
by multiply a rotate matrix. 如此,同一個normal map便可以給所有不同位置、不同結構的object使用
③ The columns of a rotation matrix used to transform object-space direction vectors into a normal map's
texture space are named tangent (T), binormal (B), and normal (N), respectively.
④ For example, Instead of rendering a bump-mapped brick wall that has an object-space surface normal (0, 0, 1),
consider rendering the same brick-textured rectangle repositioned so it becomes a brick floor rather than a wall.
The surface normal for the floor is (0, 1, 0), straight up in the y direction. In this floor example, apply the same
brick normal map to the floor that you applied to the wall in the last example. However, "straight up" in the
normal map is the (0, 0, 1) vector, while "straight up" for the floor in object space is (0, 1, 0). These two
coordinate systems are not consistent.
What does it take to make these two coordinate systems consistent? The floor has the same normal at every
point, so the following rotation matrix transforms the floor's object-space "straight up" vector to the normal
map's corresponding "straight up" vector
7. Consistent Texture-Space Bump Mapping vs. Inconsistent Object-Space Bump Mapping
8. In a mesh, more than one triangle may share a given vertex in the mesh. Typically, each triangle
that shares a particular vertex will have its own distinct tangent, binormal, and normal vectors.
9. As with the decal map and normal map, the gloss map can share the same texture coordinate
parameterization with these other texture maps. The gloss map can often be stored in the alpha
component of the decal map (or even the bump map). For example, this fragment of Cg code
shows how a decal texture can provide both the decal material and a gloss map:
color = lightColor * (decal.rgb * (ambient + diffuse) + decal.a * specular);
10. ① Geometric self-shadowing accounts for the fact that a surface should not reflect a light if the light
source is below the plane of the surface. The diffuse and specular case are in the following:
diffuse = max(dot(normal, light), 0);
specular = dot(normal, light) > 0 ? max(dot(normal, halfAngle), 0) : 0;
② When you bump map, there are actually two surface normals: the conventional interpolated normal
and the perturbed surface normal supplied by the normal map. One way to think about these two
normals is that the interpolated normal is a large-scale approximation of the surface orientation,
and the perturbed normal is a small-scale approximation of the surface orientation. If either normal
faces away from the incoming light direction, then there should be no illumination from the light.
③ When you're lighting in texture space for bump mapping, the light vector's z component indicates whether
the light is above or below the horizon of the geometric (or large-scale) normal. If the z component is
negative, the geometric orientation of the surface faces away from the light and there should be no
illumination from the light on the surface.
④ You can implement this in Cg as follows:
diffuse = light.z > 0 ? max(dot(normal, light), 0) : 0;
The ?: test enforces geometric self-shadowing for the large-scale surface orientation; the max function
enforces geometric self-shadowing for the small-scale surface orientation.
⑤ You can implement the geometric self-shadowing for specular bump mapping in Cg this way:
specular = dot(normal, light) > 0 && light.z > 0 ?max(dot(normal, halfAngle), 0) : 0;





浙公网安备 33010602011771号