three.js MirrorShader 详解:用一行像素镜像实现画面水平/垂直翻转变换
2026/9/10 2:21:37 网站建设 项目流程

three.js MirrorShader 详解:用一行像素镜像实现画面水平/垂直翻转变换

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

本篇文章基于 three.js 官方文档的 MirrorShader 页面(docs/pages/module-MirrorShader.html.md),结合仓库内的 着色器源码 与后处理管线源码,系统讲解如何用MirrorShader将画面的一半镜像复制到另一半,生成左右对称或上下对称的视觉效果。读完本文,你将掌握该 addon 的导入方式、side参数的四种语义、GLSL 实现原理,以及如何在EffectComposer后处理链中通过ShaderPass直接落地使用。

MirrorShader 是什么

MirrorShader是 three.js 官方提供的一个内置后处理着色器对象(addon),其核心功能正如文档所述:将输入的图像(通常是一整帧渲染结果)按指定方向做半幅镜像复制——把画面的一侧(左/右/上/下)作为源,经镜像翻转后覆盖另一侧,最终得到一幅"左右对称"或"上下对称"的输出。

它属于ShaderMaterial~Shader类型的常量对象,本身不携带任何 scene / mesh 逻辑,只描述一份可直接喂给ShaderMaterial的着色器定义(uniforms + vertexShader + fragmentShader)。因此最常见的用法是挂载到后处理链路上,对整帧颜色缓冲做全屏后处理。

导入方式:addon 必须显式引入

与 three.js 核心库不同,MirrorShader位于examples/jsm目录,属于非默认导出的官方附加模块,需要显式导入:

import { MirrorShader } from 'three/addons/shaders/MirrorShader.js';

之所以能通过three/addons/...这一路径访问,是因为仓库中 examples/jsm/Addons.js 集中重新导出了所有附加模块:

export * from './shaders/MirrorShader.js';

因此只要构建/打包环境正确解析three/addons别名,即可与其它 addon(如ShaderPassEffectComposer)一起引用。在仓库自带的示例 HTML 中(例如 webgl_postprocessing.html),这一别名通过 importmap 映射到 addons 目录实现,效果等价。

模块成员:.MirrorShader(inner, constant)

模块对外暴露的成员只有一个:

.MirrorShader : ShaderMaterial~Shader(内部常量)

一个类型为ShaderMaterial~Shader的着色器定义对象。所谓 inner/constant 指它是该模块私有的、只读的常量结构,包含三部分:

  • name'MirrorShader'
  • uniformstDiffuse(采样输入贴图)与side(镜像方向)
  • 着色器代码:vertexShaderfragmentShader

实际源码见 examples/jsm/shaders/MirrorShader.js,其完整结构如下:

