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

2D游戏光照系统:GI颜色插值Upscale遮挡物伪影问题求助

2D辐射度光照系统的Upscale伪影优化问题

我正在为2D游戏开发一套基于**2D辐射度(2D radiosity)**的光照系统,实现流程:

  • 为游戏场景生成JFA Voronoi图(遮挡物为黑色、alpha=1.0;发射器为带颜色、alpha=1.0)
  • 基于该图生成有符号距离场(SDF)
  • 对屏幕每个像素执行N条光线的光线步进(raymarch),每条光线在SDF上最多走M步且每个像素带随机角度偏移,在光线终点采样发射器/遮挡物表面,再退回空白区域采样最近空白处的发射光

这套方案最终效果良好,但存在效率问题。

为优化效率,我采用先以1/N分辨率渲染全局光照(GI),再通过加权颜色插值进行Upscale的方案。该方案在空白区域效果符合预期,但在遮挡物和发射器附近出现严重伪影。我尝试在主函数循环中对发射器/遮挡物(场景纹理中的源像素)颜色进行权重剔除,但并未解决问题。

目前发现权重函数会产生权重为0的颜色,但暂无从插值过程中完全移除这类颜色的解决方案。

完整Upscale着色器代码

/*
    UPSCALING SHADER:
        Find the nearest 4 boundign samples to the current pixel (xyDelta & xyShift)
        Calculate all of the sample's weights based on whether they're marchable or source pixels.
        Final perform a composite weighted interpolation for the current pixel to the nearest 4 samples.
*/
varying vec2 in_Coord;
uniform float in_Sample;
uniform vec2 in_Screen;
uniform sampler2D in_GIField;
uniform sampler2D in_SDField;

#define TPI 9.4247779607693797153879301498385
#define PI  3.1415926535897932384626433832795
#define TAU 6.2831853071795864769252867665590
#define EPSILON         0.001 // floating point precision check
#define dot(f)       dot(f,f) // shorthand dot of a single float

float ATAN2(float yy, float xx) { return mod(atan(yy, xx), TAU); }
float DIRECT(vec2 v1, vec2 v2) { vec2 v3 = v2 - v1; return ATAN2(-v3.y, v3.x); }
float DIFFERENCE(float src, float dst) { return mod(dst - src + TPI, TAU) - PI; }

float V2_F16(vec2 v) { return v.x + (v.y / 255.0); }
float VMAX(vec3 v) { return max(v.r, max(v.g, v.b)); }
vec2 SAMPLEXY(vec2 xycoord) { return (floor(xycoord / in_Sample) * in_Sample) + (in_Sample*0.5); }
vec3 TONEMAP(vec3 color, float dist) { return color * (1.0 / (1.0 + dot(dist / min(in_Screen.x, in_Screen.y)))); }

float TESTMARCH(vec2 pix, vec2 end) {
    float aspect = in_Screen.x / in_Screen.y,
        dst = distance(pix, end);
    vec2 dir = normalize((end*in_Screen) - (pix*in_Screen)) / in_Screen;
    
    for(float i = 0.0; i < in_Sample; i += 1.0) {
        vec2 test = vec2(pix.x * aspect, pix.y) + (dir * (i/in_Screen));
        test.x /= aspect;
        
        vec4 sourceCol = texture2D(gm_BaseTexture, test);
        float source = max(sourceCol.r, max(sourceCol.g, sourceCol.b));
        if (source < EPSILON && sourceCol.a > 0.0) return 0.0;
    }
    
    return 1.0;
}

vec3 WCOMPOSITE(vec3 colors[4], float weights[4], vec2 uv) {
    // (uv * A * B) + (B * (1.0 - A)) //0, 2, 1, 3
    float weightA = (uv.y * weights[0] * weights[2]) + (weights[2] * (1.0 - weights[0])),
        weightB = (uv.y * weights[1] * weights[3]) + (weights[3] * (1.0 - weights[1]));
    vec3 colorA = mix(colors[0], colors[2], weightA),
        colorB = mix(colors[1], colors[3], weightB);
    return mix(colorA, colorB, uv.x);
}

void main() {
    vec2 xyCoord = in_Coord * in_Screen;
    vec2 xyLight = SAMPLEXY(xyCoord);
    vec2 xyDelta = sign(sign(xyCoord - xyLight) - 1.0);
    
    vec2 xyShift[4];
    xyShift[0] = vec2(0.,0.) + xyDelta;
    xyShift[1] = vec2(1.,0.) + xyDelta;
    xyShift[2] = vec2(0.,1.) + xyDelta;
    xyShift[3] = vec2(1.,1.) + xyDelta;
    
    vec2 xyField[4]; vec3 xyColor[4]; float notSource[4]; float xyWghts[4];
    for(int i = 0; i < 4; i++) {
        xyField[i] = (xyLight + (xyShift[i] * in_Sample)) * (1.0/in_Screen);
        xyColor[i] = texture2D(in_GIField, xyField[i]).rgb;
        notSource[i] = 1.0 - sign(texture2D(gm_BaseTexture, xyField[i]).a);
        xyWghts[i] = TESTMARCH(in_Coord, xyField[i]) * sign(VMAX(xyColor[i])) * notSource[i];
    }
    
    vec2 uvCoord = mod(xyCoord-xyLight, in_Sample) * (1.0/in_Sample);
    vec3 xyFinal = WCOMPOSITE(xyColor, xyWghts, uvCoord);
    
    vec4 xySource = texture2D(gm_BaseTexture, in_Coord);
    float isSource = sign(xySource.a);
    gl_FragColor = vec4((isSource * xySource.rgb) + ((1.0-isSource) * xyFinal), 1.0);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:45:30