跳到主要内容
← Image Manipulation with Shaders

Image Manipulation with Shaders

Toon

本题核心在于实现颜色通道的量化,每通道压缩为 5 个等级,通过 `floor(color * 5.0 + 0.5) / 5.0` 实现“就近取整”。同时,利用 radius 控制邻域采样范围,从而影响边缘检测结果。虽然 Sobel 算子来自上一课,但在本题中结合量化效果,提升了 toon 风格的清晰度和表现力。我的实现

实时渲染 · 纹理 GLSL

第 12 课:Toon

我的解:本题核心在于实现颜色通道的量化,每通道压缩为 5 个等级,通过 floor(color * 5.0 + 0.5) / 5.0 实现“就近取整”。同时,利用 radius 控制邻域采样范围,从而影响边缘检测结果。虽然 Sobel 算子来自上一课,但在本题中结合量化效果,提升了 toon 风格的清晰度和表现力。我的实现更通用,适合扩展;别人的实现更简洁,执行效率更高。

我的解

uniform vec2 iResolution;
uniform sampler2D iChannel0;
uniform vec2 iChannelSize0;

vec3 w = vec3(0.2125, 0.7154, 0.0721);
float radius = 2.0;
float Luminance(vec3 tex){
  return dot(tex, w);
}
float applyKernel(mat3 kernel, mat3 region){
  float sum = 0.0;
  for(int i = 0; i<3; i++){
    for(int j = 0; j<3; j++){
      sum += kernel[j][i] * region[j][i];
    }
  }
  return sum;
}
void main() {
  vec2 texCoord = gl_FragCoord.xy / iResolution.xy;
  vec2 texelSize = 1.0 / iChannelSize0 * radius;
  mat3 region;
  for(int i = 0; i<3; i++){
    for(int j = 0; j<3; j++){
      vec2 offset = texelSize*vec2((j-1),(i-1));
      region[j][i] =
        Luminance(texture(iChannel0,texCoord + offset).rgb);
    }
  }

  mat3 sobelX = mat3(
    -1, -2, -1,  // column 0
     0,  0,  0,  // column 1
     1,  2,  1   // column 2
  );

  mat3 sobelY = mat3(
    -1,  0,  1,
    -2,  0,  2,
    -1,  0,  1
  );

 float gx = applyKernel(sobelX,region);
 float gy = applyKernel(sobelY,region);
 float M = sqrt(gx*gx + gy*gy);

 vec4 tex = texture(iChannel0,texCoord);
 float black = 1.0 - step(0.5, M);
 vec4 quantizeTex = floor(tex * 5.0+0.5)/5.0;
 gl_FragColor = vec4(mix(vec3(0.0),quantizeTex.rgb,  black),1.0);
}

复盘笔记

  • 本题核心在于实现颜色通道的量化,每通道压缩为 5 个等级,通过 floor(color * 5.0 + 0.5) / 5.0 实现“就近取整”。同时,利用 radius 控制邻域采样范围,从而影响边缘检测结果。虽然 Sobel 算子来自上一课,但在本题中结合量化效果,提升了 toon 风格的清晰度和表现力。我的实现更通用,适合扩展;别人的实现更简洁,执行效率更高。
  • <2025/05/31>