如何通过Shader实现已有纹理的曲线抗锯齿?
如何用Shader平滑纹理中的锯齿曲线?
我有大量同尺寸的预生成图像,这些图像包含曲线和彩色填充,但曲线存在锯齿问题。现询问:如何通过Shader让给定纹理中的所有曲线更平滑?我是Shader新手,暂未找到解决方案,当前通过p5.js将图片(纹理)导入Shader,相关代码如下:
let img, theShader, pg; function preload() { img = loadImage('texture.jpg'); } function setup() { createCanvas(600,800); pixelDensity(1); pg = createGraphics(600, 800); pg.pixelDensity(1); theShader = pg.createShader(vert, frag); pg.texture(img); } function draw() { pg.shader(theShader); theShader.setUniform('u_resolution', [pg.width, pg.height]); theShader.setUniform('u_texture', pg); pg.rectMode(CENTER); pg.noStroke(); pg.rect(0, 0, pg.width, pg.height); image(pg, 0,0, width, height); }
针对新手的Shader平滑方案
核心思路是识别曲线的边缘区域,对边缘做柔和的模糊过渡,避免硬边产生锯齿。以下是可直接复用的完整实现:
1. 顶点着色器(固定模板,无需改动)
负责传递纹理坐标给片段着色器:
precision mediump float; attribute vec3 aPosition; attribute vec2 aTexCoord; varying vec2 vTexCoord; void main() { vTexCoord = aTexCoord; vec4 positionVec4 = vec4(aPosition, 1.0); positionVec4.xy = positionVec4.xy * 2.0 - 1.0; gl_Position = positionVec4; }
2. 实现平滑效果的片段着色器
通过边缘检测+区域模糊混合,实现曲线平滑:
precision mediump float; uniform sampler2D u_texture; uniform vec2 u_resolution; varying vec2 vTexCoord; // 检测图像边缘(基于像素灰度差异) float detectEdge(vec2 uv) { vec2 pixelStep = 1.0 / u_resolution; vec4 centerColor = texture2D(u_texture, uv); float centerGray = (centerColor.r + centerColor.g + centerColor.b) / 3.0; float edgeSum = 0.0; // 检测上下左右四个方向的灰度差异 edgeSum += abs(centerGray - (texture2D(u_texture, uv + vec2(pixelStep.x, 0.0)).r + texture2D(u_texture, uv + vec2(pixelStep.x, 0.0)).g + texture2D(u_texture, uv + vec2(pixelStep.x, 0.0)).b)/3.0); edgeSum += abs(centerGray - (texture2D(u_texture, uv - vec2(pixelStep.x, 0.0)).r + texture2D(u_texture, uv - vec2(pixelStep.x, 0.0)).g + texture2D(u_texture, uv - vec2(pixelStep.x, 0.0)).b)/3.0); edgeSum += abs(centerGray - (texture2D(u_texture, uv + vec2(0.0, pixelStep.y)).r + texture2D(u_texture, uv + vec2(0.0, pixelStep.y)).g + texture2D(u_texture, uv + vec2(0.0, pixelStep.y)).b)/3.0); edgeSum += abs(centerGray - (texture2D(u_texture, uv - vec2(0.0, pixelStep.y)).r + texture2D(u_texture, uv - vec2(0.0, pixelStep.y)).g + texture2D(u_texture, uv - vec2(0.0, pixelStep.y)).b)/3.0); return clamp(edgeSum, 0.0, 1.0); } void main() { vec2 uv = vTexCoord; vec4 originalColor = texture2D(u_texture, uv); // 获取当前像素的边缘强度 float edgeStrength = detectEdge(uv); // 对边缘区域进行多采样模糊 vec2 pixelStep = 1.0 / u_resolution; vec4 blurredColor = vec4(0.0); int sampleRange = 3; // 采样范围,值越大模糊越明显,性能消耗也越高 float sampleCount = 0.0; for(int x = -sampleRange; x <= sampleRange; x++) { for(int y = -sampleRange; y <= sampleRange; y++) { blurredColor += texture2D(u_texture, uv + vec2(x * pixelStep.x, y * pixelStep.y)); sampleCount += 1.0; } } blurredColor /= sampleCount; // 混合原图和模糊图,仅在边缘区域应用模糊 float blurIntensity = edgeStrength * 0.7; // 调整这个值控制模糊强度,0-1之间 vec4 finalColor = mix(originalColor, blurredColor, blurIntensity); gl_FragColor = finalColor; }
3. 修正后的p5.js代码
把着色器代码整合进去,同时修复原代码中的错误(比如传递纹理时应该用原图像而非pg):
let img, theShader, pg; // 顶点着色器代码 const vertShader = ` precision mediump float; attribute vec3 aPosition; attribute vec2 aTexCoord; varying vec2 vTexCoord; void main() { vTexCoord = aTexCoord; vec4 positionVec4 = vec4(aPosition, 1.0); positionVec4.xy = positionVec4.xy * 2.0 - 1.0; gl_Position = positionVec4; } `; // 片段着色器代码 const fragShader = ` precision mediump float; uniform sampler2D u_texture; uniform vec2 u_resolution; varying vec2 vTexCoord; float detectEdge(vec2 uv) { vec2 pixelStep = 1.0 / u_resolution; vec4 centerColor = texture2D(u_texture, uv); float centerGray = (centerColor.r + centerColor.g + centerColor.b) / 3.0; float edgeSum = 0.0; edgeSum += abs(centerGray - (texture2D(u_texture, uv + vec2(pixelStep.x, 0.0)).r + texture2D(u_texture, uv + vec2(pixelStep.x, 0.0)).g + texture2D(u_texture, uv + vec2(pixelStep.x, 0.0)).b)/3.0); edgeSum += abs(centerGray - (texture2D(u_texture, uv - vec2(pixelStep.x, 0.0)).r + texture2D(u_texture, uv - vec2(pixelStep.x, 0.0)).g + texture2D(u_texture, uv - vec2(pixelStep.x, 0.0)).b)/3.0); edgeSum += abs(centerGray - (texture2D(u_texture, uv + vec2(0.0, pixelStep.y)).r + texture2D(u_texture, uv + vec2(0.0, pixelStep.y)).g + texture2D(u_texture, uv + vec2(0.0, pixelStep.y)).b)/3.0); edgeSum += abs(centerGray - (texture2D(u_texture, uv - vec2(0.0, pixelStep.y)).r + texture2D(u_texture, uv - vec2(0.0, pixelStep.y)).g + texture2D(u_texture, uv - vec2(0.0, pixelStep.y)).b)/3.0); return clamp(edgeSum, 0.0, 1.0); } void main() { vec2 uv = vTexCoord; vec4 originalColor = texture2D(u_texture, uv); float edgeStrength = detectEdge(uv); vec2 pixelStep = 1.0 / u_resolution; vec4 blurredColor = vec4(0.0); int sampleRange = 3; float sampleCount = 0.0; for(int x = -sampleRange; x <= sampleRange; x++) { for(int y = -sampleRange; y <= sampleRange; y++) { blurredColor += texture2D(u_texture, uv + vec2(x * pixelStep.x, y * pixelStep.y)); sampleCount += 1.0; } } blurredColor /= sampleCount; float blurIntensity = edgeStrength * 0.7; vec4 finalColor = mix(originalColor, blurredColor, blurIntensity); gl_FragColor = finalColor; } `; function preload() { img = loadImage('texture.jpg'); } function setup() { createCanvas(600, 800); pixelDensity(1); pg = createGraphics(600, 800); pg.pixelDensity(1); theShader = pg.createShader(vertShader, fragShader); } function draw() { pg.shader(theShader); // 传递 uniforms theShader.setUniform('u_resolution', [pg.width, pg.height]); theShader.setUniform('u_texture', img); // 直接传入原图像 // 绘制全屏矩形,触发Shader渲染 pg.rectMode(CENTER); pg.noStroke(); pg.rect(0, 0, pg.width, pg.height); // 将渲染结果绘制到画布 image(pg, 0, 0, width, height); noLoop(); // 静态图像只需渲染一次,提升性能 }
4. 参数调整技巧
sampleRange:控制模糊的采样范围,3是平衡效果和性能的起点,5会更模糊但渲染变慢。blurIntensity中的0.7:数值越大边缘模糊越强,可根据你的图像锯齿程度调整(比如0.5-0.9之间)。- 如果彩色曲线的边缘检测不准,可以修改
detectEdge函数,用颜色的欧氏距离代替灰度差异,比如计算distance(centerColor, neighborColor)。
内容的提问来源于stack exchange,提问作者Taras
相关产品推荐
相关产品推荐

