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
相关产品推荐
相关产品推荐

