update render logic

This commit is contained in:
casperlamboo 2017-11-23 13:17:44 +01:00
parent 8e76f3383c
commit d4e1fd8e55
20 changed files with 277 additions and 325 deletions

15
shaders/combine_frag.glsl Normal file
View File

@ -0,0 +1,15 @@
varying vec2 vUv;
uniform sampler2D tDiffuse;
uniform sampler2D uTexArray[1];
void main() {
vec4 cDiffuse = texture2D(tDiffuse, vUv);
for (int i = 0; i < 1; i ++) {
vec4 cTex = texture2D(uTexArray[i], vUv);
cDiffuse = mix(cDiffuse, cTex, cTex.w);
// cDiffuse = cTex;
}
gl_FragColor = cDiffuse;
}

38
shaders/edge_frag.glsl Normal file
View File

@ -0,0 +1,38 @@
uniform sampler2D tDiffuse;
uniform vec3 color;
uniform vec2 resolution;
varying vec2 vUv;
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1., 2. / 3., 1. / 3., 3.);
vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0., 1.), c.y);
}
// convert depth to hue value so we can detect edges
vec3 depth(vec4 c) {
return hsv2rgb(vec3(c.z, 1., 1.)) * c.w;
}
float cubicInOut(float t) {
return t < .5 ? 4. * pow(t, 3.) : .5 * pow(2. * t - 2., 3.) + 1.;
}
void main() {
float x = 1. / resolution.x;
float y = 1. / resolution.y;
// lapace filter
vec4 f01 = texture2D(tDiffuse, vUv + vec2(0., -y));
vec4 f10 = texture2D(tDiffuse, vUv + vec2(-x, 0.));
vec4 f11 = texture2D(tDiffuse, vUv);
vec4 f12 = texture2D(tDiffuse, vUv + vec2(0., y));
vec4 f21 = texture2D(tDiffuse, vUv + vec2(x, 0.));
float depthLaplace = length(4. * depth(f11) - depth(f01) - depth(f10) - depth(f12) - depth(f21));
float normalLaplace = length(4. * f11.xy - f01.xy - f10.xy - f12.xy - f21.xy);
float edge = cubicInOut(depthLaplace) + cubicInOut(normalLaplace);
gl_FragColor = vec4(vec3(0.), edge);
}

6
shaders/edge_vert.glsl Normal file
View File

@ -0,0 +1,6 @@
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

View File

@ -1,16 +0,0 @@
uniform sampler2D tDiffuse;
uniform sampler2D tNormal;
uniform sampler2D tDepth;
uniform sampler2D tUI;
varying vec2 vUv;
void main() {
vec4 colorDiffuse = texture2D(tDiffuse, vUv); // cell shader
vec4 colorNormal = texture2D(tNormal, vUv); // outline from normal texture
colorNormal.w = 0.0;
vec4 colorDepth = texture2D(tDepth, vUv); // outline from depth texture
colorDepth.w = 0.0;
vec4 colorUI = texture2D(tUI, vUv); // color ui's
gl_FragColor = mix(max(colorDiffuse - colorDepth - colorNormal, 0.0), colorUI, colorUI.w);
}

View File

@ -1,14 +0,0 @@
uniform float mNear;
uniform float mFar;
uniform float opacity;
uniform float logDepthBufFC;
varying float vFragDepth;
#include <common>
void main() {
float fragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;
float depth = fragDepthEXT / gl_FragCoord.w;
float color = 1.0 - smoothstep( mNear, mFar, depth );
gl_FragColor = vec4( vec3( color ), opacity );
}

View File

@ -1,26 +0,0 @@
uniform sampler2D tDiffuse;
uniform float threshold;
uniform vec2 aspect;
varying vec2 vUv;
void main() {
float w = (1.0 / aspect.x);
float h = (1.0 / aspect.y);
vec4 n[9];
n[0] = texture2D(tDiffuse, vUv + vec2( -w, -h));
n[1] = texture2D(tDiffuse, vUv + vec2(0.0, -h));
n[2] = texture2D(tDiffuse, vUv + vec2( w, -h));
n[3] = texture2D(tDiffuse, vUv + vec2( -w, 0.0));
n[4] = texture2D(tDiffuse, vUv);
n[5] = texture2D(tDiffuse, vUv + vec2( w, 0.0));
n[6] = texture2D(tDiffuse, vUv + vec2( -w, h));
n[7] = texture2D(tDiffuse, vUv + vec2(0.0, h));
n[8] = texture2D(tDiffuse, vUv + vec2( w, h));
vec4 sobel_horizEdge = n[2] + (2.0 * n[5]) + n[8] - (n[0] + (2.0 * n[3]) + n[6]);
vec4 sobel_vertEdge = n[0] + (2.0 * n[1]) + n[2] - (n[6] + (2.0 * n[7]) + n[8]);
vec3 sobel = sqrt((sobel_horizEdge.rgb * sobel_horizEdge.rgb) + (sobel_vertEdge.rgb * sobel_vertEdge.rgb));
gl_FragColor = (length(sobel) > threshold) ? vec4(vec3(1.0), 0.0) : vec4(0.0);
}

