第 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>