2017-10-24 12:33:14 +02:00
|
|
|
import * as THREE from 'three';
|
2017-10-24 15:45:58 +02:00
|
|
|
import 'three/examples/js/postprocessing/EffectComposer.js';
|
|
|
|
import 'three/examples/js/postprocessing/RenderPass.js';
|
|
|
|
import 'three/examples/js/postprocessing/ShaderPass.js';
|
|
|
|
import 'three/examples/js/shaders/CopyShader.js';
|
2017-10-24 12:33:14 +02:00
|
|
|
import vertexShaderPostprocessing from './shaders/vertexShaderPostprocessing.js';
|
|
|
|
import fragmentShaderSobelDepth from './shaders/fragmentShaderSobelDepth.js';
|
|
|
|
import fragmentShaderSobelNormal from './shaders/fragmentShaderSobelNormal.js';
|
|
|
|
import fragmentShaderCombineTextures from './shaders/fragmentShaderCombineTextures.js';
|
|
|
|
import fragmentShaderDepth from './shaders/fragmentShaderDepth.js';
|
|
|
|
import vertexShaderDepth from './shaders/vertexShaderDepth.js';
|
|
|
|
|
|
|
|
// Based on Doodle3D/Toon-Shader
|
|
|
|
|
|
|
|
// initize render targets with default canvas size
|
|
|
|
const DEFAULT_WIDTH = 300;
|
|
|
|
const DEFAULT_HEIGHT = 200;
|
|
|
|
|
|
|
|
const NORMAL_MATERIAL = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide });
|
|
|
|
const DEPTH_MATERIAL = new THREE.ShaderMaterial({
|
|
|
|
uniforms: {
|
|
|
|
mNear: { type: 'f', value: 1.0 },
|
|
|
|
mFar: { type: 'f', value: 3000.0 },
|
|
|
|
opacity: { type: 'f', value: 1.0 },
|
|
|
|
logDepthBufFC: { type: 'f', value: 2.0 }
|
|
|
|
},
|
|
|
|
vertexShader: vertexShaderDepth,
|
|
|
|
fragmentShader: fragmentShaderDepth,
|
|
|
|
side: THREE.DoubleSide
|
|
|
|
});
|
|
|
|
|
|
|
|
export default class Composer {
|
|
|
|
constructor(renderer, scene, camera, groups) {
|
|
|
|
this._renderer = renderer;
|
|
|
|
this._scene = scene;
|
|
|
|
this._camera = camera;
|
|
|
|
this._groups = groups;
|
|
|
|
|
|
|
|
this._aspect = new THREE.Vector2(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
|
|
|
|
|
|
const {
|
|
|
|
composer: composerDepth,
|
|
|
|
renderTarget: renderTargetDepth
|
|
|
|
} = this._createSobelComposer(DEPTH_MATERIAL, 0.005, fragmentShaderSobelDepth);
|
|
|
|
|
|
|
|
this._composerDepth = composerDepth;
|
|
|
|
|
|
|
|
const {
|
|
|
|
composer: composerNormal,
|
|
|
|
renderTarget: renderTargetNormal
|
|
|
|
} = this._createSobelComposer(NORMAL_MATERIAL, 0.5, fragmentShaderSobelNormal);
|
|
|
|
|
|
|
|
this._composerNormal = composerNormal;
|
|
|
|
|
|
|
|
const renderTargetUI = new THREE.WebGLRenderTarget(DEFAULT_WIDTH, DEFAULT_HEIGHT, {
|
|
|
|
format: THREE.RGBAFormat
|
|
|
|
});
|
|
|
|
this._composerUI = new THREE.EffectComposer(this._renderer, renderTargetUI);
|
|
|
|
this._composerUI.addPass(new THREE.RenderPass(scene, camera));
|
|
|
|
this._composerUI.addPass(new THREE.ShaderPass(THREE.CopyShader));
|
|
|
|
|
|
|
|
this._composer = new THREE.EffectComposer(renderer);
|
|
|
|
this._composer.addPass(new THREE.RenderPass(scene, camera, undefined, new THREE.Color(0xffffff), 1.0));
|
|
|
|
const combineComposers = new THREE.ShaderPass(new THREE.ShaderMaterial({
|
|
|
|
uniforms: {
|
|
|
|
tDiffuse: { type: 't' },
|
|
|
|
tNormal: { type: 't', value: renderTargetNormal.texture },
|
|
|
|
tDepth: { type: 't', value: renderTargetDepth.texture },
|
|
|
|
tUI: { type: 't', value: renderTargetUI.texture }
|
|
|
|
},
|
|
|
|
vertexShader: vertexShaderPostprocessing,
|
|
|
|
fragmentShader: fragmentShaderCombineTextures
|
|
|
|
}));
|
|
|
|
combineComposers.renderToScreen = true;
|
|
|
|
this._composer.addPass(combineComposers);
|
|
|
|
}
|
|
|
|
|
|
|
|
_createSobelComposer(material, threshold, fragmentShader) {
|
|
|
|
const renderTarget = new THREE.WebGLRenderTarget(DEFAULT_WIDTH, DEFAULT_HEIGHT, {
|
|
|
|
format: THREE.RGBFormat
|
|
|
|
});
|
|
|
|
|
|
|
|
const composer = new THREE.EffectComposer(this._renderer, renderTarget);
|
|
|
|
|
|
|
|
composer.addPass(new THREE.RenderPass(this._scene, this._camera, material));
|
|
|
|
|
|
|
|
const sobelShader = new THREE.ShaderPass(new THREE.ShaderMaterial({
|
|
|
|
uniforms: {
|
|
|
|
tDiffuse: { type: 't' },
|
|
|
|
threshold: { type: 'f', value: threshold },
|
|
|
|
aspect: { type: 'v2', value: this._aspect }
|
|
|
|
},
|
|
|
|
vertexShader: vertexShaderPostprocessing,
|
|
|
|
fragmentShader
|
|
|
|
}));
|
|
|
|
composer.addPass(sobelShader);
|
|
|
|
|
|
|
|
composer.addPass(new THREE.ShaderPass(THREE.CopyShader));
|
|
|
|
|
|
|
|
return { renderTarget, composer };
|
|
|
|
}
|
|
|
|
|
|
|
|
setSize(width, height, pixelRatio) {
|
|
|
|
this._renderer.setPixelRatio(pixelRatio);
|
|
|
|
this._renderer.setSize(width, height);
|
|
|
|
|
|
|
|
this._aspect.set(width, height);
|
|
|
|
|
|
|
|
// adjust aspect ratio of camera
|
|
|
|
this._camera.aspect = width / height;
|
|
|
|
this._camera.updateProjectionMatrix();
|
|
|
|
|
|
|
|
width *= pixelRatio;
|
|
|
|
height *= pixelRatio;
|
|
|
|
|
|
|
|
this._composer.setSize(width, height);
|
|
|
|
this._composerNormal.setSize(width, height);
|
|
|
|
this._composerDepth.setSize(width, height);
|
|
|
|
this._composerUI.setSize(width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
getCurrentVisibleValues() {
|
|
|
|
const visibleValues = {};
|
|
|
|
|
|
|
|
for (const key in this._groups) {
|
|
|
|
visibleValues[key] = this._groups[key].visible;
|
|
|
|
}
|
|
|
|
|
|
|
|
return visibleValues;
|
|
|
|
}
|
|
|
|
|
|
|
|
setVisible(initalValues, visibleGroups) {
|
|
|
|
for (const key in this._groups) {
|
|
|
|
const group = this._groups[key];
|
|
|
|
|
|
|
|
if (visibleGroups.indexOf(group) !== -1) {
|
|
|
|
group.visible = initalValues[key];
|
|
|
|
} else {
|
|
|
|
group.visible = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const initalValues = this.getCurrentVisibleValues();
|
|
|
|
|
|
|
|
const shapes = this._groups.shapes;
|
|
|
|
const UI = this._groups.UI;
|
|
|
|
const plane = this._groups.plane;
|
|
|
|
const boundingBox = this._groups.boundingBox;
|
|
|
|
|
|
|
|
this.setVisible(initalValues, [shapes]);
|
|
|
|
this._composerDepth.render();
|
|
|
|
this._composerNormal.render();
|
|
|
|
|
|
|
|
this.setVisible(initalValues, [UI]);
|
|
|
|
this._composerUI.render();
|
|
|
|
|
|
|
|
this.setVisible(initalValues, [shapes, plane, boundingBox]);
|
|
|
|
this._composer.render();
|
|
|
|
|
|
|
|
this.setVisible(initalValues, [shapes, UI, plane, boundingBox]);
|
|
|
|
}
|
|
|
|
}
|