View File

@ -1,21 +0,0 @@
// edge detection based on http://williamchyr.com/tag/unity/page/2/
uniform sampler2D tDiffuse;
uniform float threshold;
uniform vec2 aspect;
varying vec2 vUv;
void main() {
float w = (1.0 / aspect.x);
float h = (1.0 / aspect.y);
vec4 a = texture2D(tDiffuse, vUv);
vec4 b = texture2D(tDiffuse, vUv + vec2(-w, -h));
vec4 c = texture2D(tDiffuse, vUv + vec2(w, -h));
vec4 d = texture2D(tDiffuse, vUv + vec2(-w, h));
vec4 e = texture2D(tDiffuse, vUv + vec2(w, h));
vec4 averageDepth = (b + c + d + e) / 4.0;
float difference = length(averageDepth - a);
gl_FragColor = difference > threshold ? vec4(vec3(1.0), 0.0) : vec4(0.0);
}

View File

@ -1,20 +0,0 @@
// edge detection based on http://williamchyr.com/tag/unity/page/2/
uniform sampler2D tDiffuse;
uniform float threshold;
uniform vec2 aspect;
varying vec2 vUv;
void main() {
float w = (1.0 / aspect.x);
float h = (1.0 / aspect.y);
// vec4 a = texture2D(tDiffuse, vUv);
vec4 b = texture2D(tDiffuse, vUv + vec2(-w, -h));
vec4 c = texture2D(tDiffuse, vUv + vec2(w, -h));
vec4 d = texture2D(tDiffuse, vUv + vec2(-w, h));
vec4 e = texture2D(tDiffuse, vUv + vec2(w, h));
float difference = length(b - e) + length(c - d);
gl_FragColor = difference > threshold ? vec4(vec3(1.0), 0.0) : vec4(0.0);
}

View File

@ -0,0 +1,9 @@
varying vec3 vNormal;
varying float vDepth;
void main() {
vec2 normal = vNormal.xy / 2. + .5;
float depth = mod(vDepth, 1.);
gl_FragColor = vec4(normal, depth, 1.);
}

View File

@ -0,0 +1,10 @@
varying vec3 vNormal;
varying float vDepth;
void main() {
vNormal = normalize(normalMatrix * normal);
vDepth = (projectionMatrix * modelViewMatrix * vec4(position, 1.0)).z / 30.;
#include <begin_vertex>
#include <project_vertex>
}

View File

@ -1,14 +0,0 @@
varying float vFragDepth;
uniform float logDepthBufFC;
#include <common>
#include <morphtarget_pars_vertex>
void main() {
#include <begin_vertex>
#include <morphtarget_vertex>
#include <project_vertex>
// gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;
vFragDepth = 1.0 + gl_Position.w;
}

View File

