• 用 Three.js 实现手绘涂鸦风格游戏


    原理:场景先渲进"数据缓冲",再用全屏后处理把每个像素"画"成圆珠笔/铅笔笔触。

    github:https://github.com/zsj23333/doodleBreak
    在线体验:https://zsj23333.github.io/doodleBreak/

    一、两步管线

    pass 1 数据缓冲                 pass 2 全屏后处理
    场景 ──► RGBA半浮点(r=明暗 g=墨色ID ba=法线xy) ──► 每像素画成: 轮廓+阴影线+纸纹
            + Float深度纹理
    
    • 数据缓冲:自定义 ShaderMaterial,只存"作画所需信息",无任何风格化。
    • 后处理:全屏 quad(独立场景 + 正交相机),逐像素决定画成纸色、墨线还是轮廓。
    • 好处:风格化与场景内容解耦,任意模型/相机运动都有一致手绘感。

    二、数据缓冲 pass

    • 片元输出 vec4(shade, inkId, n.x, n.y),shade = dot(n, L)*0.5+0.5 半球光;fill 模式输出 shade=-1(后处理整片涂纯色)。
    • 深度纹理必须 FloatType;RT 用 HalfFloatType + NearestFilter、无 mipmap。
    • 同色物体共享材质实例,减少 draw call。

    三、后处理五层笔触

    1. 手抖采样:值噪声偏移采样 UV,整幅笔触同步微抖。

    2. 轮廓 = 逆深度 Laplacian(关键):

    任意平面 ax+by+cz=d 的 1/z 在屏幕空间是仿射函数 → 二阶差分恒 0。平面内部无响应,只有穿过真实深度不连续(轮廓)时非零。除以 1/d 归一化,任意距离轮廓强度一致。

    float lap = abs(1.0/linDepth(zl) + 1.0/linDepth(zr) - 2.0*iw)
              + abs(1.0/linDepth(zu) + 1.0/linDepth(zd) - 2.0*iw);
    float edge = smoothstep(0.07, 0.30, lap / (iw + 1e-7));   // 尺度不变
    

    法线突变也补一条轮廓(贴在一起的物体间);轮廓颜色取最近深度物体的墨色。

    3. 阴影线 = 表面锚定 + LOD + 三方向排线:

    • 从深度重建世界坐标和法线 → 条纹"钉"在表面上,随物体转动不漂移。
    • 法线最朝外的轴决定投影面,避免条纹拉伸。
    • 间距按 sp = 0.5 * exp2(floor(log2(0.016*d/0.5))) 做 2 的幂分级,远处自动变粗防摩尔纹。
    • 三方向(45°/-45°/15°)叠加,密度随 shade 递减:smoothstep(0.64→0.5)、(0.42→0.32)、(0.24→0.14),最暗处涂黑。
    float stripes(vec2 p, vec2 dir, float spacing, float width) {
      float t = dot(p, vec2(-dir.y, dir.x));
      float f = abs(fract(t / spacing) - 0.5) * spacing;
      float soft = width * 0.6;
      return 1.0 - smoothstep(width*0.5 - soft, width*0.5 + soft, f);
    }
    

    4. 纸面:双层值噪声(低频大块+高频细颗粒)做纸纹;横线 = 屏幕 y 取模,间距 画布高/13.5,淡蓝 (0.58,0.70,0.92)。红边距可选(不需要就删)。

    5. 合成:paper → mix(墨色, hatch*0.72) → mix(轮廓墨色*0.92, edge*ew),ew = 0.75+0.35*vnoise 让轮廓线毛糙一点。

    渲染器:render(scene) 到 RT → render(postScene, ortho) 到屏幕。后处理 quad 必须单独场景,否则主场景被重复渲染。

    四、调参速查表

    参数 作用 调大效果
    wob*2.0*sc*px 手抖幅度 更"手抖"
    o=1.15*sc 轮廓采样半径 线更粗
    smoothstep(0.07,0.30,…) 轮廓灵敏度 更易勾边
    0.016(LOD 基准) 阴影线间距 线更稀
    w=sp*0.17 笔宽 排线更粗
    0.64/0.42/0.24 三方向明暗阈值 更早出现更多方向线
    hatch*0.72 墨色浓度 更黑
    grain / rule*0.5 纸纹强度 / 横线透明度 纸更糙 / 线更明显
    uPaper / uLineSpacing 纸色 / 横线间距 改纸色 / 改密度

    五、常见坑

    1. r147 默认 Linear 输出,示例色是手调亮的;换深色系需自行 pow(col, 1/2.2) 或开 sRGBEncoding。
    2. r152+ 升级注意 texture2D → texture 与 outputEncoding 改名。
    3. 深度纹理必须 FloatType,否则逆深度轮廓出噪点。
    4. RT 用 NearestFilter 无 mipmap;深度与颜色采样必须用同一 wob 偏移,否则轮廓错位。
    5. 空场景也要渲一帧,否则黑屏。
  • 相关阅读:
    Android源码——Configuration源码解析
    想要将如下代码进行一次渲染(上面有一层遍历了) 有什么方法吗
    MongoDB基础之数据库,集合,文档
    三层神经网络的训练模型,深度神经网络如何训练
    Ant Design Charts 行政区地图(ChoroplethMap)地图不显示的奇怪问题
    Linux下快速确定目标服务器支持哪些协议和密码套件
    Linux抓包工具tcpdump
    操作系统实验三 进程间通信
    VUE路由传参的实用方式
    【Qt】绘图与绘图设备
  • 原文地址:https://www.cnblogs.com/zhuoshengjie/p/22906621