vault backup: 2026-05-03 21:38:46
This commit is contained in:
@@ -123,14 +123,183 @@ float GetMaterialOrthoBlendWeight(FMaterialVertexParameters Parameters)
|
||||
- [[屏幕对齐网格]] — 另一种屏幕空间变换方案
|
||||
- [[局部位置缩放]] — 局部空间的顶点变换
|
||||
|
||||
## 修改文件列表
|
||||
## 完整代码解析
|
||||
|
||||
| 文件 | 修改类型 |
|
||||
|------|---------|
|
||||
| `Shaders/Private/Common.ush` | 新增 `GetOrthoBlendPosition` / `GetOrthoBlendPosition2` |
|
||||
| `Shaders/Private/BasePassVertexShader.usf` | 调用正交混合 |
|
||||
| `Shaders/Private/DepthOnlyVertexShader.usf` | 调用正交混合 |
|
||||
| `Shaders/Private/PositionOnlyDepthVertexShader.usf` | 调用正交混合 |
|
||||
| `Shaders/Private/HitProxyVertexShader.usf` | 调用正交混合 |
|
||||
| `Shaders/Private/DebugViewModeVertexShader.usf` | 调用正交混合 |
|
||||
| 所有 VertexFactory `.ush` | 提供 ObjWorldPosition |
|
||||
### Common.ush — V1:X轴正交混合
|
||||
|
||||
```hlsl
|
||||
// ASW Change : 2016/03/29 11:30:55 Takuro.K
|
||||
// V1:X轴正交混合
|
||||
// 在透视投影的X轴坐标和正交投影的X轴坐标之间做 lerp
|
||||
// weight 来自材质属性 MP_OrthoBlendWeight
|
||||
// ResolvedView.OrthoBlendParameter 是全局混合强度
|
||||
float4 GetOrthoBlendPosition( float4 WorldPosition, float4x4 ViewProjectionMatrix, float weight )
|
||||
{
|
||||
#if USES_ORTHO_BLEND_POSITION
|
||||
float4 ScreenPosition = mul(WorldPosition, ViewProjectionMatrix);
|
||||
// 用正交投影矩阵的X行点乘世界坐标,得到正交投影下的X坐标
|
||||
float OrthoScreenPositionX = dot(ResolvedView.OrthoViewProjectionX, WorldPosition);
|
||||
|
||||
// 在透视X和正交X之间混合
|
||||
// 注意乘以 ScreenPosition.w 是为了从 NDC 空间转回裁剪空间
|
||||
ScreenPosition.x = lerp(
|
||||
ScreenPosition.x, // 透视X
|
||||
OrthoScreenPositionX * ScreenPosition.w, // 正交X(转回裁剪空间)
|
||||
weight * ResolvedView.OrthoBlendParameter ); // 混合权重
|
||||
|
||||
return ScreenPosition;
|
||||
#else
|
||||
return mul(WorldPosition, ViewProjectionMatrix);
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
### Common.ush — V2:全透视校正("Purse correction")
|
||||
|
||||
```hlsl
|
||||
// ASW Change : 2019/01/11 13:53:00 Takeshi.N
|
||||
// V2:全透视校正("Purse correction")
|
||||
// 原理:将所有顶点投影到与物体中心等距的平面上
|
||||
// 消除前后肢体因透视导致的大小差异
|
||||
float4 GetOrthoBlendPosition2( float4 WorldPosition, float4x4 ViewProjectionMatrix,
|
||||
float weight, float3 ObjWorldPosition )
|
||||
{
|
||||
#if USES_ORTHO_BLEND_POSITION2
|
||||
|
||||
#if PARTICLE_FACTORY && !PARTICLE_MESH_FACTORY
|
||||
// 非网格粒子不做透视校正,直接返回标准投影
|
||||
return mul(WorldPosition, ViewProjectionMatrix);
|
||||
#else
|
||||
// 保存原始裁剪空间位置(用于最终 lerp)
|
||||
float4 ClipSpacePositionOrigin = mul(WorldPosition, ViewProjectionMatrix);
|
||||
|
||||
// 计算顶点相对于物体中心的偏移
|
||||
float3 offset = WorldPosition.xyz - ObjWorldPosition.xyz;
|
||||
|
||||
// 物体中心到相机的距离(沿视线方向的投影距离)
|
||||
float dist = abs(dot(
|
||||
ObjWorldPosition.xyz - ResolvedView.WorldCameraOrigin.xyz,
|
||||
ResolvedView.ViewForward.xyz));
|
||||
|
||||
// 在视线方向上,距离相机 dist 处的点
|
||||
float3 origin = ResolvedView.WorldCameraOrigin.xyz
|
||||
+ ResolvedView.ViewForward.xyz * dist;
|
||||
|
||||
// 关键步骤:将顶点XY"拍平"到与物体中心等距的平面
|
||||
// 这样所有顶点在相机方向上的距离相同,消除透视缩放差异
|
||||
WorldPosition.xy = origin.xy + offset.xy;
|
||||
float4 ClipSpacePosition = mul(WorldPosition, ViewProjectionMatrix);
|
||||
|
||||
// X轴补偿:物体中心可能不在视线正前方
|
||||
// 需要补偿因"拍平"导致的X轴偏移
|
||||
float4x1 VPM_X = float4x1( ViewProjectionMatrix._11_21_31_41 );
|
||||
float x1 = mul(float4(origin, 1), VPM_X); // "拍平"参考点的X
|
||||
float x2 = mul(float4(ObjWorldPosition, 1), VPM_X); // 物体中心的X
|
||||
ClipSpacePosition.x += ClipSpacePosition.w * (x2 - x1) / dist;
|
||||
|
||||
// 根据全局参数和材质权重,在原始位置和校正位置之间混合
|
||||
// step(0.01, weight) 确保 weight 接近0时完全不应用校正
|
||||
return lerp( ClipSpacePositionOrigin, ClipSpacePosition,
|
||||
ResolvedView.OrthoBlendParameter * step(0.01, weight) );
|
||||
#endif
|
||||
|
||||
#else
|
||||
return mul(WorldPosition, ViewProjectionMatrix);
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
### BasePassVertexShader.usf — 调用逻辑
|
||||
|
||||
```hlsl
|
||||
// BasePassVertexShader.usf 中的调用逻辑
|
||||
// 按优先级选择:V2 > V1 > 原始投影
|
||||
#if USES_ORTHO_BLEND_POSITION2
|
||||
// V2 透视校正:传入物体世界坐标作为参考点
|
||||
ClipSpacePosition = GetOrthoBlendPosition2(
|
||||
RasterizedWorldPosition,
|
||||
ResolvedView.TranslatedWorldToClip,
|
||||
GetMaterialOrthoBlendWeight( VertexParameters ), // 材质图中设置的权重
|
||||
GetActorWorldPosition(VertexParameters.PrimitiveId) // 物体世界坐标
|
||||
);
|
||||
#elif USES_ORTHO_BLEND_POSITION
|
||||
// V1 简单X轴混合
|
||||
ClipSpacePosition = GetOrthoBlendPosition(
|
||||
RasterizedWorldPosition,
|
||||
ResolvedView.TranslatedWorldToClip,
|
||||
GetMaterialOrthoBlendWeight( VertexParameters )
|
||||
);
|
||||
#else
|
||||
// 原始透视投影
|
||||
ClipSpacePosition = INVARIANT(mul(RasterizedWorldPosition, ResolvedView.TranslatedWorldToClip));
|
||||
#endif
|
||||
```
|
||||
|
||||
### C++ 端实现
|
||||
|
||||
```cpp
|
||||
// SceneRendering.cpp — 正交投影混合参数计算
|
||||
// ASW Change : 2016/03/29 Takuro.K
|
||||
|
||||
// 计算正交投影矩阵的 X 行(用于 Shader 中 dot 计算正交 X 坐标)
|
||||
static FVector4 CalcOrthoBlendParameter(
|
||||
const FViewMatrices& ViewMatrices,
|
||||
const FMatrix& EffectiveTranslatedViewMatrix)
|
||||
{
|
||||
const FVector2D FOVTheta = ViewMatrices.ComputeHalfFieldOfViewPerAxis();
|
||||
float distance = ViewMatrices.GetOrthoBlendBaseDistance();
|
||||
float w = distance * FMath::Tan(FOVTheta.X);
|
||||
float h = distance * FMath::Tan(FOVTheta.Y);
|
||||
|
||||
// 构建正交投影矩阵,提取 X 行
|
||||
const FMatrix orthoMat = FOrthoMatrix(w, h, 0, 1);
|
||||
const FMatrix ViewOrthoMatrix = EffectiveTranslatedViewMatrix * orthoMat;
|
||||
return FVector4(
|
||||
ViewOrthoMatrix.M[0][0],
|
||||
ViewOrthoMatrix.M[1][0],
|
||||
ViewOrthoMatrix.M[2][0],
|
||||
ViewOrthoMatrix.M[3][0]);
|
||||
}
|
||||
|
||||
// 计算混合权重(基于相机朝向的衰减)
|
||||
static float CalcOrthoBlendWeight(
|
||||
const FViewMatrices& ViewMatrices,
|
||||
const FMatrix& EffectiveTranslatedViewMatrix)
|
||||
{
|
||||
float weight = ViewMatrices.GetOrthoBlendWeight();
|
||||
// 相机正面朝向时权重最大,侧面时衰减
|
||||
float face = ViewMatrices.GetOrthoBlendBaseRot()
|
||||
.RotateVector(FVector(1, 0, 0)).X;
|
||||
if (weight < 1.0f)
|
||||
{
|
||||
static const float THRESHOLD_MIN = 0.995f;
|
||||
static const float RANGE = 1.0f - THRESHOLD_MIN;
|
||||
face = FMath::Max((face - THRESHOLD_MIN) / RANGE, 0.0f);
|
||||
weight *= face * face; // 平方衰减
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
// SceneRendering.cpp — 写入 ViewUniformShaderParameters
|
||||
ViewUniformShaderParameters.OrthoViewProjectionX =
|
||||
CalcOrthoBlendParameter(ViewMatrices, TranslatedViewMatrix);
|
||||
ViewUniformShaderParameters.OrthoBlendParameter =
|
||||
CalcOrthoBlendWeight(ViewMatrices, TranslatedViewMatrix);
|
||||
```
|
||||
|
||||
## 代码修改情况
|
||||
|
||||
| 文件路径 | 行号 | 修改类型 | 修改内容 |
|
||||
|---------|------|---------|---------|
|
||||
| `Shaders/Private/Common.ush` | L1739~L1758 | 新增 | `GetOrthoBlendPosition` V1 X轴正交混合函数 |
|
||||
| `Shaders/Private/Common.ush` | L1761~L1800 | 新增 | `GetOrthoBlendPosition2` V2 全透视校正函数 |
|
||||
| `Shaders/Private/BasePassVertexShader.usf` | L94~L114 | 新增 | V2/V1 投影分支调用逻辑 |
|
||||
| `Shaders/Private/DepthOnlyVertexShader.usf` | L152~L220 | 新增 | 同上(DepthOnly Pass) |
|
||||
| `Shaders/Private/PositionOnlyDepthVertexShader.usf` | L49~L59 | 新增 | 同上(PositionOnlyDepth) |
|
||||
| `Shaders/Private/HitProxyVertexShader.usf` | L147~L169 | 新增 | 同上(HitProxy Pass) |
|
||||
| `Shaders/Private/DebugViewModeVertexShader.usf` | L162~L219 | 新增 | 同上(DebugViewMode Pass) |
|
||||
| `Shaders/Private/MaterialTemplate.ush` | L2235~L2240 | 新增 | `GetMaterialOrthoBlendWeight` 材质访问函数 |
|
||||
| `Source/Runtime/Renderer/Private/SceneRendering.cpp` | L1014~L1067 | 新增 | `CalcOrthoBlendParameter` / `CalcOrthoBlendWeight` 函数 |
|
||||
| `Source/Runtime/Renderer/Private/SceneRendering.cpp` | L1717~L1758 | 新增 | 写入 `ViewUniformShaderParameters` OrthoBlend 参数 |
|
||||
|
||||
Reference in New Issue
Block a user