片段着色器渲染随机纹理出现线条伪影:排查与UV偏移可行性分析
随机纹理渲染的线条伪影问题
我在用片段着色器渲染随机纹理时出现了线条状伪影,试过修改纹理的minFilter参数但没用。想问问问题可能出在代码哪个阶段?能不能通过偏移极端UV坐标解决?
相关着色器代码:
uniform sampler2D uTexture; vec2 hash2D2D (vec2 s){ return fract(sin(mod(vec2(dot(s, vec2(127.1, 311.7)), dot(s, vec2(269.5,183.3))), 3.14159))*43758.5453); } vec4 tex2DStochastic(sampler2D tex, vec2 UV){ mat4x3 BW_vx; vec2 skewUV = mat2x2(1.0 , 0.0 , -0.57735027 , 1.15470054) * UV; vec2 vxID = vec2(floor(skewUV)); vec3 tNoise = vec3(fract(skewUV), 0); tNoise.z = 1.0 - tNoise.x - tNoise.y; if(tNoise.z > 0.0){ BW_vx = mat4x3( vec3(vxID, 0), vec3(vxID + vec2(0, 1), 0), vec3(vxID + vec2(1, 0), 0), tNoise.zyx ); }else{ BW_vx = mat4x3( vec3(vxID + vec2 (1, 1), 0), vec3(vxID + vec2 (1, 0), 0), vec3(vxID + vec2 (0, 1), 0), vec3(-tNoise.z, 1.0-tNoise.y, 1.0-tNoise.x) ); } return texture2D( tex, (UV + hash2D2D(BW_vx[0].xy)) ) * (BW_vx[3].x)+ texture2D( tex, (UV + hash2D2D(BW_vx[1].xy)) ) * (BW_vx[3].y)+ texture2D( tex, (UV + hash2D2D(BW_vx[2].xy)) ) * (BW_vx[3].z); } vec4 color = tex2DStochastic(uTexture, vUv*5.0);
输出效果:
问题定位
线条伪影核心出现在三角形网格的边界硬分支判断阶段——也就是代码里tNoise.z > 0.0的分支切换逻辑。当UV处于网格边界附近时,相邻像素可能会进入不同分支,导致采样的网格顶点ID(vxID)突变,哈希函数输出的偏移量随之跳变,最终形成明显的线条断层。修改minFilter没用,因为伪影不是纹理过滤导致的,而是采样逻辑的不连续性。
极端UV偏移的可行性
可以偏移极端UV,但这只是治标不治本的方法——它只能掩盖局部边界的问题,没法从根源解决分支切换带来的采样突变。
具体修复方案
1. 用平滑权重替代硬分支
去掉if-else的硬判断,改为对所有可能的网格顶点采样,并用平滑过渡的权重混合结果,彻底消除边界处的采样突变:
vec4 tex2DStochastic(sampler2D tex, vec2 UV){ vec2 skewUV = mat2x2(1.0 , 0.0 , -0.57735027 , 1.15470054) * UV; vec2 vxID = floor(skewUV); vec3 tNoise = vec3(fract(skewUV), 0); tNoise.z = 1.0 - tNoise.x - tNoise.y; // 计算平滑权重,将负权重置0后归一化 vec3 weights = max(tNoise, 0.0); float weightSum = dot(weights, vec3(1.0)); weights /= weightSum; // 采样四个网格顶点对应的偏移纹理 vec4 sample0 = texture2D(tex, UV + hash2D2D(vxID)); vec4 sample1 = texture2D(tex, UV + hash2D2D(vxID + vec2(0, 1))); vec4 sample2 = texture2D(tex, UV + hash2D2D(vxID + vec2(1, 0))); vec4 sample3 = texture2D(tex, UV + hash2D2D(vxID + vec2(1, 1))); // 混合四个采样结果,包含tNoise.z为负时的权重 return sample0 * weights.z + sample1 * weights.x + sample2 * weights.y + sample3 * max(-tNoise.z, 0.0) / weightSum; }
2. 优化哈希函数的连续性
原哈希函数在整数输入跳变时,输出可能存在微小不连续,可以换成更平滑的实现:
vec2 hash2D2D(vec2 s) { vec3 p = vec3(s.xy, 1.0) * vec3(127.1, 311.7, 269.5); p = fract(p * 0.015); p += dot(p, p.yzx + 19.19); return fract(vec2(p.x * p.y, p.y * p.z)); }
这个版本的哈希函数在输入变化时输出过渡更自然,能进一步减少边界处的跳变伪影。
3. 调整UV缩放时机
当前代码先缩放UV再计算斜网格,可能导致边界密度过高。可以尝试先计算斜网格再缩放,或者调整缩放倍数,避免边界过于密集放大伪影:
// 改成先计算skewUV再缩放 vec2 skewUV = mat2x2(1.0 , 0.0 , -0.57735027 , 1.15470054) * (vUv * 5.0);
内容的提问来源于stack exchange,提问作者Yuri Gusak
相关产品推荐
相关产品推荐

