跳到主要内容
← Vertex Transformations

Vertex Transformations

Normal in Non-uniform Scale

transpose 让“垂直”在被拉扯变形后的空间里仍然保持垂直,而 inverse 负责把被拉歪的比例反过来修正。

代码复盘 HLSL

第 24 课:Normal in Non-uniform Scale

我的解:transpose 让“垂直”在被拉扯变形后的空间里仍然保持垂直,而 inverse 负责把被拉歪的比例反过来修正。

本课需 HLSL 顶点 / 纹理对象,暂以代码复盘呈现。

我的解

cbuffer Transform : register(b0) {
  float4x4 modelMatrix;
  float4x4 viewMatrix;
  float4x4 projectionMatrix;
  float iTime;
};

struct VSInput {
  float3 position : Position;
  float3 normal : NORMAL0;
};

struct VSOutput {
  float4 position : SV_Position;
  float3 normal: NORMAL0;
};

// HLSL does not provide a built-in inverse() function.
// This utility manually computes the inverse of the
// upper-left 3×3 portion of a 4×4 matrix, assuming
// the bottom row and rightmost column are identity.
float4x4 inverse4x4(float4x4 m) {
  float a00 = m._11, a01 = m._12, a02 = m._13;
  float a10 = m._21, a11 = m._22, a12 = m._23;
  float a20 = m._31, a21 = m._32, a22 = m._33;

  float det = a00 * (a11 * a22 - a12 * a21)
            - a01 * (a10 * a22 - a12 * a20)
            + a02 * (a10 * a21 - a11 * a20);

  float invDet = 1.0 / det;

  float4x4 inv;
  inv._11 =  (a11 * a22 - a12 * a21) * invDet;
  inv._12 = -(a01 * a22 - a02 * a21) * invDet;
  inv._13 =  (a01 * a12 - a02 * a11) * invDet;
  inv._21 = -(a10 * a22 - a12 * a20) * invDet;
  inv._22 =  (a00 * a22 - a02 * a20) * invDet;
  inv._23 = -(a00 * a12 - a02 * a10) * invDet;
  inv._31 =  (a10 * a21 - a11 * a20) * invDet;
  inv._32 = -(a00 * a21 - a01 * a20) * invDet;
  inv._33 =  (a00 * a11 - a01 * a10) * invDet;
  inv._44 = 1.0;
  return inv;
}

float3x3 extractFloat3x3(float4x4 m) {
  return float3x3(
    m._11, m._12, m._13,
    m._21, m._22, m._23,
    m._31, m._32, m._33
  );
}

float4x4 getModel() {
  float4 r1 = float4(cos(iTime) + 2.0, 0.0, 0.0, 0.0);
  float4 r2 = float4(0.0, 1.0, 0.0, 0.0);
  float4 r3 = float4(0.0, 0.0, 1.0, 0.0);
  float4 r4 = float4(0.0, 0.0, 0.0, 1.0);
  float4x4 transform = transpose(float4x4(r1, r2, r3, r4));

  return mul(modelMatrix, transform);
}

VSOutput main(VSInput input) {
  VSOutput output;

  float4x4 model = getModel();

  float3x3 normTransform = extractFloat3x3(transpose(inverse4x4(model)));
  output.normal = normalize(mul(normTransform, input.normal));

  float4 localPos = mul(model, float4(input.position, 1.0));
  float4 viewPos  = mul(viewMatrix, localPos);
  output.position = mul(projectionMatrix, viewPos);

  return output;
}

复盘笔记

  • transpose 让“垂直”在被拉扯变形后的空间里仍然保持垂直,而 inverse 负责把被拉歪的比例反过来修正。
  • algebra能证明,拉扯变形的法向量可以通过transpose(inverse4x4(model))来修复。