You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Metal着色语言光线追踪着色器的垂直拉伸与水平移动问题

问题分析与修复方案

问题根源

  • 垂直拉伸:未考虑屏幕宽高比,NDC坐标直接转换时,y轴比例未适配屏幕分辨率,导致射线方向在相机空间的垂直维度比例错误。
  • 水平移动速度异常:硬编码射线深度值(如-1、-3),未结合传入的fov参数计算透视投影的焦距,导致射线水平覆盖范围与相机实际视场角不匹配,相机移动时场景相对速度偏离预期。

修复步骤与代码修改

核心修复逻辑

  1. 校正宽高比:对NDC坐标的x分量乘以宽高比,确保相机空间中射线的水平/垂直比例与屏幕一致,消除拉伸。
  2. 基于FOV计算焦距:用视场角推导相机空间的射线深度,替代硬编码值,匹配相机透视参数,修复移动速度问题。

修改后的代码片段

fragment half4 shader_fragment(Vertex in [[stage_in]],
                               constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                               constant NodeBuffer& scn_node [[buffer(1)]],
                               constant Resolution& resolution [[buffer(3)]],
                               constant float& fov [[buffer(2)]]) {
    float time = scn_frame.time;
    
    float2 resolutionVector = float2(resolution.width, resolution.height);
    // 计算屏幕宽高比
    float aspectRatio = resolutionVector.x / resolutionVector.y;
    
    float2 uv = float2(in.position.x, resolutionVector.y - in.position.y) / resolutionVector.xy;
    float2 ndc = uv * 2.0 - 1.0;
    
    // 校正NDC的宽高比,避免垂直拉伸
    ndc.x *= aspectRatio;
    
    // 将FOV转换为弧度,计算透视焦距
    float fovRadians = fov * M_PI / 180.0;
    float focalLength = 1.0 / tan(fovRadians * 0.5);
    // 相机空间z轴指向后方,所以射线z分量为负的焦距
    float3 rayDirCameraSpace = normalize(float3(ndc.x, ndc.y, -focalLength));
    
    // 射线转换到世界空间(原逻辑保留)
    float4x4 invViewMatrix = scn_frame.inverseViewTransform;
    float3 rayDirWorldSpace = (invViewMatrix * float4(rayDirCameraSpace, 0.0)).xyz;
    float3 rayOrigin = (invViewMatrix * float4(0, 0, 0, 1)).xyz;
    
    // 后续着色器代码...
}

修复说明

  • 宽高比校正:通过ndc.x *= aspectRatio,让射线在相机空间的水平范围适配屏幕宽度,保证不同分辨率下场景比例正常,解决垂直拉伸问题。
  • FOV焦距计算:透视投影的焦距由视场角决定,1/tan(fov/2)是标准透视相机的焦距公式,用这个值替代硬编码的深度,确保射线的水平覆盖范围与相机视场角匹配,修复移动时的速度异常,同时避免硬改深度带来的场景畸变。

内容的提问来源于stack exchange,提问作者Arthur TL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 23:26:18