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

窗口调整大小时2D光照出现拉伸,如何基于宽高比修正光照半径?

2D光照拉伸问题修复方案

核心原因

你当前计算光照距离的坐标处于**归一化设备坐标(NDC)**空间,该空间下x、y轴的取值范围均为[-1,1],但实际窗口宽高比不为1:1时,两个轴相同单位对应的实际像素长度不同,直接计算欧氏距离就会出现拉伸/挤压变形。

修复方案

方法1:着色器端校正距离计算(推荐,改动最小)

在计算距离前,将坐标差值按宽高比校正到等比例空间后再计算长度,修改calculateLight函数即可:

vec4 calculateLight(Light light)
{
    // 先计算片段到光源的轴向差值
    vec2 fragToLight = fragmentPosition.xy - light.position.xy;
    // 按宽高比校正x轴差值,也可选择y轴乘宽高比,效果一致
    float ar = resolution.x / resolution.y;
    fragToLight.x /= ar;
    // 校正后再计算距离
    float lightDistance = length(fragToLight);

    if (lightDistance >= light.radius)
    {
        return vec4(0, 0, 0, 1);
    }
    return light.intensity * (1 - lightDistance / light.radius) * light.colour;
}

方法2:CPU端预处理光照半径

如果你不想在着色器每次计算宽高比,也可以在CPU端将光照半径按NDC空间的比例换算后再传给着色器,适合固定宽高比的场景。

代码优化建议

  1. 修正DrawLight调用位置:你当前把Renderer2D::DrawLight放在遍历实体的for循环内部,会导致每收集一个光源就触发一次绘制,严重浪费性能。请将该行代码移到for循环外部,等所有光源收集完成后再调用。
  2. 修正环境光叠加逻辑:你现在把u_ambientColour的加法放在循环内部,每遍历一盏灯就叠加一次环境光,会导致环境光亮度随光源数量变化。请将环境光的加法挪到循环外面,只叠加一次:
void main()
{   
    vec4 texColor = v_Color;
    if (numLights == 0) {
        color = texColor * (vec4(1.0) + u_ambientColour);
        return;
    }
    vec4 netLightColour = vec4(0, 0, 0, 1);
    for(int i = 0; i < numLights; ++i) {
        netLightColour += calculateLight(allLights[i]);
    }
    // 环境光仅叠加一次
    netLightColour += u_ambientColour;
    color = texColor * netLightColour;
}
  1. 半径预转换:如果你的光照半径是世界空间单位,建议在CPU端直接将其转换为NDC空间的对应值再传给着色器,避免着色器端额外换算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 01:39:01