Relief Mapping (ピクセルシェーダによる単純なレイトレーシング)
数年前, 学生の頃にOpenGL2.0とNVidia Cgで実装したRelief Mappingの画像です.
- ピクセルシェーダで単純なレイトレーシングを行って, ポリゴン上に幾何形状をリアルタイムに描画します.
- 今回のリリーフマッピング用に使用したテクスチャデータは ディフューズアルベドテクスチャ(RGB), 法線マップテクスチャ(RGB), ハイトマップテクスチャ(R)です.
- 実装の際に参考にした記事は, ShaderX4の"Rendering Surface Detials in Games with Relief Mapping Using a Minimally Invasive Approach"です.
一応,ソースコードも貼り付けておきます.
// 頂点シェーダ (NVidia Cg)
struct VertexInput
{
float4 position : POSITION;
float2 texcoord : TEXCOORD0;
};
struct VertexOutput
{
float4 position : POSITION;
float2 texcoord : TEXCOORD0;
float3 object_position : TEXCOORD1;
float4 color : COLOR;
};
VertexOutput main(VertexInput vertex_input,
uniform float4 color)
{
VertexOutput vertex_output;
// position
vertex_output.position = mul(glstate.matrix.mvp, vertex_input.position);
// object_position
vertex_output.object_position = vertex_input.position.xyz;
// texcoord
vertex_output.texcoord = vertex_input.texcoord;
return vertex_output;
}フラグメントシェーダ (NVidia Cg)
struct FragmentInput
{
float2 texcoord : TEXCOORD0;
float3 object_position : TEXCOORD1;
float4 color : COLOR;
};
struct FragmentOutput
{
float4 color : COLOR;
};
#define RELIEF_LINEAR_SEARCH_STEP 10
#define RELIEF_BINARY_SEARCH_STEP 6
void rayIntersectLinear(sampler2D relief_texture, inout float3 tex_vec,
inout float3 eye_vec)
{
eye_vec /= RELIEF_LINEAR_SEARCH_STEP;
for (int i = 0; i < RELIEF_LINEAR_SEARCH_STEP - 1; i++)
{
float4 relief_vec4 = tex2D(relief_texture, tex_vec.xy);
if ( tex_vec.z < relief_vec4.w)
tex_vec += eye_vec;
}
}
void rayIntersectBinary(sampler2D relief_texture,
inout float3 tex_vec, inout float3 eye_vec)
{
for (int i = 0; i < RELIEF_BINARY_SEARCH_STEP; i++)
{
eye_vec *= 0.5;
float4 relief_vec4 = tex2D(relief_texture, tex_vec.xy);
if ( tex_vec.z < relief_vec4.w)
tex_vec += 2 * eye_vec;
tex_vec -= eye_vec;
}
}
FragmentOutput main(FragmentInput fragment_input,
uniform float3 eye_position,
uniform float3 tbn_tangent,
uniform float3 tbn_binormal,
uniform float3 tbn_normal,
uniform float object_x_unit,
uniform float object_y_unit,
uniform float relief_depth,
uniform float tile_scale,
uniform float use_texture,
uniform sampler2D texture,
uniform float use_relief_texture,
uniform sampler2D relief_texture)
{
FragmentOutput fragment_output;
fragment_output.color = float4(1, 1, 1, 1);
float3 object_position_eye_vec = fragment_input.object_position - eye_position;
float3 n_object_position_eye_vec = normalize(object_position_eye_vec);
float cos_z_object_position_eye = dot(- n_object_position_eye_vec, tbn_normal);
// eye_vec in TBN space
float3 n_eye_tbn_vec = normalize( float3( dot(n_object_position_eye_vec, tbn_tangent),
dot(n_object_position_eye_vec, tbn_binormal),
cos_z_object_position_eye) );
// scale depth
float3 scale = float3(object_x_unit, object_y_unit, relief_depth) / tile_scale;
n_eye_tbn_vec *= scale.z / (scale * n_eye_tbn_vec.z);
// search tex_vec by raycast
float3 tex_vec = float3(fragment_input.texcoord * tile_scale, 0);
rayIntersectLinear(relief_texture, tex_vec, n_eye_tbn_vec);
rayIntersectBinary(relief_texture, tex_vec, n_eye_tbn_vec);
// expand normal to object_space
float3 object_normal = tex2D(relief_texture, tex_vec.xy).xyz - 0.5;
object_normal = normalize(object_normal.x * tbn_tangent.xyz +
object_normal.y * tbn_binormal.xyz +
object_normal.z * tbn_normal.xyz);
float3 new_object_position = fragment_input.object_position +
n_object_position_eye_vec * tex_vec.z * relief_depth / cos_z_object_position_eye;
// color
fragment_output.color = tex2D(texture, tex_vec.xy);
// diffuse, specular
float4 diffuse_sum = float4(0, 0, 0, 0);
float4 specular_sum = float4(0, 0, 0, 0);
for (int i = 0; i < LIGHT_NUM; i++)
{
float4 tmp_diffuse;
float4 tmp_specular;
computeLight(glstate.light[i].position, glstate.light[i].diffuse,
glstate.light[i].specular,
new_object_position, object_normal, eye_position,
1.0,
tmp_diffuse, tmp_specular);
diffuse_sum += tmp_diffuse;
specular_sum += tmp_specular;
diffuse_sum = saturate(diffuse_sum);
specular_sum = saturate(specular_sum);
}
fragment_output.color *= (diffuse_sum + specular_sum);
return fragment_output;
}






