import EventSubscriber from "@lijuhong1981/jsevents/src/EventSubscriber.js";
import { Camera, Color, DoubleSide, NoBlending, Object3D, Scene, ShaderMaterial, WebGLRenderer, WebGLRenderTarget } from "three";
import { FullScreenQuad, Pass } from "three/addons/postprocessing/Pass.js";
import { CopyShader } from "three/addons/shaders/CopyShader.js";
/**
* 线框渲染后处理通道
* @extends Pass
*/
class WireframePass extends Pass {
/**
* 构造函数
* @param {Scene} scene - 场景
* @param {Camera} camera - 相机
*/
constructor(scene, camera) {
super();
this.scene = scene;
this.camera = camera;
/**
* 选中对象数组,为undefined时,表示全局显示场景所有对象。
* @type {Array<Object3D>|undefined}
*/
this.selectedObjects = undefined;
/**
* 线框颜色
* @type {Color}
* @default 0x000000
*/
this.color = new Color(0x000000);
/**
* 线框线宽
* @type {number}
* @default 1.0
*/
this.linewidth = 1.0;
/**
* 线框材质
* @type {ShaderMaterial}
* @readonly
*/
this.wireframeMaterial = new ShaderMaterial({
uniforms: {
"color": { value: this.color }
},
vertexShader: `
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 color;
void main() {
gl_FragColor = vec4(color, 1.0);
}
`,
side: DoubleSide,
transparent: false,
opacity: 1.0,
wireframe: true,
vertexColors: false,
depthTest: true,
depthWrite: false
});
// 复制材质,用于混合渲染buffer
this._copyMaterial = new ShaderMaterial(Object.assign({}, CopyShader, {
depthTest: true,
depthWrite: false,
blending: NoBlending,
}));
this._fsQuad = new FullScreenQuad(this._copyMaterial);
this._selectedMeshes = [];
this._invisibleCaches = new Map();
}
/**
* 状态改变事件订阅器
* @type {EventSubscriber}
* @readonly
*/
get changed() {
if (!this._changed)
this._changed = new EventSubscriber();
return this._changed;
}
set enabled(value) {
if (this._enabled !== value) {
this._enabled = value;
this.changed.raiseEvent('enabled', value);
}
}
get enabled() {
return this._enabled;
}
/**
* 渲染
* @param {WebGLRenderer} renderer - 渲染器
* @param {WebGLRenderTarget} writeBuffer - 写入缓冲区
* @param {WebGLRenderTarget} readBuffer - 读取缓冲区
* @param {number} deltaTime - 时间差
* @param {boolean} maskActive - 是否激活遮罩
*/
render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) {
// 选中的对象数组存在时,隐藏非选中的对象。
if (Array.isArray(this.selectedObjects)) {
const meshes = [];
// 遍历选中的对象,将选中的对象添加到meshes数组中
this.selectedObjects.forEach(element => {
element.traverse(child => {
if (child.isMesh) {
meshes.push(child);
}
});
});
// 遍历场景,将非选中的对象设置为不可见,并添加到_invisibleCaches映射中
this.scene.traverseVisible(child => {
if (child.geometry) {
if (!meshes.includes(child)) {
this._invisibleCaches.set(child, child.visible);
child.visible = false;
}
}
});
meshes.length = 0; //清空meshes数组
}
// 保存原始状态
const originalOverrideMaterial = this.scene.overrideMaterial;
const originalBackground = this.scene.background;
const originalEnvironment = this.scene.environment;
const originalAutoClear = renderer.autoClear;
this.wireframeMaterial.wireframeLinewidth = this.linewidth;
// 设置场景为线框模式
this.scene.overrideMaterial = this.wireframeMaterial;
this.scene.background = null;
this.scene.environment = null;
// 设置渲染器状态
renderer.autoClear = false;
// 设置渲染目标
renderer.setRenderTarget(writeBuffer);
// 复制输入缓冲区
this._copyMaterial.uniforms.tDiffuse.value = readBuffer.texture;
this._fsQuad.render(renderer);
// 渲染线框场景
renderer.render(this.scene, this.camera);
// 恢复原始状态
this.scene.overrideMaterial = originalOverrideMaterial;
this.scene.background = originalBackground;
this.scene.environment = originalEnvironment;
renderer.autoClear = originalAutoClear;
// 恢复非选中的对象的可见性
if (this._invisibleCaches.size > 0) {
this._invisibleCaches.forEach((value, key) => {
key.visible = value;
});
this._invisibleCaches.clear();
}
if (this.renderToScreen) {
renderer.setRenderTarget(null);
// 复制输出缓冲区到屏幕
this._copyMaterial.uniforms.tDiffuse.value = writeBuffer.texture;
this._fsQuad.render(renderer);
}
}
/**
* 释放资源
*/
dispose() {
this.wireframeMaterial.dispose();
this._copyMaterial.dispose();
this._fsQuad.dispose();
if (this._tempRenderTarget) {
this._tempRenderTarget.dispose();
}
}
};
export default WireframePass;
export { WireframePass };