切线空间与法线贴图和环境贴图: Tangent Space & normallMapping & envMapping - MartinRGB/GLES30_ProgrammingGuide_NDK GitHub Wiki
切线空间
Explan 1:
Generally speaking, a Normal vector represents the direction pointing directly "out" from a surface, meaning it is orthogonal (at 90 degree angles to) any vector which is coplanar with (in the case of a flat surface) or tangent to (in the case of a non-flat surface) the surface at a given point.
A Tangent vector is typically regarded as one vector that exists within the surface's plane (for a flat surface) or which lies tangent to a reference point on a curved surface (ie. if a flat plane were constructed with the same normal from the reference point, the tangent vector would be coplanar with that plane).
The concept of a Binormal vector is a bit more complex; in computer graphics, it generally refers to a Bitangent vector (reference here), which is effectively the "other" tangent vector for the surface, which is orthogonal to both the Normal vector and the chosen Tangent vector.
How to calculate Tangent and Binormal?
How to calculate Tangent, Binormal, Normal vectors? Thanks!
how to get tangent of vertex normal?
Explain 2:
First, the tangent and bitangent both need to be perpendicular to the normal. Second, they define the interpretation of the values in the normal map, so their rotations about the normal need to be correct. Which usually means that they should be the partial derivatives of the surface with respect to texture coordinates, i.e. T=∂v/∂s, B=∂v/∂t, with N=T×B.
To find T and B, start with T' = ∂v/∂s = ∂v/∂b*∂b/∂s + ∂v/∂c*∂c/∂s B' = ∂v/∂t = ∂v/∂b*∂b/∂t + ∂v/∂c*∂c/∂t
If the vertex coordinates are va, vb and vc, with va being the vertex corresponding to N, then ∂v/∂b = vb - va ∂v/∂c = vc - va
For the texture coordinates, construct the matrix [sb-sa sc-sa] [tb-ta tc-ta]
and invert it, to get [∂b/∂s ∂b/∂t] [∂c/∂s ∂c/∂t]
which can then be used to calculate B' or T'.
At this point, B', T' and N probably won't be perpendicular, so calculate T=B'×N and B=N×T.
法线贴图
利用法线贴图存储纹理法线,能够提升效率并获取更多细节,法线贴图中,颜色通道(r,g,b)对应着切线空间中的(t,b,n),从而计算光照。
Vert:
void main() {
// ------------------------- this example -------------------------
// 将眼睛坐标转换为世界坐标,然后计算到顶点距离
vec3 eyePositionWorld = (u_mvInverseMatrix * (u_eyePosition)).xyz;
vec3 viewDirectionWorld = eyePositionWorld - position;
// 将光线坐标转换为世界坐标,然后计算到顶点距离
vec3 lightPositionWorld = (u_mvInverseMatrix * (u_lightPosition)).xyz;
vec3 lightDirectionWorld = lightPositionWorld - position;
// 计算切线空间矩阵
vec3 Normal = normalize( normal );
vec3 Tangent = normalize( tangent.xyz );
vec3 Bitangent = normalize( cross( Normal, Tangent ) * tangent.w );
mat3 tangentMatrix = mat3(Tangent,Bitangent , Normal);
v_viewDirection = viewDirectionWorld * tangentMatrix ;
v_lightDirection = lightDirectionWorld * tangentMatrix;
v_texcoord = position.xy;
gl_Position = u_mvpMatrix * vec4(position,1.0);
}
Frag:
void main(){
// ------------------------- this example -------------------------
// 获取基础贴图
vec4 baseColor = texture2D(s_baseMap,v_texcoord);
// 从法线贴图中获取切线空间
vec3 normal = texture2D(s_bumpMap,v_texcoord).xyz;
normal = normalize(normal*2. -1.);
vec3 lightDirection = normalize(v_lightDirection);
vec3 viewDirection = normalize(v_viewDirection);
// 计算光线和法线的夹角
float nDotL = max(0.0,dot(normal,lightDirection));
// 计算反射向量
vec3 reflection = (2. * normal * nDotL) - lightDirection;
// 计算反射光线和视线的夹角
float rDotV = max(0.0, dot(reflection, viewDirection));
// 环境光 基础材质相关
vec4 ambient = vec4(u_ambientColor,1.) * baseColor;
// 漫反射 入射角决定
vec4 diffuse = vec4(u_diffuseColor,1.) * nDotL * baseColor;
// 反射 出射角决定
vec4 specular = vec4(u_specularColor,1.) * pow(rDotV,u_specularPower);
gl_FragColor = baseColor/2. + (ambient + diffuse + specular)/2.0;
}
环境贴图
将照明向量保存在世界空间,而不是切线空间中,因为要利用世界空间反射量来 从 textureCube 中读取图像。
void main(){
// 将光线坐标转换为世界坐标,然后计算到顶点距离
vec3 lightPositionWorld = (modelViewMatrix * vec4(u_lightPosition,1.0)).xyz;
vec3 lightDirectionWorld = lightPositionWorld - position;
v_lightDirection = lightDirectionWorld;
v_normal = normalize( normal );
v_tangent = normalize( tangent.xyz );
v_bitangent = normalize( cross( v_normal, v_tangent ) * tangent.w );
v_texcoord = vUv;
vUv = uv.xy;
gl_Position = u_mvpMatrix * vec4(position,1.0);
}
Frag:
void main(){
// 获取基础贴图
vec4 baseColor = texture2D(s_baseMap,vUv);
// 从法线贴图中获取切线空间
vec3 normal = texture2D(s_bumpMap,vUv).xyz;
normal = normalize(normal*2. -1.);
// 将法线量转换到世界空间
mat3 tangentToWorldMat = mat3(v_tangent,v_bitangent,v_normal);
normal = normalize(tangentToWorldMat * normal);
vec3 lightDirection = normalize(v_lightDirection);
// 计算光线和法线的夹角
float nDotL = dot(normal,lightDirection);
// 计算反射向量
vec3 reflection = (2. * normal * nDotL) - lightDirection;
vec4 envColor = textureCube(s_envMap,reflection);
gl_FragColor = 0.5*baseColor + envColor;
}