From 4e37a4c4cfccd251d9628a67c057f1870cac2b2e Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Thu, 23 Nov 2017 17:29:56 +0100 Subject: [PATCH] use matcap material --- img/matcap.png | Bin 0 -> 51379 bytes shaders/matcap_frag.glsl | 29 +++++++++++++++++++++ shaders/matcap_vert.glsl | 8 ++++++ src/components/D3Panel.js | 4 ++- src/components/DoodlePreview.js | 5 +++- src/d3/MatcapMaterial.js | 44 ++++++++++++++++++++++++++++++++ src/d3/ShapeMesh.js | 22 ++++------------ src/d3/ShapesManager.js | 6 ++--- 8 files changed, 95 insertions(+), 23 deletions(-) create mode 100644 img/matcap.png create mode 100644 shaders/matcap_frag.glsl create mode 100644 shaders/matcap_vert.glsl create mode 100644 src/d3/MatcapMaterial.js diff --git a/img/matcap.png b/img/matcap.png new file mode 100644 index 0000000000000000000000000000000000000000..f34f252bde941a16821bf85a025903223e301d5e GIT binary patch literal 51379 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 KMNUMnLSTZ8Ow+6Y literal 0 HcmV?d00001 diff --git a/shaders/matcap_frag.glsl b/shaders/matcap_frag.glsl new file mode 100644 index 0000000..5395324 --- /dev/null +++ b/shaders/matcap_frag.glsl @@ -0,0 +1,29 @@ +uniform float opacity; +uniform sampler2D tMatcap; +uniform vec3 color; +varying vec2 vNormal; + +// color blending from https://www.w3.org/TR/compositing-1/#blendingcolor +float lum(vec3 c) { + return c.r * .3 + c.g * .59 + c.b * .11; +} + +vec3 clipColor(vec3 c) { + float l = lum(c); + float n = min(min(c.r, c.g), c.b); + float x = max(max(c.r, c.g), c.b); + if (n < 0.) c = l + (((c - l) * l) / (l - n)); + if (x > 1.) c = l + (((c - l) * (1. - l)) / (x - l)); + return c; +} + +vec3 setLum(vec3 c, float l) { + float d = l - lum(c); + return clipColor(c + d); +} + +void main() { + vec4 matcap = texture2D(tMatcap, vNormal); + vec3 coloredMatcap = setLum(color, lum(matcap.rgb)); + gl_FragColor = vec4(coloredMatcap, opacity); +} diff --git a/shaders/matcap_vert.glsl b/shaders/matcap_vert.glsl new file mode 100644 index 0000000..db6f191 --- /dev/null +++ b/shaders/matcap_vert.glsl @@ -0,0 +1,8 @@ +varying vec2 vNormal; + +void main() { + vNormal = normalize(normalMatrix * normal).xy / 2. + .5; + + #include + #include +} diff --git a/src/components/D3Panel.js b/src/components/D3Panel.js index c62d0d9..080642b 100644 --- a/src/components/D3Panel.js +++ b/src/components/D3Panel.js @@ -20,6 +20,7 @@ import RenderChain from '../d3/RenderChain'; import BaseTransformer from '../d3/transformers/BaseTransformer.js'; import Camera from '../d3/Camera.js'; import ReactResizeDetector from 'react-resize-detector'; +import { load as loadMatcapMaterial } from '../d3/MatcapMaterial.js'; // import createDebug from 'debug'; // const debug = createDebug('d3d:d3'); @@ -74,6 +75,7 @@ class D3Panel extends React.Component { plane: this.plane }); + loadMatcapMaterial.then(this.renderRequest); this.DOM = null; } @@ -117,7 +119,7 @@ class D3Panel extends React.Component { const ambientLight = new THREE.AmbientLight(0x505050); this.scene.add(ambientLight); - this.shapesManager = new ShapesManager({ toonShader: hasExtensionsFor.toonShaderPreview }); + this.shapesManager = new ShapesManager(); this.UIContainer = new EventObject3D(); this.UIContainer.matrixAutoUpdate = false; diff --git a/src/components/DoodlePreview.js b/src/components/DoodlePreview.js index fd188e0..cc1652b 100644 --- a/src/components/DoodlePreview.js +++ b/src/components/DoodlePreview.js @@ -8,6 +8,7 @@ import createScene from '../d3/createScene.js'; import injectSheet from 'react-jss'; import ReactResizeDetector from 'react-resize-detector'; import requestAnimationFrame from 'raf'; +import { load as loadMatcapMaterial } from '../d3/MatcapMaterial.js'; const styles = { container: { @@ -55,7 +56,9 @@ class DoodlePreview extends React.Component { this.setState(scene); this.editorControls = new THREE.EditorControls(scene.camera, canvas); - this.editorControls.addEventListener('change', () => scene.render()); + this.editorControls.addEventListener('change', scene.render); + + loadMatcapMaterial.then(scene.render; } componentWillUnmount() { diff --git a/src/d3/MatcapMaterial.js b/src/d3/MatcapMaterial.js new file mode 100644 index 0000000..3f0eb54 --- /dev/null +++ b/src/d3/MatcapMaterial.js @@ -0,0 +1,44 @@ +import * as THREE from 'three'; +import matcapVert from '../../shaders/matcap_vert.glsl'; +import matcapFrag from '../../shaders/matcap_frag.glsl'; +import matcapURL from '../../img/matcap.png'; + +let matcapTexture; +export const load = new Promise((resolve, reject) => { + matcapTexture = new THREE.TextureLoader().load(matcapURL, resolve, () => {}, reject); +}); + +export default class MatcapMaterial extends THREE.ShaderMaterial { + constructor({ color = new THREE.Color(), opacity = 1 }) { + super({ + uniforms: { + "opacity": { type: 'f', value: opacity }, + "tMatcap": { type: 't', value: matcapTexture }, + "color": { type: 'vec3', value: new THREE.Vector3() } + }, + vertexShader: matcapVert, + fragmentShader: matcapFrag + }); + + this.color = color; + this.side = THREE.DoubleSide; + } + + set color(color) { + this.uniforms.color.value.fromArray(color.toArray()); + return color; + } + + get color() { + return new THREE.Color().fromArray(this.uniforms.color.value.toArray()); + } + + set opacity(opacity) { + if (!this.uniforms) return opacity; + return this.uniforms.opacity.value = opacity; + } + + clone() { + return new MatcapMaterial({ color: this.color, opacity: this.uniforms.opacity.value }); + } +} diff --git a/src/d3/ShapeMesh.js b/src/d3/ShapeMesh.js index 5e51566..afae6a2 100644 --- a/src/d3/ShapeMesh.js +++ b/src/d3/ShapeMesh.js @@ -3,6 +3,7 @@ import { applyMatrixOnPath } from '../utils/vectorUtils.js'; import { shapeToPointsCornered } from '../shape/shapeToPoints.js'; import * as THREE from 'three'; import { getPointsBounds, shapeChanged } from '../shape/shapeDataUtils.js'; +import MatcapMaterial from './MatcapMaterial.js'; import { DESELECT_TRANSPARENCY, LEGACY_HEIGHT_STEP } from '../constants/d3Constants.js'; import ThreeBSP from 'three-js-csg'; @@ -16,32 +17,18 @@ const MAX_HEIGHT_BASE = 5; const isValidNumber = (num) => typeof num === 'number' && !isNaN(num); class ShapeMesh extends THREE.Object3D { - constructor(shapeData, active, toonShader) { + constructor(shapeData, active) { super(); this.name = shapeData.UID; const { sculpt, rotate, twist, height, type, transform, z, color, fill, solid } = shapeData; - let material; - if (toonShader) { - material = new THREE.MeshToonMaterial({ - color: new THREE.Color(color), - shading: THREE.SmoothShading, - side: THREE.DoubleSide - }); - } else { - material = new THREE.MeshLambertMaterial({ - color: new THREE.Color(color), - side: THREE.DoubleSide - }); - } + const material = new MatcapMaterial({ color: new THREE.Color(color) }); this._mesh = new THREE.Mesh(new THREE.BufferGeometry(), material.clone()); this._mesh.name = shapeData.UID; this._mesh.isShapeMesh = true; - this._toonShader = toonShader; - this._shapes = []; this._shapesMap = []; @@ -221,7 +208,8 @@ class ShapeMesh extends THREE.Object3D { throw new Error(`Cannot update object ${this.name}: color is an invalid value.`); } - this._holeMesh.material.color.setHex(color); + this._mesh.material.color = new THREE.Color(color); + this._holeMesh.material.color = new THREE.Color(color); this._color = color; } diff --git a/src/d3/ShapesManager.js b/src/d3/ShapesManager.js index 2823181..4953e20 100644 --- a/src/d3/ShapesManager.js +++ b/src/d3/ShapesManager.js @@ -7,11 +7,9 @@ import ThreeBSP from 'three-js-csg'; const THREE_BSP = ThreeBSP(THREE); export default class ShapesManager extends THREE.Object3D { - constructor({ toonShader }) { + constructor() { super(); - this._toonShader = toonShader; - this._meshes = {}; this._spaces = {}; this.name = 'shapes-manager'; @@ -137,7 +135,7 @@ export default class ShapesManager extends THREE.Object3D { _handleShapeAdded(shapeData, active) { if (!SHAPE_TYPE_PROPERTIES[shapeData.type].D3Visible) return; const { space } = shapeData; - const mesh = new ShapeMesh(shapeData, active, this._toonShader); + const mesh = new ShapeMesh(shapeData, active); this._meshes[shapeData.UID] = { mesh, space }; this._spaces[space].add(mesh);