Skip to content

Commit

Permalink
feat: Improve display quality
Browse files Browse the repository at this point in the history
  • Loading branch information
xiangechen committed Jul 23, 2024
1 parent 657fba7 commit 73e3b2d
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 80 deletions.
6 changes: 3 additions & 3 deletions packages/chili-core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { Lazy, PubSub } from "./foundation";
import { ObjectSnapType } from "./snapType";

export const VisualConfig = {
defaultEdgeColor: 0x121314,
defaultEdgeColor: 0x111111,
defaultFaceColor: 0xdedede,
highlightEdgeColor: 0x0000ee,
highlightFaceColor: 0x0000ee,
selectedEdgeColor: 0x0000ff,
selectedFaceColor: 0x0000ff,
selectedEdgeColor: 0x2222ff,
selectedFaceColor: 0x2222ff,
editVertexSize: 7,
editVertexColor: 0x0000ff,
hintVertexSize: 5,
Expand Down
99 changes: 44 additions & 55 deletions packages/chili-three/src/outlinePass.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
/**
* This is a forked and simplified version of the OutlinePass from three.js. In order to improve performance, various
* features have been deleted. This is about 2x faster.
*/
import {
AdditiveBlending,
Color,
DoubleSide,
HalfFloatType,
LinearFilter,
Matrix4,
MeshBasicMaterial,
MeshDepthMaterial,
NoBlending,
RGBADepthPacking,
RGBAFormat,
ShaderMaterial,
UniformsUtils,
Vector2,
Expand All @@ -16,7 +22,7 @@ import {
import { FullScreenQuad, Pass } from "three/examples/jsm/postprocessing/Pass.js";
import { CopyShader } from "three/examples/jsm/shaders/CopyShader.js";

export class OutlinePass extends Pass {
class OutlinePass extends Pass {
constructor(scene, camera) {
super();

Expand All @@ -25,15 +31,23 @@ export class OutlinePass extends Pass {
this.visibleEdgeColor = new Color(1, 1, 1);
this.hiddenEdgeColor = new Color(0.1, 0.04, 0.02);
this.usePatternTexture = false;
this.patternTexture = null;
this.edgeStrength = 3.0;
this.pulsePeriod = 0;

this._visibilityCache = new Map();

this.resolution = new Vector2(256, 256);

this.renderTargetMaskBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y);
const pars = {
minFilter: LinearFilter,
magFilter: LinearFilter,
format: RGBAFormat,
skipInvalidateFramebuffer: true,
};

this.maskBufferMaterial = new MeshBasicMaterial({ color: 0xffffff });
this.maskBufferMaterial.side = DoubleSide;
this.renderTargetMaskBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y, pars);
this.renderTargetMaskBuffer.texture.name = "OutlinePass.mask";
this.renderTargetMaskBuffer.texture.generateMipmaps = false;

Expand All @@ -49,26 +63,25 @@ export class OutlinePass extends Pass {
this.renderCamera,
);

this.renderTargetDepthBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y, {
type: HalfFloatType,
});
this.renderTargetDepthBuffer = new WebGLRenderTarget(this.resolution.x, this.resolution.y, pars);
this.renderTargetDepthBuffer.texture.name = "OutlinePass.depth";
this.renderTargetDepthBuffer.texture.generateMipmaps = false;

this.edgeDetectionMaterial = this.getEdgeDetectionMaterial();
this.renderTargetEdgeBuffer1 = new WebGLRenderTarget(this.resolution.x, this.resolution.y, {
type: HalfFloatType,
});
this.renderTargetEdgeBuffer1 = new WebGLRenderTarget(this.resolution.x, this.resolution.y, pars);
this.renderTargetEdgeBuffer1.texture.name = "OutlinePass.edge1";
this.renderTargetEdgeBuffer1.texture.generateMipmaps = false;

// Overlay material
this.overlayMaterial = this.getOverlayMaterial();

// copy material
if (CopyShader === undefined) console.error("THREE.OutlinePass relies on CopyShader");

const copyShader = CopyShader;

this.copyUniforms = UniformsUtils.clone(copyShader.uniforms);
this.copyUniforms["opacity"].value = 1.0;

this.materialCopy = new ShaderMaterial({
uniforms: this.copyUniforms,
Expand All @@ -77,6 +90,7 @@ export class OutlinePass extends Pass {
blending: NoBlending,
depthTest: false,
depthWrite: false,
transparent: true,
});

this.enabled = true;
Expand All @@ -92,7 +106,8 @@ export class OutlinePass extends Pass {
this.textureMatrix = new Matrix4();

function replaceDepthToViewZ(string, camera) {
const type = camera.isPerspectiveCamera ? "perspective" : "orthographic";
var type = camera.isPerspectiveCamera ? "perspective" : "orthographic";

return string.replace(/DEPTH_TO_VIEW_Z/g, type + "DepthToViewZ");
}
}
Expand All @@ -101,14 +116,6 @@ export class OutlinePass extends Pass {
this.renderTargetMaskBuffer.dispose();
this.renderTargetDepthBuffer.dispose();
this.renderTargetEdgeBuffer1.dispose();

this.depthMaterial.dispose();
this.prepareMaskMaterial.dispose();
this.edgeDetectionMaterial.dispose();
this.overlayMaterial.dispose();
this.materialCopy.dispose();

this.fsQuad.dispose();
}

setSize(width, height) {
Expand Down Expand Up @@ -188,9 +195,7 @@ export class OutlinePass extends Pass {
this.fsQuad.material = this.overlayMaterial;
this.overlayMaterial.uniforms["maskTexture"].value = this.renderTargetMaskBuffer.texture;
this.overlayMaterial.uniforms["edgeTexture1"].value = this.renderTargetEdgeBuffer1.texture;
this.overlayMaterial.uniforms["patternTexture"].value = this.patternTexture;
this.overlayMaterial.uniforms["edgeStrength"].value = this.edgeStrength;
this.overlayMaterial.uniforms["usePatternTexture"].value = this.usePatternTexture;

if (maskActive) renderer.state.buffers.stencil.setTest(true);

Expand All @@ -210,59 +215,52 @@ export class OutlinePass extends Pass {
}

getPrepareMaskMaterial() {
return new ShaderMaterial({
const result = new ShaderMaterial({
uniforms: {
depthTexture: { value: null },
cameraNearFar: { value: new Vector2(0.5, 0.5) },
textureMatrix: { value: null },
id: { 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 = vec4( transformed, 1.0 );
#ifdef USE_INSTANCING
worldPosition = instanceMatrix * worldPosition;
#endif
worldPosition = modelMatrix * worldPosition;
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;
uniform float id;
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);
gl_FragColor = vec4(id/255.0, depthTest, 1.0, 1.0);
}`,
});

let idCounter = 0;
result.onBeforeRender = (renderer, scene, camera, geometry, object, group) => {
result.uniforms.id.value = idCounter;
result.uniformsNeedUpdate = true;
idCounter += 100;
idCounter %= 255;
};
return result;
}

getEdgeDetectionMaterial() {
Expand All @@ -275,21 +273,18 @@ export class OutlinePass extends Pass {
},

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;
vec2 invSize = 1. / 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);
Expand All @@ -302,7 +297,7 @@ export class OutlinePass extends Pass {
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);
gl_FragColor = vec4(edgeColor, 1.0) * vec4(d > 0. ? 1. : 0.);
}`,
});
}
Expand All @@ -314,32 +309,24 @@ export class OutlinePass extends Pass {
edgeTexture1: { value: null },
patternTexture: { value: null },
edgeStrength: { value: 1.0 },
usePatternTexture: { value: 0.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 patternTexture;
uniform float edgeStrength;
uniform bool usePatternTexture;
void main() {
vec4 edgeValue = texture2D(edgeTexture1, 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 finalColor = edgeStrength * edgeValue;
if(usePatternTexture)
finalColor += + visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r);
gl_FragColor = finalColor;
}`,
blending: AdditiveBlending,
Expand All @@ -349,3 +336,5 @@ export class OutlinePass extends Pass {
});
}
}

export { OutlinePass };
6 changes: 5 additions & 1 deletion packages/chili-three/src/threeGeometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IVisualGeometry,
Matrix4,
ShapeMeshData,
VisualConfig,
} from "chili-core";
import {
BufferGeometry,
Expand All @@ -26,7 +27,10 @@ import { ThreeVisualContext } from "./threeVisualContext";

export class ThreeGeometry extends Object3D implements IVisualGeometry {
private _faceMaterial: Material;
private _edgeMaterial = new LineBasicMaterial();
private _edgeMaterial = new LineBasicMaterial({
linewidth: 2,
color: VisualConfig.defaultEdgeColor,
});
private _edges?: LineSegments;
private _faces?: Mesh;

Expand Down
2 changes: 0 additions & 2 deletions packages/chili-three/src/threeHighlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,8 @@ export class GeometryState {
this.geometry.removeTemperaryMaterial();
} else if (VisualState.hasState(newState, VisualState.highlighter)) {
this.geometry.setEdgesMateiralTemperary(hilightEdgeMaterial);
this.geometry.setFacesMateiralTemperary(highlightFaceMaterial);
} else if (VisualState.hasState(newState, VisualState.selected)) {
this.geometry.setEdgesMateiralTemperary(selectedEdgeMaterial);
this.geometry.setFacesMateiralTemperary(selectedFaceMaterial);
}

this._states.set(key, [newState, this.geometry as any]);
Expand Down
4 changes: 0 additions & 4 deletions packages/chili-three/src/threeRenderBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,9 @@ export class ThreeRenderBuilder {

addOutlinePass(scene: Scene, color: number, usePatternTexture: boolean) {
const outlinePass = new OutlinePass(scene, this.camera);
outlinePass.usePatternTexture = usePatternTexture;
outlinePass.visibleEdgeColor = new Color(color).convertSRGBToLinear();
outlinePass.hiddenEdgeColor = new Color(color).convertSRGBToLinear();
outlinePass.edgeStrength = 5;
if (usePatternTexture) {
this.loadPatternTexture((t) => (outlinePass.patternTexture = t));
}
this.composer.addPass(outlinePass);

return this;
Expand Down
3 changes: 2 additions & 1 deletion packages/chili-three/src/threeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ export class ThreeView extends Observable implements IView {
});
if (!this._needsUpdate) return;

this.dynamicLight.position.copy(this.camera.position);
let dir = this.camera.position.clone().sub(this.cameraController.target);
this.dynamicLight.position.copy(dir);
this._composer.render();
this._gizmo?.update();

Expand Down
17 changes: 3 additions & 14 deletions packages/chili-three/src/threeVisual.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
// Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license.

import {
IDisposable,
IDocument,
IEventHandler,
IHighlighter,
ITextGenerator,
IVisual,
Logger,
Plane,
} from "chili-core";
import { IDisposable, IDocument, IEventHandler, ITextGenerator, IVisual, Logger, Plane } from "chili-core";
import { ModelSelectionHandler } from "chili-vis";
import { AmbientLight, AxesHelper, Color, DirectionalLight, Object3D, Scene } from "three";
import { AmbientLight, AxesHelper, Object3D, Scene } from "three";
import { ThreeHighlighter } from "./threeHighlighter";
import { ThreeTextGenerator } from "./threeTextGenerator";
import { ThreeView } from "./threeView";
Expand Down Expand Up @@ -52,11 +43,9 @@ export class ThreeVisual implements IVisual {

initScene() {
let scene = new Scene();
scene.background = new Color(0x888888);
const light = new DirectionalLight(0xffffff, 0.5);
let envLight = new AmbientLight(0x888888, 4);
let axisHelper = new AxesHelper(250);
scene.add(light, envLight, axisHelper);
scene.add(envLight, axisHelper);
return scene;
}

Expand Down

0 comments on commit 73e3b2d

Please sign in to comment.