原文链接:http://www.ogre3d.org/tikiwiki/Normal+Mapping+with+Hardware+Skinning+and+Specular&structure=Cookbook
原本标题:Normal Mapping with Hardware Skinning and Specular - Combined normal mapping and hardware skinning
官网中关于法线贴图(Normal Mapping)和置换贴图(Displacement Mapping)的例子有很多,但是它们都不能与硬件蒙皮(Hardware Skinning)一起发挥作用。为了帮助遇到同样问题的人,我写了这篇文章。
使用(Usage)
只需将animatedNormalSpecular赋予你的物体。硬件蒙皮设置为每个顶点(Vertex)绑定三个骨骼(Bone),但你可以根据自己的需要进行更改。
材质中使用了Ogre例子中的阴影片段程序(Shadow Program),如果你的资源路径中已经包含了Ogre SDK中的media文件夹就不必考虑这个问题,否则找到它并添加到你的资源路径中。
材质文件(Material File)
vertex_program AnimatedNormalSpecular_VP hlsl
{
source animatedNormalSpecular.hlsl
entry_point main_vp
target vs_2_0
column_major_matrices false //required for hlsl skinning
includes_skeletal_animation true
default_params
{
param_named_auto worldviewprojmatrix worldviewproj_matrix
param_named_auto light_position light_position_object_space 0
param_named_auto eye_position camera_position_object_space
param_named_auto worldMatrix3x4Array world_matrix_array_3x4
param_named_auto viewProjectionMatrix viewproj_matrix
param_named_auto invworldmatrix inverse_world_matrix
}
}
fragment_program AnimatedNormalSpecular_FP hlsl
{
source animatedNormalSpecular.hlsl
entry_point main_fp
target ps_2_0
default_params
{
param_named_auto lightDiffuse light_diffuse_colour 0
param_named_auto ambientLight ambient_light_colour
param_named_auto specularLight light_specular_colour 0
param_named specular_power float 64
param_named bumpiness float 1
}
}
material animatedNormalSpecular
{
technique
{
pass Single Pass
{
vertex_program_ref AnimatedNormalSpecular_VP
{
}
fragment_program_ref AnimatedNormalSpecular_FP
{
}
shadow_caster_vertex_program_ref HardwareSkinningFourShadow //this part is in the ogre samples somewhere
{
}
//diffuse map
texture_unit
{
texture_alias base_map
texture diffuse.tga
filtering linear linear linear
}
//normal map
texture_unit
{
texture_alias bump_map
texture diffuse_normal.tga
filtering linear linear linear
}
// specular map
texture_unit specular_map
{
texture_alias specular_map
texture specular.png
}
}
}
}
animatedNormalSpecular.hlsl
void main_vp(
float4 position : POSITION,
float2 uv : TEXCOORD0,
float3 normal : NORMAL,
float3 tangent : TANGENT0,
float4 blendIdx : BLENDINDICES,
float4 blendWgt : BLENDWEIGHT,
out float4 oPosition : POSITION,
out float2 oUV : TEXCOORD0,
out float3 oLightVector : TEXCOORD1,
out float3 oHalfAngle : TEXCOORD2,
uniform float4x4 worldviewprojmatrix,
uniform float4 light_position,
uniform float4 eye_position,
uniform float3x4 worldMatrix3x4Array[60],
uniform float4x4 viewProjectionMatrix,
uniform float4x4 invworldmatrix
) {
// Calculate the pixel position using the perspective matrix.
oUV = uv;
// transform by indexed matrix
float4 blendPos = float4(0,0,0,0);
int i;
for (i = 0; i < 3; ++i)
{
blendPos += float4(mul(worldMatrix3x4Array[blendIdx[i]], position).xyz, 1.0) * blendWgt[i];
}
// view / projection
oPosition = mul(viewProjectionMatrix, blendPos);
// transform normal
float3 newnormal = float3(0,0,0);
for (i = 0; i < 3; ++i)
{
newnormal += mul((float3x3)worldMatrix3x4Array[blendIdx[i]], normal) * blendWgt[i];
}
newnormal = mul((float3x3)invworldmatrix, newnormal);
newnormal = normalize(newnormal);
// transform tangent
float3 newtangent = float3(0,0,0);
for (i = 0; i < 3; ++i)
{
newtangent += mul((float3x3)worldMatrix3x4Array[blendIdx[i]], tangent) * blendWgt[i];
}
newtangent = mul((float3x3)invworldmatrix, newtangent);
newtangent = normalize(newtangent);
float3 binormal = cross(newtangent, newnormal);
float3x3 rotation = float3x3(newtangent, binormal, newnormal);
// Calculate the light vector in object space,
// and then transform it into texture space.
float3 temp_lightDir0 = normalize(light_position.xyz - (blendPos * light_position.w));
temp_lightDir0 = normalize(mul(rotation, temp_lightDir0));
oLightVector = temp_lightDir0;
// Calculate the view vector in object space,
// and then transform it into texture space.
float3 eyeDir = normalize(eye_position - blendPos);
eyeDir = normalize(mul(rotation, eyeDir.xyz));
// Calculate the half angle
oHalfAngle = oLightVector + eyeDir;
}
float4 lightDiffuse ;
float4 ambientLight;
float4 specularLight;
float specular_power;
float bumpiness;
sampler base_map;
sampler bump_map;
sampler specular_map;
struct PS_INPUT_STRUCT
{
float2 uv: TEXCOORD0;
float3 light_vector: TEXCOORD1;
float3 half_angle: TEXCOORD2;
};
struct PS_OUTPUT_STRUCT
{
float4 color0: COLOR0;
};
PS_OUTPUT_STRUCT main_fp( PS_INPUT_STRUCT psInStruct )
{
PS_OUTPUT_STRUCT psOutStruct;
float3 base = tex2D( base_map, psInStruct.uv );
float3 bump = tex2D( bump_map, psInStruct.uv );
float specularLevel = tex2D(specular_map, psInStruct.uv).r;
//normalise
float3 normalized_light_vector = normalize( psInStruct.light_vector );
float3 normalized_half_angle = normalize( psInStruct.half_angle );
// "Smooth out" the bump based on the bumpiness parameter.
// This is simply a linear interpolation between a "flat"
// normal and a "bumped" normal. Note that this "flat"
// normal is based on the texture space coordinate basis.
float3 smooth = { 0.5f, 0.5f, 1.0f };
bump = lerp( smooth, bump, bumpiness );
bump = normalize( ( bump * 2.0f ) - 1.0f );
// These dot products are used for the lighting model
// equations. The surface normal dotted with the light
// vector is denoted by n_dot_l. The normal vector
// dotted with the half angle vector is denoted by n_dot_h.
float4 n_dot_l = dot( bump, normalized_light_vector );
float4 n_dot_h = dot( bump, normalized_half_angle );
// Calculate the resulting pixel color,
// based on our lighting model.
// Ambient + Diffuse + Specular
psOutStruct.color0.rgb =
( base * ambientLight) +
( base * lightDiffuse * max( 0, n_dot_l ) ) +
( specularLight * specularLevel * pow( max( 0, n_dot_h ), specular_power ) );
psOutStruct.color0.a = 1.0f; //** Set the alpha component manually
return psOutStruct;
}