@ -16,7 +16,6 @@ import TwistTransformer from '../d3/transformers/TwistTransformer.js';
import SculptTransformer from '../d3/transformers/SculptTransformer.js';
import StampTransformer from '../d3/transformers/StampTransformer.js';
import SelectionBox from '../d3/SelectionBox.js';
import ToonShaderRenderChain from '../d3/ToonShaderRenderChain';
import RenderChain from '../d3/RenderChain';
import BaseTransformer from '../d3/transformers/BaseTransformer.js';
import Camera from '../d3/Camera.js';
@ -68,17 +67,12 @@ class D3Panel extends React.Component {
componentWillMount() {
this.createScene();
if (hasExtensionsFor.toonShaderPreview) {
this.renderChain = new ToonShaderRenderChain(this.renderer, this.scene, this.camera, {
UI: this.UIContainer,
shapes: this.shapesManager,
boundingBox: this.selectionBox,
plane: this.plane
});
} else {
this.renderer.setClearColor(0xffffff);
this.renderChain = new RenderChain(this.renderer, this.scene, this.camera);
}
this.renderChain = new RenderChain(this.renderer, this.scene, this.camera, hasExtensionsFor.toonShaderPreview, {
UI: this.UIContainer,
shapes: this.shapesManager,
boundingBox: this.selectionBox,
plane: this.plane
});
this.DOM = null;
}
@ -95,7 +89,7 @@ class D3Panel extends React.Component {
}
createScene() {
this.renderer = new THREE.WebGLRenderer();
this.renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true, preserveDrawingBuffer: true });
this.scene = new EventScene(this.renderer.domElement);
@ -224,7 +218,7 @@ class D3Panel extends React.Component {
resizeHandler = (width, height) => {
// set renderer size
this.renderChain.setSize(width, height, PIXEL_RATIO);
this.renderChain.setSize(width, height, PIXEL_RATIO, false);
this.renderRequest();
};
@ -249,7 +243,7 @@ class D3Panel extends React.Component {
renderScene = () => {
if (this.needRender) {
this.scene.updateMatrixWorld();
this.renderChain.render(true);
this.renderChain.render();
this.needRender = false;
}
};

73
src/d3/OutlinePass.js vendored Normal file
View File

@ -0,0 +1,73 @@
import * as THREE from 'three';
import normalDepthVert from '../../shaders/normal_depth_vert.glsl';
import normalDepthFrag from '../../shaders/normal_depth_frag.glsl';
import edgeVert from '../../shaders/edge_vert.glsl';
import edgeFrag from '../../shaders/edge_frag.glsl';
import combineVert from '../../shaders/combine_vert.glsl';
import combineFrag from '../../shaders/combine_frag.glsl';
export default class OutlinePass {
constructor(scene, camera, callbackBeforeRender) {
this.scene = scene;
this.camera = camera;
this._callbackBeforeRender = callbackBeforeRender;
this.clear = true;
this.renderToScreen = false;
this._depthNormalRenderTarget = new THREE.WebGLRenderTarget();
this._edgeRenderTarget = new THREE.WebGLRenderTarget();
this._normalDepthMateral = new THREE.ShaderMaterial({
vertexShader: normalDepthVert,
fragmentShader: normalDepthFrag
});
this._edgeMaterial = new THREE.ShaderMaterial({
uniforms: {
"tDiffuse": { type: 't', value: this._depthNormalRenderTarget.texture },
"resolution": { type: 'v2', value: new THREE.Vector2() }
},
vertexShader: edgeVert,
fragmentShader: edgeFrag
});
this._copyEdge = new THREE.ShaderMaterial({
uniforms: {
"tDiffuse": { type: 't', value: null },
"uTexArray" : { type: 'tv', value: [this._edgeRenderTarget.texture] }
},
vertexShader: combineVert,
fragmentShader: combineFrag
})
this._camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
this._scene = new THREE.Scene();
this._quad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2));
this._quad.frustumCulled = false;
this._scene.add(this._quad);
}
setSize(width, height) {
this._depthNormalRenderTarget.setSize(width, height);
this._edgeRenderTarget.setSize(width, height);
this._edgeMaterial.uniforms.resolution.value.set(width, height);
}
render(renderer, writeBuffer, readBuffer, delta, maskActive) {
if (this._callbackBeforeRender) this._callbackBeforeRender();
this._copyEdge.uniforms.tDiffuse.value = readBuffer.texture;
this.scene.overrideMaterial = this._normalDepthMateral;
renderer.render(this.scene, this.camera, this._depthNormalRenderTarget, true);
this._scene.overrideMaterial = this._edgeMaterial;
renderer.render(this._scene, this._camera, this._edgeRenderTarget, true);
this._scene.overrideMaterial = this._copyEdge;
renderer.render(this._scene, this._camera, this.renderToScreen ? null : writeBuffer, this.clear);
this.scene.overrideMaterial = null;
this._scene.overrideMaterial = null;
}
}

83
src/d3/RenderChain.js vendored
View File

@ -1,19 +1,88 @@
export default class RenderChain {
constructor(renderer, scene, camera) {
import * as THREE from 'three';
import OutlinePass from './OutlinePass.js';
import RenderPass from './RenderPass.js';
import 'three/examples/js/shaders/CopyShader.js';
import 'three/examples/js/postprocessing/EffectComposer.js';
import 'three/examples/js/postprocessing/ShaderPass.js';
export default class RenderChain extends THREE.EffectComposer {
constructor(renderer, scene, camera, toonShader, groups) {
super(renderer);
this._groups = groups;
if (toonShader) {
const renderPass = new RenderPass(scene, camera, () => {
this._setVisible(this._initalValues, [groups.shapes, groups.plane, groups.boundingBox]);
});
this.addPass(renderPass);
const outlinePass = new OutlinePass(scene, camera, () => {
this._setVisible(this._initalValues, [groups.shapes]);
});
outlinePass.renderToScreen = true;
this.addPass(outlinePass);
const renderPassUI = new RenderPass(scene, camera, () => {
this._setVisible(this._initalValues, [groups.UI]);
});
renderPassUI.clear = false;
renderPassUI.renderToScreen = true;
this.addPass(renderPassUI);
} else {
const renderPass = new RenderPass(scene, camera);
renderPass.renderToScreen = true;
this.addPass(renderPass);
}
this._renderer = renderer;
this._scene = scene;
this._camera = camera;
this._scene = scene;
this._toonShader = toonShader;
}
setSize(width, height) {
this._renderer.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;
}
}
}
setSize(width, height, pixelRatio, rerender = true) {
this._renderer.setPixelRatio(pixelRatio);
this._renderer.setSize(width, height);
super.setSize(width * pixelRatio, height * pixelRatio);
// adjust aspect ratio of camera
this._camera.aspect = width / height;
this._camera.updateProjectionMatrix();
if (rerender) this.render();
}
render() {
this._renderer.render(this._scene, this._camera);
if (this._toonShader) {
this._initalValues = this._getCurrentVisibleValues();
}
super.render();
if (this._toonShader) {
const { shapes, UI, plane, boundingBox } = this._groups;
this._setVisible(this._initalValues, [shapes, UI, plane, boundingBox]);
}
}
}

