Cg Programming/Unity/Displacement Maps

Irregular shapes like this asteroid can be created using a spherical mesh with vertices that are moved by a displacement map.

This tutorial introduces displacement maps as an example of an application of texture lookups in vertex shaders.

This tutorial requires some knowledge about texture mapping as described in Section “Textured Spheres”.

Most tutorials in this wikibook use texture lookups only in fragment shaders because texture lookups in vertex shaders are a Shader Model 3.0 feature, i.e., it is not supported by all GPUs (see Unity's description of platform-specific rendering differences).

However, displacement mapping requires a lookup of a texture value for each vertex, which is used to displace, i.e., move, each vertex to a new position. For technical reasons, we cannot use the standard `tex2d` command. Instead we have to use the command `tex2dlod` with a four-dimensional vector specifying the texture coordinates where the 4th component is 0. (This makes sure that we always read the value from the finest mipmap level and we don't request an automatic computation of the mipmap level, which is impossible in a vertex shader.) The command could look like this:

```      float4 dispTexColor = tex2Dlod(_DisplacementTex, float4(i.texcoord.xy, 0.0, 0.0));
```

where `i.texcoord.xy` are the two texture coordinates.

Displacement of Vertices

The example below converts the resulting color `dispTexColor` to a gray value and scales it with a user-specified uniform `_MaxDisplacement`:

```      float displacement = dot(float3(0.21, 0.72, 0.07), dispTexColor.rgb) * _MaxDisplacement;
```

This displacement value is then used to move each vertex along its surface normal vector, i.e., we add the vertex position in object coordinates (`i.vertex` in the example) to the surface normal vector (`i.normal`) multiplied with the displacement value.

```      float4 newVertexPos = i.vertex + float4(i.normal * displacement, 0.0);
```

Note that `i.normal` is a three-dimensional vector; thus, we have to append a 0.0 coordinate to form a four-dimensional vector before we can add it to the vertex position.

The rest of the code applies the standard vertex transformations and colors the surface with a unlit texture map. Make sure to use it on meshes with relatively many vertices - otherwise the displaced surface will appear quite "blocky."

```Shader "Vertex Displacement" {
Properties {
_MainTex ("Main Texture", 2D) = "white" {}
_DisplacementTex ("Displacement Texture", 2D) = "white" {}
_MaxDisplacement ("Max Displacement", Float) = 1.0
}
Pass {
CGPROGRAM

#pragma vertex vert
#pragma fragment frag

uniform sampler2D _MainTex;
uniform sampler2D _DisplacementTex;
uniform float _MaxDisplacement;

struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};

struct vertexOutput {
float4 position : SV_POSITION;
float4 texcoord : TEXCOORD0;
};

vertexOutput vert(vertexInput i) {
vertexOutput o;

// get color from displacement map, and convert to float from 0 to _MaxDisplacement
float4 dispTexColor = tex2Dlod(_DisplacementTex, float4(i.texcoord.xy, 0.0, 0.0));
float displacement = dot(float3(0.21, 0.72, 0.07), dispTexColor.rgb) * _MaxDisplacement;

// displace vertices along surface normal vector
float4 newVertexPos = i.vertex + float4(i.normal * displacement, 0.0);

// output data
o.position = UnityObjectToClipPos(newVertexPos);
o.texcoord = i.texcoord;
return o;

}

float4 frag(vertexOutput i) : COLOR
{
return tex2D(_MainTex, i.texcoord.xy);
}

ENDCG
}
}
}
```

Note that edges of the surface mesh might not match up if the colors at the edges of the texture image do not match or if the surface normal vectors of corresponding vertices do not match.

Summary

Congratulations, you have reached the end of this tutorial. You saw:

• How use texture mapping in vertex shaders.
• How to move vertex positions along surface normal vectors.