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

如何通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:50:23