31
src/d3/RenderPass.js vendored Normal file
View File

@ -0,0 +1,31 @@
import * as THREE from 'three';
export default class RenderPass {
constructor(scene, camera, callbackBeforeRender) {
this.scene = scene;
this.camera = camera;
this._callbackBeforeRender = callbackBeforeRender;
this.clear = true;
this.renderToScreen = false;
}
setSize(width, height) {
}
render(renderer, writeBuffer, readBuffer, delta, maskActive) {
if (this._callbackBeforeRender) this._callbackBeforeRender();
let oldAutoClear;
if (this.clear === false) {
oldAutoClear = renderer.autoClear;
renderer.autoClear = false;
}
renderer.render(this.scene, this.camera, this.renderToScreen ? null : readBuffer, this.clear);
if (this.clear === false) {
renderer.autoClear = oldAutoClear;
}
}
}

View File

@ -1,164 +0,0 @@
import * as THREE from 'three';
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';
import vertexShaderPostprocessing from '../../shaders/vertexShaderPostprocessing.glsl';
import fragmentShaderSobelDepth from '../../shaders/fragmentShaderSobelDepth.glsl';
import fragmentShaderSobelNormal from '../../shaders/fragmentShaderSobelNormal.glsl';
import fragmentShaderCombineTextures from '../../shaders/fragmentShaderCombineTextures.glsl';
import fragmentShaderDepth from '../../shaders/fragmentShaderDepth.glsl';
import vertexShaderDepth from '../../shaders/vertexShaderDepth.glsl';
// 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]);
}
}

36
src/d3/createScene.js vendored
View File

@ -1,6 +1,6 @@
import * as THREE from 'three';
import ShapesManager from './ShapesManager.js';
import ToonShaderRenderChain from './ToonShaderRenderChain.js';
import RenderChain from './RenderChain.js';
import { hasExtensionsFor } from '../utils/webGLSupport.js';
import { CANVAS_SIZE } from '../constants/d2Constants.js';
@ -43,32 +43,14 @@ export default function createScene(state, canvas) {
const light = new THREE.AmbientLight(0x505050);
scene.add(light);
let render;
let setSizeRenderer;
if (hasExtensionsFor.toonShaderThumbnail) {
const renderChain = new ToonShaderRenderChain(renderer, scene, camera, {
plane,
UI: new THREE.Object3D(),
shapes: shapesManager,
boundingBox: new THREE.Object3D()
});
setSizeRenderer = renderChain.setSize.bind(renderChain);
render = renderChain.render.bind(renderChain);
} else {
renderer.setClearColor(0xffffff);
setSizeRenderer = renderer.setSize.bind(renderer);
render = renderer.render.bind(renderer, scene, camera);
}
const setSize = (width, height, pixelRatio) => {
setSizeRenderer(width, height, pixelRatio);
camera.aspect = width / height;
camera.updateProjectionMatrix();
render();
};
const renderChain = new RenderChain(renderer, scene, camera, hasExtensionsFor.toonShaderThumbnail, {
plane,
UI: new THREE.Object3D(),
shapes: shapesManager,
boundingBox: new THREE.Object3D()
});
const setSize = renderChain.setSize.bind(renderChain);
const render = renderChain.render.bind(renderChain);
return { scene, camera, renderer, render, setSize };
}

1
src/d3/index.js vendored
View File

@ -1,7 +1,6 @@
import createSceneData from './createSceneData.js';
import createScene from './createScene.js';
import RenderChain from './RenderChain.js';
import ToonShaderRenderChain from './ToonShaderRenderChain.js';
import ShapeMesh from './ShapeMesh.js';
import ShapesManager from './ShapesManager.js';

View File

@ -69,6 +69,7 @@ export class CanvasPlane extends THREE.Mesh {
const texture = new THREE.Texture(canvas);
const material = new THREE.MeshBasicMaterial({
map: texture,
depthTest: false,
side: THREE.DoubleSide
});