const MirrorShader = { name: 'MirrorShader', uniforms: { 'tDiffuse': { value: null }, 'side': { value: 1 } }, vertexShader: /* glsl */` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, fragmentShader: /* glsl */` uniform sampler2D tDiffuse; uniform int side; varying vec2 vUv; void main() { vec2 p = vUv; if (side == 0){ if (p.x > 0.5) p.x = 1.0 - p.x; }else if (side == 1){ if (p.x < 0.5) p.x = 1.0 - p.x; }else if (side == 2){ if (p.y < 0.5) p.y = 1.0 - p.y; }else if (side == 3){ if (p.y > 0.5) p.y = 1.0 - p.y; } vec4 color = texture2D(tDiffuse, p); gl_FragColor = color; }` };

uniforms 参数详解

源码定义了两个 uniform:

uniform类型默认值说明
tDiffusesampler2Dnull输入颜色贴图。用于后处理时通常指向上一个 pass 的readBuffer纹理,由ShaderPass自动注入
sideint1指定"取哪一半作为镜像源",取值范围03,对应左/右/上/下

注意side在 GLSL 中声明为uniform int,因此设置时务必赋整数:

material.uniforms.side.value = 0; // 而不是 0.0

side 语义速查表

结合文档说明与 fragmentShader 判断分支,side四种取值的语义如下:

side含义实际采样逻辑效果
0leftp.x > 0.5时令p.x = 1.0 - p.x左侧半图,水平镜像后复制到右侧半区,画面水平对称
1right(默认)p.x < 0.5时令p.x = 1.0 - p.x右侧半图,水平镜像后复制到左侧半区
2topp.y < 0.5时令p.y = 1.0 - p.y上方半图,垂直镜像后复制到下方半区,画面垂直对称
3bottomp.y > 0.5时令p.y = 1.0 - p.y下方半图,垂直镜像后复制到上方半区

一句话记忆:哪个 side 代表取哪一半的源数据;被保留的另一半是原样输出,源半幅则被翻转后覆盖到对侧。这个"复制一半到另一半"的行为正是文档中对.MirrorShader的那句描述——Copies half the input to the other half

着色器实现原理逐行解析

顶点点着色器非常简单,仅仅把模型顶点的uv原样传递给片元:

varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }

片元着色器才是"镜像"发生的核心位置,其思路是:先改写采样坐标,再做一次普通贴图采样

uniform sampler2D tDiffuse; uniform int side; varying vec2 vUv; void main() { vec2 p = vUv; if (side == 0){ if (p.x > 0.5) p.x = 1.0 - p.x; // 右半区采样点映射到左半区 }else if (side == 1){ if (p.x < 0.5) p.x = 1.0 - p.x; // 左半区采样点映射到右半区 }else if (side == 2){ if (p.y < 0.5) p.y = 1.0 - p.y; // 下半区采样点映射到上半区 }else if (side == 3){ if (p.y > 0.5) p.y = 1.0 - p.y; // 上半区采样点映射到下半区 } vec4 color = texture2D(tDiffuse, p); gl_FragColor = color; }

关键点在于坐标变换p = 1.0 - p:UV 中01互为镜像位置。以side = 0为例,对于右半区(p.x > 0.5)的任意像素,其采样横坐标会被换算为1.0 - p.x(落在0 ~ 0.5),等价于以垂直中线为轴水平翻转后采样——于是右半屏展示的是左半屏的镜像拷贝,画面整体呈左右对称。side = 1/2/3只是把同样的几何思想换到另一侧或 Y 轴方向。

由于它采样自tDiffuse,本质上是一个逐像素的"坐标重映射"型全屏效果,不引入额外的几何体、相机或渲染目标,成本极低。

实战:接入 EffectComposer 后处理链

MirrorShader最常见的正确打开方式是通过ShaderPass加入后处理管线。这里给出一个可直接套用的完整示例(参照仓库 webgl_postprocessing.html 的组织方式,把其中的RGBShiftShader换成MirrorShader即可):

import * as THREE from 'three'; import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; import { MirrorShader } from 'three/addons/shaders/MirrorShader.js'; // ...场景与渲染器初始化省略,例如: // const renderer = new THREE.WebGLRenderer(); // const scene = ..., camera = ..., composer = new EffectComposer( renderer ); // 1. 先渲染主场景到 composer composer.addPass( new RenderPass( scene, camera ) ); // 2. 追加 MirrorShader 后处理 pass(默认 textID = 'tDiffuse',与着色器 uniform 匹配) const mirrorPass = new ShaderPass( MirrorShader ); // 3. 动态切换镜像方向:0/1/2/3 mirrorPass.uniforms.side.value = 0; // 左侧镜像复制到右侧 composer.addPass( mirrorPass ); // 4. 末尾接 OutputPass 完成色调空间与颜色编码输出 composer.addPass( new OutputPass() ); // ...render 循环中调用 composer.render() 而非 renderer.render()

为什么能直接用?

ShaderPass 的构造函数会把传入的 shader 对象复制 uniforms 并包装为ShaderMaterial;其构造参数textureID默认值为'tDiffuse'(见 examples/jsm/postprocessing/ShaderPass.js)。随后在每次render()时自动执行:

this.uniforms[ this.textureID ].value = readBuffer.texture;

把上一 pass 的渲染结果写入tDiffuse,从而满足MirrorShader的输入需求。这就是为什么着色器对象上必须声明名为tDiffusesampler2Duniform。若你的目标纹理不同,也可改用它法:直接实例化ShaderMaterial并手动管理tDiffuse,再自行渲染一个全屏四边形,MirrorShader的对象结构(uniforms + 两个 shader 字符串)完全兼容ShaderMaterial的常规构造方式。

注意区分:与 objects/Water.js 中同名 shader 不是一回事

在仓库中搜索MirrorShader还会命中 examples/jsm/objects/Water.js,其中同样出现了一个name: 'MirrorShader'的内部 shader 对象。但这只是名字巧合,二者完全无关

  • examples/jsm/shaders/MirrorShader.js 是本文讲解的独立后处理工具,只含tDiffuse/side两个 uniform,用于生成对称画面;
  • Water.js 中的同名 shader 服务于水面物体,其 uniforms 包括mirrorSamplernormalSamplertextureMatrixtime等一整套水面反射/折射参数,属于Water类的内部实现细节,并不会从MirrorShader.js模块导入。

阅读代码或搜索 API 时留意这一命名重合,可避免误判调用链。

小结与扩展方向

围绕文档中的一句话——Copies half the input to the other half——本文把MirrorShader的能力边界讲透了:这是一个用于后处理阶段、对整帧或任意tDiffuse纹理做四方向半幅镜像复制的轻量着色器对象。其side参数(0=左、1=右、2=上、3=下,默认 1)直接决定源半幅与镜像方向,实现上等价于对 UV 坐标的简单映射,性能开销可忽略。

基于它你可以继续扩展:

  • 叠加时间变量或噪声 uniform,让对称的接缝处产生"万花筒"式动态扰动;
  • tDiffuse从帧缓冲替换为任意纹理贴图,实现 UV 对称平铺的采样工具;
  • 参照仓库内其它 addon 的写法(例如 CopyShader,同样仅含tDiffuse),把它改写为带自定义 mixing 权重的对称混合效果。

更多示例与 API 组织可参考 Addons.js、ShaderPass 以及仓库 examples 目录下的后处理演示页,结合 源码 自行调试side的四种取值即可直观看到对称效果的变化。

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询