three-stdlib
Version:
stand-alone library of threejs examples
481 lines (480 loc) • 21.5 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const Pass = require("./Pass.cjs");
const THREE = require("three");
const CopyShader = require("../shaders/CopyShader.cjs");
class OutlinePass extends Pass.Pass {
constructor(resolution, scene, camera, selectedObjects) {
super();
__publicField(this, "renderScene");
__publicField(this, "renderCamera");
__publicField(this, "selectedObjects");
__publicField(this, "visibleEdgeColor");
__publicField(this, "hiddenEdgeColor");
__publicField(this, "edgeGlow");
__publicField(this, "usePatternTexture");
__publicField(this, "edgeThickness");
__publicField(this, "edgeStrength");
__publicField(this, "downSampleRatio");
__publicField(this, "pulsePeriod");
__publicField(this, "resolution");
__publicField(this, "renderTargetMaskBuffer");
__publicField(this, "depthMaterial");
__publicField(this, "prepareMaskMaterial");
__publicField(this, "renderTargetDepthBuffer");
__publicField(this, "renderTargetMaskDownSampleBuffer");
__publicField(this, "renderTargetBlurBuffer1");
__publicField(this, "renderTargetBlurBuffer2");
__publicField(this, "edgeDetectionMaterial");
__publicField(this, "renderTargetEdgeBuffer1");
__publicField(this, "renderTargetEdgeBuffer2");
__publicField(this, "separableBlurMaterial1");
__publicField(this, "separableBlurMaterial2");
__publicField(this, "overlayMaterial");
__publicField(this, "materialCopy");
__publicField(this, "oldClearAlpha");
__publicField(this, "fsQuad");
__publicField(this, "tempPulseColor1");
__publicField(this, "tempPulseColor2");
__publicField(this, "textureMatrix");
__publicField(this, "patternTexture");
__publicField(this, "_visibilityCache");
__publicField(this, "_oldClearColor");
__publicField(this, "copyUniforms");
__publicField(this, "BlurDirectionX", new THREE.Vector2(1, 0));
__publicField(this, "BlurDirectionY", new THREE.Vector2(0, 1));
this.renderScene = scene;
this.renderCamera = camera;
this.selectedObjects = selectedObjects !== void 0 ? selectedObjects : [];
this.visibleEdgeColor = new THREE.Color(1, 1, 1);
this.hiddenEdgeColor = new THREE.Color(0.1, 0.04, 0.02);
this.edgeGlow = 0;
this.usePatternTexture = false;
this.edgeThickness = 1;
this.edgeStrength = 3;
this.downSampleRatio = 2;
this.pulsePeriod = 0;
this._visibilityCache = /* @__PURE__ */ new Map();
this.resolution = resolution !== void 0 ? new THREE.Vector2(resolution.x, resolution.y) : new THREE.Vector2(256, 256);
const resx = Math.round(this.resolution.x / this.downSampleRatio);
const resy = Math.round(this.resolution.y / this.downSampleRatio);
this.renderTargetMaskBuffer = new THREE.WebGLRenderTarget(this.resolution.x, this.resolution.y);
this.renderTargetMaskBuffer.texture.name = "OutlinePass.mask";
this.renderTargetMaskBuffer.texture.generateMipmaps = false;
this.depthMaterial = new THREE.MeshDepthMaterial();
this.depthMaterial.side = THREE.DoubleSide;
this.depthMaterial.depthPacking = THREE.RGBADepthPacking;
this.depthMaterial.blending = THREE.NoBlending;
this.prepareMaskMaterial = this.getPrepareMaskMaterial();
this.prepareMaskMaterial.side = THREE.DoubleSide;
this.prepareMaskMaterial.fragmentShader = replaceDepthToViewZ(
this.prepareMaskMaterial.fragmentShader,
this.renderCamera
);
this.renderTargetDepthBuffer = new THREE.WebGLRenderTarget(this.resolution.x, this.resolution.y);
this.renderTargetDepthBuffer.texture.name = "OutlinePass.depth";
this.renderTargetDepthBuffer.texture.generateMipmaps = false;
this.renderTargetMaskDownSampleBuffer = new THREE.WebGLRenderTarget(resx, resy);
this.renderTargetMaskDownSampleBuffer.texture.name = "OutlinePass.depthDownSample";
this.renderTargetMaskDownSampleBuffer.texture.generateMipmaps = false;
this.renderTargetBlurBuffer1 = new THREE.WebGLRenderTarget(resx, resy);
this.renderTargetBlurBuffer1.texture.name = "OutlinePass.blur1";
this.renderTargetBlurBuffer1.texture.generateMipmaps = false;
this.renderTargetBlurBuffer2 = new THREE.WebGLRenderTarget(Math.round(resx / 2), Math.round(resy / 2));
this.renderTargetBlurBuffer2.texture.name = "OutlinePass.blur2";
this.renderTargetBlurBuffer2.texture.generateMipmaps = false;
this.edgeDetectionMaterial = this.getEdgeDetectionMaterial();
this.renderTargetEdgeBuffer1 = new THREE.WebGLRenderTarget(resx, resy);
this.renderTargetEdgeBuffer1.texture.name = "OutlinePass.edge1";
this.renderTargetEdgeBuffer1.texture.generateMipmaps = false;
this.renderTargetEdgeBuffer2 = new THREE.WebGLRenderTarget(Math.round(resx / 2), Math.round(resy / 2));
this.renderTargetEdgeBuffer2.texture.name = "OutlinePass.edge2";
this.renderTargetEdgeBuffer2.texture.generateMipmaps = false;
const MAX_EDGE_THICKNESS = 4;
const MAX_EDGE_GLOW = 4;
this.separableBlurMaterial1 = this.getSeperableBlurMaterial(MAX_EDGE_THICKNESS);
this.separableBlurMaterial1.uniforms["texSize"].value.set(resx, resy);
this.separableBlurMaterial1.uniforms["kernelRadius"].value = 1;
this.separableBlurMaterial2 = this.getSeperableBlurMaterial(MAX_EDGE_GLOW);
this.separableBlurMaterial2.uniforms["texSize"].value.set(Math.round(resx / 2), Math.round(resy / 2));
this.separableBlurMaterial2.uniforms["kernelRadius"].value = MAX_EDGE_GLOW;
this.overlayMaterial = this.getOverlayMaterial();
if (CopyShader.CopyShader === void 0)
console.error("THREE.OutlinePass relies on CopyShader");
const copyShader = CopyShader.CopyShader;
this.copyUniforms = THREE.UniformsUtils.clone(copyShader.uniforms);
this.copyUniforms["opacity"].value = 1;
this.materialCopy = new THREE.ShaderMaterial({
uniforms: this.copyUniforms,
vertexShader: copyShader.vertexShader,
fragmentShader: copyShader.fragmentShader,
blending: THREE.NoBlending,
depthTest: false,
depthWrite: false,
transparent: true
});
this.enabled = true;
this.needsSwap = false;
this._oldClearColor = new THREE.Color();
this.oldClearAlpha = 1;
this.fsQuad = new Pass.FullScreenQuad(this.materialCopy);
this.tempPulseColor1 = new THREE.Color();
this.tempPulseColor2 = new THREE.Color();
this.textureMatrix = new THREE.Matrix4();
function replaceDepthToViewZ(string, camera2) {
const type = camera2.isPerspectiveCamera ? "perspective" : "orthographic";
return string.replace(/DEPTH_TO_VIEW_Z/g, type + "DepthToViewZ");
}
}
dispose() {
this.renderTargetMaskBuffer.dispose();
this.renderTargetDepthBuffer.dispose();
this.renderTargetMaskDownSampleBuffer.dispose();
this.renderTargetBlurBuffer1.dispose();
this.renderTargetBlurBuffer2.dispose();
this.renderTargetEdgeBuffer1.dispose();
this.renderTargetEdgeBuffer2.dispose();
}
setSize(width, height) {
this.renderTargetMaskBuffer.setSize(width, height);
this.renderTargetDepthBuffer.setSize(width, height);
let resx = Math.round(width / this.downSampleRatio);
let resy = Math.round(height / this.downSampleRatio);
this.renderTargetMaskDownSampleBuffer.setSize(resx, resy);
this.renderTargetBlurBuffer1.setSize(resx, resy);
this.renderTargetEdgeBuffer1.setSize(resx, resy);
this.separableBlurMaterial1.uniforms["texSize"].value.set(resx, resy);
resx = Math.round(resx / 2);
resy = Math.round(resy / 2);
this.renderTargetBlurBuffer2.setSize(resx, resy);
this.renderTargetEdgeBuffer2.setSize(resx, resy);
this.separableBlurMaterial2.uniforms["texSize"].value.set(resx, resy);
}
changeVisibilityOfSelectedObjects(bVisible) {
const cache = this._visibilityCache;
function gatherSelectedMeshesCallBack(object) {
if (object.isMesh) {
if (bVisible === true) {
object.visible = cache.get(object);
} else {
cache.set(object, object.visible);
object.visible = bVisible;
}
}
}
for (let i = 0; i < this.selectedObjects.length; i++) {
const selectedObject = this.selectedObjects[i];
selectedObject.traverse(gatherSelectedMeshesCallBack);
}
}
changeVisibilityOfNonSelectedObjects(bVisible) {
const cache = this._visibilityCache;
const selectedMeshes = [];
function gatherSelectedMeshesCallBack(object) {
if (object.isMesh)
selectedMeshes.push(object);
}
for (let i = 0; i < this.selectedObjects.length; i++) {
const selectedObject = this.selectedObjects[i];
selectedObject.traverse(gatherSelectedMeshesCallBack);
}
function VisibilityChangeCallBack(object) {
if (object.isMesh || object.isSprite) {
let bFound = false;
for (let i = 0; i < selectedMeshes.length; i++) {
const selectedObjectId = selectedMeshes[i].id;
if (selectedObjectId === object.id) {
bFound = true;
break;
}
}
if (bFound === false) {
const visibility = object.visible;
if (bVisible === false || cache.get(object) === true) {
object.visible = bVisible;
}
cache.set(object, visibility);
}
} else if (object.isPoints || object.isLine) {
if (bVisible === true) {
object.visible = cache.get(object);
} else {
cache.set(object, object.visible);
object.visible = bVisible;
}
}
}
this.renderScene.traverse(VisibilityChangeCallBack);
}
updateTextureMatrix() {
this.textureMatrix.set(0.5, 0, 0, 0.5, 0, 0.5, 0, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 1);
this.textureMatrix.multiply(this.renderCamera.projectionMatrix);
this.textureMatrix.multiply(this.renderCamera.matrixWorldInverse);
}
render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) {
if (this.selectedObjects.length > 0) {
renderer.getClearColor(this._oldClearColor);
this.oldClearAlpha = renderer.getClearAlpha();
const oldAutoClear = renderer.autoClear;
renderer.autoClear = false;
if (maskActive)
renderer.state.buffers.stencil.setTest(false);
renderer.setClearColor(16777215, 1);
this.changeVisibilityOfSelectedObjects(false);
const currentBackground = this.renderScene.background;
this.renderScene.background = null;
this.renderScene.overrideMaterial = this.depthMaterial;
renderer.setRenderTarget(this.renderTargetDepthBuffer);
renderer.clear();
renderer.render(this.renderScene, this.renderCamera);
this.changeVisibilityOfSelectedObjects(true);
this._visibilityCache.clear();
this.updateTextureMatrix();
this.changeVisibilityOfNonSelectedObjects(false);
this.renderScene.overrideMaterial = this.prepareMaskMaterial;
this.prepareMaskMaterial.uniforms["cameraNearFar"].value.set(
this.renderCamera.near,
this.renderCamera.far
);
this.prepareMaskMaterial.uniforms["depthTexture"].value = this.renderTargetDepthBuffer.texture;
this.prepareMaskMaterial.uniforms["textureMatrix"].value = this.textureMatrix;
renderer.setRenderTarget(this.renderTargetMaskBuffer);
renderer.clear();
renderer.render(this.renderScene, this.renderCamera);
this.renderScene.overrideMaterial = null;
this.changeVisibilityOfNonSelectedObjects(true);
this._visibilityCache.clear();
this.renderScene.background = currentBackground;
this.fsQuad.material = this.materialCopy;
this.copyUniforms["tDiffuse"].value = this.renderTargetMaskBuffer.texture;
renderer.setRenderTarget(this.renderTargetMaskDownSampleBuffer);
renderer.clear();
this.fsQuad.render(renderer);
this.tempPulseColor1.copy(this.visibleEdgeColor);
this.tempPulseColor2.copy(this.hiddenEdgeColor);
if (this.pulsePeriod > 0) {
const scalar = (1 + 0.25) / 2 + Math.cos(performance.now() * 0.01 / this.pulsePeriod) * (1 - 0.25) / 2;
this.tempPulseColor1.multiplyScalar(scalar);
this.tempPulseColor2.multiplyScalar(scalar);
}
this.fsQuad.material = this.edgeDetectionMaterial;
this.edgeDetectionMaterial.uniforms["maskTexture"].value = this.renderTargetMaskDownSampleBuffer.texture;
this.edgeDetectionMaterial.uniforms["texSize"].value.set(
this.renderTargetMaskDownSampleBuffer.width,
this.renderTargetMaskDownSampleBuffer.height
);
this.edgeDetectionMaterial.uniforms["visibleEdgeColor"].value = this.tempPulseColor1;
this.edgeDetectionMaterial.uniforms["hiddenEdgeColor"].value = this.tempPulseColor2;
renderer.setRenderTarget(this.renderTargetEdgeBuffer1);
renderer.clear();
this.fsQuad.render(renderer);
this.fsQuad.material = this.separableBlurMaterial1;
this.separableBlurMaterial1.uniforms["colorTexture"].value = this.renderTargetEdgeBuffer1.texture;
this.separableBlurMaterial1.uniforms["direction"].value = this.BlurDirectionX;
this.separableBlurMaterial1.uniforms["kernelRadius"].value = this.edgeThickness;
renderer.setRenderTarget(this.renderTargetBlurBuffer1);
renderer.clear();
this.fsQuad.render(renderer);
this.separableBlurMaterial1.uniforms["colorTexture"].value = this.renderTargetBlurBuffer1.texture;
this.separableBlurMaterial1.uniforms["direction"].value = this.BlurDirectionY;
renderer.setRenderTarget(this.renderTargetEdgeBuffer1);
renderer.clear();
this.fsQuad.render(renderer);
this.fsQuad.material = this.separableBlurMaterial2;
this.separableBlurMaterial2.uniforms["colorTexture"].value = this.renderTargetEdgeBuffer1.texture;
this.separableBlurMaterial2.uniforms["direction"].value = this.BlurDirectionX;
renderer.setRenderTarget(this.renderTargetBlurBuffer2);
renderer.clear();
this.fsQuad.render(renderer);
this.separableBlurMaterial2.uniforms["colorTexture"].value = this.renderTargetBlurBuffer2.texture;
this.separableBlurMaterial2.uniforms["direction"].value = this.BlurDirectionY;
renderer.setRenderTarget(this.renderTargetEdgeBuffer2);
renderer.clear();
this.fsQuad.render(renderer);
this.fsQuad.material = this.overlayMaterial;
this.overlayMaterial.uniforms["maskTexture"].value = this.renderTargetMaskBuffer.texture;
this.overlayMaterial.uniforms["edgeTexture1"].value = this.renderTargetEdgeBuffer1.texture;
this.overlayMaterial.uniforms["edgeTexture2"].value = this.renderTargetEdgeBuffer2.texture;
this.overlayMaterial.uniforms["patternTexture"].value = this.patternTexture;
this.overlayMaterial.uniforms["edgeStrength"].value = this.edgeStrength;
this.overlayMaterial.uniforms["edgeGlow"].value = this.edgeGlow;
this.overlayMaterial.uniforms["usePatternTexture"].value = this.usePatternTexture;
if (maskActive)
renderer.state.buffers.stencil.setTest(true);
renderer.setRenderTarget(readBuffer);
this.fsQuad.render(renderer);
renderer.setClearColor(this._oldClearColor, this.oldClearAlpha);
renderer.autoClear = oldAutoClear;
}
if (this.renderToScreen) {
this.fsQuad.material = this.materialCopy;
this.copyUniforms["tDiffuse"].value = readBuffer.texture;
renderer.setRenderTarget(null);
this.fsQuad.render(renderer);
}
}
getPrepareMaskMaterial() {
return new THREE.ShaderMaterial({
uniforms: {
depthTexture: { value: null },
cameraNearFar: { value: new THREE.Vector2(0.5, 0.5) },
textureMatrix: { value: null }
},
vertexShader: `#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
varying vec4 projTexCoord;
varying vec4 vPosition;
uniform mat4 textureMatrix;
void main() {
#include <skinbase_vertex>
#include <begin_vertex>
#include <morphtarget_vertex>
#include <skinning_vertex>
#include <project_vertex>
vPosition = mvPosition;
vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );
projTexCoord = textureMatrix * worldPosition;
}`,
fragmentShader: `#include <packing>
varying vec4 vPosition;
varying vec4 projTexCoord;
uniform sampler2D depthTexture;
uniform vec2 cameraNearFar;
void main() {
float depth = unpackRGBAToDepth(texture2DProj( depthTexture, projTexCoord ));
float viewZ = - DEPTH_TO_VIEW_Z( depth, cameraNearFar.x, cameraNearFar.y );
float depthTest = (-vPosition.z > viewZ) ? 1.0 : 0.0;
gl_FragColor = vec4(0.0, depthTest, 1.0, 1.0);
}`
});
}
getEdgeDetectionMaterial() {
return new THREE.ShaderMaterial({
uniforms: {
maskTexture: { value: null },
texSize: { value: new THREE.Vector2(0.5, 0.5) },
visibleEdgeColor: { value: new THREE.Vector3(1, 1, 1) },
hiddenEdgeColor: { value: new THREE.Vector3(1, 1, 1) }
},
vertexShader: `varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`,
fragmentShader: `varying vec2 vUv;
uniform sampler2D maskTexture;
uniform vec2 texSize;
uniform vec3 visibleEdgeColor;
uniform vec3 hiddenEdgeColor;
void main() {
vec2 invSize = 1.0 / texSize;
vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);
vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);
vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);
vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);
vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);
float diff1 = (c1.r - c2.r)*0.5;
float diff2 = (c3.r - c4.r)*0.5;
float d = length( vec2(diff1, diff2) );
float a1 = min(c1.g, c2.g);
float a2 = min(c3.g, c4.g);
float visibilityFactor = min(a1, a2);
vec3 edgeColor = 1.0 - visibilityFactor > 0.001 ? visibleEdgeColor : hiddenEdgeColor;
gl_FragColor = vec4(edgeColor, 1.0) * vec4(d);
}`
});
}
getSeperableBlurMaterial(maxRadius) {
return new THREE.ShaderMaterial({
defines: {
MAX_RADIUS: maxRadius
},
uniforms: {
colorTexture: { value: null },
texSize: { value: new THREE.Vector2(0.5, 0.5) },
direction: { value: new THREE.Vector2(0.5, 0.5) },
kernelRadius: { value: 1 }
},
vertexShader: `varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`,
fragmentShader: `#include <common>
varying vec2 vUv;
uniform sampler2D colorTexture;
uniform vec2 texSize;
uniform vec2 direction;
uniform float kernelRadius;
float gaussianPdf(in float x, in float sigma) {
return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
}
void main() {
vec2 invSize = 1.0 / texSize;
float weightSum = gaussianPdf(0.0, kernelRadius);
vec4 diffuseSum = texture2D( colorTexture, vUv) * weightSum;
vec2 delta = direction * invSize * kernelRadius/float(MAX_RADIUS);
vec2 uvOffset = delta;
for( int i = 1; i <= MAX_RADIUS; i ++ ) {
float w = gaussianPdf(uvOffset.x, kernelRadius);
vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);
vec4 sample2 = texture2D( colorTexture, vUv - uvOffset);
diffuseSum += ((sample1 + sample2) * w);
weightSum += (2.0 * w);
uvOffset += delta;
}
gl_FragColor = diffuseSum/weightSum;
}`
});
}
getOverlayMaterial() {
return new THREE.ShaderMaterial({
uniforms: {
maskTexture: { value: null },
edgeTexture1: { value: null },
edgeTexture2: { value: null },
patternTexture: { value: null },
edgeStrength: { value: 1 },
edgeGlow: { value: 1 },
usePatternTexture: { value: 0 }
},
vertexShader: `varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`,
fragmentShader: `varying vec2 vUv;
uniform sampler2D maskTexture;
uniform sampler2D edgeTexture1;
uniform sampler2D edgeTexture2;
uniform sampler2D patternTexture;
uniform float edgeStrength;
uniform float edgeGlow;
uniform bool usePatternTexture;
void main() {
vec4 edgeValue1 = texture2D(edgeTexture1, vUv);
vec4 edgeValue2 = texture2D(edgeTexture2, vUv);
vec4 maskColor = texture2D(maskTexture, vUv);
vec4 patternColor = texture2D(patternTexture, 6.0 * vUv);
float visibilityFactor = 1.0 - maskColor.g > 0.0 ? 1.0 : 0.5;
vec4 edgeValue = edgeValue1 + edgeValue2 * edgeGlow;
vec4 finalColor = edgeStrength * maskColor.r * edgeValue;
if(usePatternTexture)
finalColor += + visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r);
gl_FragColor = finalColor;
}`,
blending: THREE.AdditiveBlending,
depthTest: false,
depthWrite: false,
transparent: true
});
}
}
exports.OutlinePass = OutlinePass;
//# sourceMappingURL=OutlinePass.cjs.map