mirror of
https://github.com/tengge1/ShadowEditor.git
synced 2026-01-25 15:08:11 +00:00
commit
ac4d7bf1bb
@ -26,7 +26,7 @@
|
||||
## v0.1.1 即将更新
|
||||
|
||||
1. 修复mmd动画和音频不同步问题。支持多个mmd模型与模型动画、相机动画同步。
|
||||
2. 新增点阵化特效、颜色偏移特效、残影特效、背景虚化、快速近似抗锯齿(FXAA)、毛刺特效。
|
||||
2. 新增点阵化特效、颜色偏移特效、残影特效、背景虚化、快速近似抗锯齿(FXAA)、毛刺特效、半色调特效。
|
||||
3. 新增粒子、预设体、角色面板。(暂未实现具体功能)
|
||||
|
||||
## 主要功能
|
||||
@ -58,6 +58,8 @@
|
||||
|
||||

|
||||
|
||||
[点击此处](images/README.md)查看更多截图。
|
||||
|
||||
## 相关链接
|
||||
|
||||
* Three.js官网:https://threejs.org/
|
||||
|
||||
@ -46,6 +46,9 @@
|
||||
<script src="assets/js/shaders/AfterimageShader.js"></script>
|
||||
<script src="assets/js/shaders/BokehShader.js"></script>
|
||||
<script src="assets/js/shaders/DigitalGlitch.js"></script>
|
||||
<script src="assets/js/shaders/HalftoneShader.js"></script>
|
||||
<script src="assets/js/shaders/DepthLimitedBlurShader.js"></script>
|
||||
<script src="assets/js/shaders/UnpackDepthRGBAShader.js"></script>
|
||||
|
||||
<!-- postprocessing -->
|
||||
<script src="assets/js/postprocessing/EffectComposer.js"></script>
|
||||
@ -56,6 +59,7 @@
|
||||
<script src="assets/js/postprocessing/BokehPass.js"></script>
|
||||
<script src="assets/js/postprocessing/OutlinePass.js"></script>
|
||||
<script src="assets/js/postprocessing/GlitchPass.js"></script>
|
||||
<script src="assets/js/postprocessing/HalftonePass.js"></script>
|
||||
|
||||
<!-- controls -->
|
||||
<script src="assets/js/controls/FirstPersonControls.js"></script>
|
||||
|
||||
@ -113,6 +113,8 @@ AfterimageComponent.prototype.onChange = function () {
|
||||
damp: damp.getValue()
|
||||
},
|
||||
});
|
||||
|
||||
this.app.call(`postProcessingChanged`, this);
|
||||
};
|
||||
|
||||
export default AfterimageComponent;
|
||||
@ -145,6 +145,8 @@ BokehComponent.prototype.onChange = function () {
|
||||
maxBlur: maxBlur.getValue(),
|
||||
},
|
||||
});
|
||||
|
||||
this.app.call(`postProcessingChanged`, this);
|
||||
};
|
||||
|
||||
export default BokehComponent;
|
||||
@ -113,6 +113,8 @@ DotScreenComponent.prototype.onChange = function () {
|
||||
scale: scale.getValue(),
|
||||
},
|
||||
});
|
||||
|
||||
this.app.call(`postProcessingChanged`, this);
|
||||
};
|
||||
|
||||
export default DotScreenComponent;
|
||||
@ -97,6 +97,8 @@ FxaaComponent.prototype.onChange = function () {
|
||||
enabled: enabled.getValue(),
|
||||
},
|
||||
});
|
||||
|
||||
this.app.call(`postProcessingChanged`, this);
|
||||
};
|
||||
|
||||
export default FxaaComponent;
|
||||
@ -112,6 +112,8 @@ GlitchComponent.prototype.onChange = function () {
|
||||
wild: wild.getValue()
|
||||
},
|
||||
});
|
||||
|
||||
this.app.call(`postProcessingChanged`, this);
|
||||
};
|
||||
|
||||
export default GlitchComponent;
|
||||
@ -0,0 +1,263 @@
|
||||
import BaseComponent from '../BaseComponent';
|
||||
|
||||
/**
|
||||
* 半色调特效组件
|
||||
* @author tengge / https://github.com/tengge1
|
||||
* @param {*} options
|
||||
*/
|
||||
function HalftoneComponent(options) {
|
||||
BaseComponent.call(this, options);
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
HalftoneComponent.prototype = Object.create(BaseComponent.prototype);
|
||||
HalftoneComponent.prototype.constructor = HalftoneComponent;
|
||||
|
||||
HalftoneComponent.prototype.render = function () {
|
||||
var data = {
|
||||
xtype: 'div',
|
||||
id: 'panel',
|
||||
scope: this.id,
|
||||
parent: this.parent,
|
||||
cls: 'Panel',
|
||||
style: {
|
||||
display: 'none'
|
||||
},
|
||||
children: [{
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
style: {
|
||||
color: '#555',
|
||||
fontWeight: 'bold',
|
||||
width: '100%'
|
||||
},
|
||||
text: '半色调特效'
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '启用状态'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'enabled',
|
||||
scope: this.id,
|
||||
value: false,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '形状'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'shape',
|
||||
scope: this.id,
|
||||
options: {
|
||||
1: '点',
|
||||
2: '椭圆',
|
||||
3: '线',
|
||||
4: '正方形'
|
||||
},
|
||||
value: 1,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '半径'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'radius',
|
||||
scope: this.id,
|
||||
range: [1, 25],
|
||||
value: 4,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '红色偏转'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'rotateR',
|
||||
scope: this.id,
|
||||
value: 15,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '绿色偏转'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'rotateG',
|
||||
scope: this.id,
|
||||
value: 45,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '蓝色偏转'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'rotateB',
|
||||
scope: this.id,
|
||||
value: 30,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '分散'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'scatter',
|
||||
scope: this.id,
|
||||
value: 0,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '混合'
|
||||
}, {
|
||||
xtype: 'number',
|
||||
id: 'blending',
|
||||
scope: this.id,
|
||||
range: [0, 1],
|
||||
value: 1,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '混合模式'
|
||||
}, {
|
||||
xtype: 'select',
|
||||
id: 'blendingMode',
|
||||
scope: this.id,
|
||||
options: {
|
||||
1: '线性',
|
||||
2: '乘法',
|
||||
3: '相加',
|
||||
4: '变亮',
|
||||
5: '变暗'
|
||||
},
|
||||
value: 1,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}, {
|
||||
xtype: 'row',
|
||||
children: [{
|
||||
xtype: 'label',
|
||||
text: '灰阶'
|
||||
}, {
|
||||
xtype: 'checkbox',
|
||||
id: 'greyscale',
|
||||
scope: this.id,
|
||||
value: false,
|
||||
onChange: this.onChange.bind(this)
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
var control = UI.create(data);
|
||||
control.render();
|
||||
|
||||
this.app.on(`objectSelected.${this.id}`, this.onObjectSelected.bind(this));
|
||||
this.app.on(`objectChanged.${this.id}`, this.onObjectChanged.bind(this));
|
||||
};
|
||||
|
||||
HalftoneComponent.prototype.onObjectSelected = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
HalftoneComponent.prototype.onObjectChanged = function () {
|
||||
this.updateUI();
|
||||
};
|
||||
|
||||
HalftoneComponent.prototype.updateUI = function () {
|
||||
var container = UI.get('panel', this.id);
|
||||
var editor = this.app.editor;
|
||||
if (editor.selected && editor.selected instanceof THREE.Scene) {
|
||||
container.dom.style.display = '';
|
||||
} else {
|
||||
container.dom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = editor.selected;
|
||||
|
||||
var enabled = UI.get('enabled', this.id);
|
||||
var shape = UI.get('shape', this.id);
|
||||
var radius = UI.get('radius', this.id);
|
||||
var rotateR = UI.get('rotateR', this.id);
|
||||
var rotateB = UI.get('rotateB', this.id);
|
||||
var rotateG = UI.get('rotateG', this.id);
|
||||
var scatter = UI.get('scatter', this.id);
|
||||
var blending = UI.get('blending', this.id);
|
||||
var blendingMode = UI.get('blendingMode', this.id);
|
||||
var greyscale = UI.get('greyscale', this.id);
|
||||
|
||||
var scene = this.selected;
|
||||
var postProcessing = scene.userData.postProcessing || {};
|
||||
|
||||
if (postProcessing.halftone) {
|
||||
enabled.setValue(postProcessing.halftone.enabled);
|
||||
shape.setValue(postProcessing.halftone.shape);
|
||||
radius.setValue(postProcessing.halftone.radius);
|
||||
rotateR.setValue(postProcessing.halftone.rotateR);
|
||||
rotateB.setValue(postProcessing.halftone.rotateB);
|
||||
rotateG.setValue(postProcessing.halftone.rotateG);
|
||||
scatter.setValue(postProcessing.halftone.scatter);
|
||||
blending.setValue(postProcessing.halftone.blending);
|
||||
blendingMode.setValue(postProcessing.halftone.blendingMode);
|
||||
greyscale.setValue(postProcessing.halftone.greyscale);
|
||||
}
|
||||
};
|
||||
|
||||
HalftoneComponent.prototype.onChange = function () {
|
||||
var enabled = UI.get('enabled', this.id);
|
||||
var shape = UI.get('shape', this.id);
|
||||
var radius = UI.get('radius', this.id);
|
||||
var rotateR = UI.get('rotateR', this.id);
|
||||
var rotateB = UI.get('rotateB', this.id);
|
||||
var rotateG = UI.get('rotateG', this.id);
|
||||
var scatter = UI.get('scatter', this.id);
|
||||
var blending = UI.get('blending', this.id);
|
||||
var blendingMode = UI.get('blendingMode', this.id);
|
||||
var greyscale = UI.get('greyscale', this.id);
|
||||
|
||||
var scene = this.selected;
|
||||
scene.userData.postProcessing = scene.userData.postProcessing || {};
|
||||
|
||||
Object.assign(scene.userData.postProcessing, {
|
||||
halftone: {
|
||||
enabled: enabled.getValue(),
|
||||
shape: shape.getValue(),
|
||||
radius: radius.getValue(),
|
||||
rotateR: rotateR.getValue(),
|
||||
rotateB: rotateB.getValue(),
|
||||
rotateG: rotateG.getValue(),
|
||||
scatter: scatter.getValue(),
|
||||
blending: blending.getValue(),
|
||||
blendingMode: blendingMode.getValue(),
|
||||
greyscale: greyscale.getValue()
|
||||
},
|
||||
});
|
||||
|
||||
this.app.call(`postProcessingChanged`, this);
|
||||
};
|
||||
|
||||
export default HalftoneComponent;
|
||||
@ -113,6 +113,8 @@ RgbShiftComponent.prototype.onChange = function () {
|
||||
amount: amount.getValue()
|
||||
},
|
||||
});
|
||||
|
||||
this.app.call(`postProcessingChanged`, this);
|
||||
};
|
||||
|
||||
export default RgbShiftComponent;
|
||||
@ -33,6 +33,7 @@ import AfterimageComponent from '../../component/postProcessing/AfterimageCompon
|
||||
import BokehComponent from '../../component/postProcessing/BokehComponent';
|
||||
import FxaaComponent from '../../component/postProcessing/FxaaComponent';
|
||||
import GlitchComponent from '../../component/postProcessing/GlitchComponent';
|
||||
import HalftoneComponent from '../../component/postProcessing/HalftoneComponent';
|
||||
|
||||
/**
|
||||
* 属性面板
|
||||
@ -71,6 +72,7 @@ PropertyPanel.prototype.render = function () {
|
||||
new BokehComponent({ app: this.app }),
|
||||
new FxaaComponent({ app: this.app }),
|
||||
new GlitchComponent({ app: this.app }),
|
||||
new HalftoneComponent({ app: this.app }),
|
||||
new SkyComponent({ app: this.app }),
|
||||
new PerlinTerrainComponent({ app: this.app }),
|
||||
new AudioListenerComponent({ app: this.app }),
|
||||
|
||||
@ -10,6 +10,7 @@ function OutlineEffect(app) {
|
||||
|
||||
this.init();
|
||||
this.app.on(`sceneLoaded.${this.id}`, this.onSceneLoaded.bind(this));
|
||||
this.app.on(`postProcessingChanged.${this.id}`, this.onPostProcessingChanged.bind(this));
|
||||
};
|
||||
|
||||
OutlineEffect.prototype = Object.create(BaseEffect.prototype);
|
||||
@ -36,39 +37,107 @@ OutlineEffect.prototype.init = function () {
|
||||
}
|
||||
|
||||
var composer = new THREE.EffectComposer(renderer);
|
||||
composer.passes.length = 0;
|
||||
|
||||
var renderPass1 = new THREE.RenderPass(scene, camera);
|
||||
renderPass1.clear = true;
|
||||
composer.addPass(renderPass1);
|
||||
var effects = [];
|
||||
var postProcessing = this.app.editor.scene.userData.postProcessing || {};
|
||||
|
||||
var renderPass2 = new THREE.RenderPass(sceneHelpers, camera);
|
||||
renderPass2.clear = false;
|
||||
composer.addPass(renderPass2);
|
||||
var effect = new THREE.RenderPass(scene, camera);
|
||||
effect.clear = true;
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
|
||||
var outlinePass = new THREE.OutlinePass(new THREE.Vector2(renderer.domElement.width, renderer.domElement.height), scene, camera);
|
||||
outlinePass.edgeStrength = params.edgeStrength;
|
||||
outlinePass.edgeGlow = params.edgeGlow;
|
||||
outlinePass.edgeThickness = params.edgeThickness;
|
||||
outlinePass.pulsePeriod = params.pulsePeriod;
|
||||
// outlinePass.usePatternTexture = true;
|
||||
outlinePass.visibleEdgeColor.set(params.visibleEdgeColor);
|
||||
outlinePass.hiddenEdgeColor.set(params.hiddenEdgeColor);
|
||||
composer.addPass(outlinePass);
|
||||
effect = new THREE.RenderPass(sceneHelpers, camera);
|
||||
effect.clear = false;
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
|
||||
var loader = new THREE.TextureLoader();
|
||||
effect = new THREE.OutlinePass(new THREE.Vector2(renderer.domElement.width, renderer.domElement.height), scene, camera);
|
||||
effect.edgeStrength = 10;
|
||||
effect.edgeGlow = 0.4;
|
||||
effect.edgeThickness = 1.8;
|
||||
effect.pulsePeriod = 2;
|
||||
effect.visibleEdgeColor.set('#ffffff');
|
||||
effect.hiddenEdgeColor.set('#190a05');
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
|
||||
// loader.load('assets/textures/tri_pattern.jpg', texture => {
|
||||
// outlinePass.patternTexture = texture;
|
||||
// texture.wrapS = THREE.RepeatWrapping;
|
||||
// texture.wrapT = THREE.RepeatWrapping;
|
||||
// });
|
||||
this.outlinePass = effect;
|
||||
|
||||
var effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
|
||||
effectFXAA.uniforms['resolution'].value.set(1 / renderer.domElement.width, 1 / renderer.domElement.height);
|
||||
effectFXAA.renderToScreen = true;
|
||||
composer.addPass(effectFXAA);
|
||||
// 后期处理
|
||||
if (postProcessing.fxaa && postProcessing.fxaa.enabled) {
|
||||
effect = new THREE.ShaderPass(THREE.FXAAShader);
|
||||
effect.uniforms['resolution'].value.set(1 / renderer.domElement.width, 1 / renderer.domElement.height);
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
if (postProcessing.dotScreen && postProcessing.dotScreen.enabled) {
|
||||
effect = new THREE.ShaderPass(THREE.DotScreenShader);
|
||||
effect.uniforms['scale'].value = postProcessing.dotScreen.scale;
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
if (postProcessing.rgbShift && postProcessing.rgbShift.enabled) {
|
||||
effect = new THREE.ShaderPass(THREE.RGBShiftShader);
|
||||
effect.uniforms['amount'].value = postProcessing.rgbShift.amount;
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
if (postProcessing.afterimage && postProcessing.afterimage.enabled) {
|
||||
effect = new THREE.AfterimagePass();
|
||||
effect.uniforms['damp'].value = postProcessing.afterimage.damp;
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
if (postProcessing.halftone && postProcessing.halftone.enabled) {
|
||||
effect = new THREE.HalftonePass(
|
||||
renderer.domElement.width,
|
||||
renderer.domElement.height, {
|
||||
shape: postProcessing.halftone.shape,
|
||||
radius: postProcessing.halftone.radius,
|
||||
rotateR: postProcessing.halftone.rotateR * (Math.PI / 180),
|
||||
rotateB: postProcessing.halftone.rotateB * (Math.PI / 180),
|
||||
rotateG: postProcessing.halftone.rotateG * (Math.PI / 180),
|
||||
scatter: postProcessing.halftone.scatter,
|
||||
blending: postProcessing.halftone.blending,
|
||||
blendingMode: postProcessing.halftone.blendingMode,
|
||||
greyscale: postProcessing.halftone.greyscale,
|
||||
});
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
if (postProcessing.bokeh && postProcessing.bokeh.enabled) {
|
||||
effect = new THREE.BokehPass(scene, camera, {
|
||||
focus: postProcessing.bokeh.focus,
|
||||
aperture: postProcessing.bokeh.aperture / 100000,
|
||||
maxblur: postProcessing.bokeh.maxBlur,
|
||||
width: renderer.domElement.width,
|
||||
height: renderer.domElement.height
|
||||
});
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
if (postProcessing.glitch && postProcessing.glitch.enabled) {
|
||||
effect = new THREE.GlitchPass();
|
||||
effect.goWild = postProcessing.glitch.wild;
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
for (var i = 0; i < effects.length; i++) {
|
||||
if (i === effects.length - 1) {
|
||||
effects[i].renderToScreen = true;
|
||||
} else {
|
||||
effects[i].renderToScreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.outlinePass = outlinePass;
|
||||
this.composer = composer;
|
||||
};
|
||||
|
||||
@ -86,4 +155,8 @@ OutlineEffect.prototype.onSceneLoaded = function () {
|
||||
this.init();
|
||||
};
|
||||
|
||||
OutlineEffect.prototype.onPostProcessingChanged = function () {
|
||||
this.init();
|
||||
};
|
||||
|
||||
export default OutlineEffect;
|
||||
@ -72,6 +72,7 @@ var EventList = [
|
||||
'refreshScriptEditor', // 刷新脚本编辑器事件
|
||||
|
||||
'sceneLoaded', // 场景载入
|
||||
'postProcessingChanged', // 后期处理设置改变
|
||||
|
||||
// 场景编辑区
|
||||
'transformControlsChange', // 变形控件改变
|
||||
|
||||
@ -55,6 +55,24 @@ PlayerRenderer.prototype.create = function (scene, camera, renderer) {
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
if (postProcessing.halftone && postProcessing.halftone.enabled) {
|
||||
effect = new THREE.HalftonePass(
|
||||
renderer.domElement.width,
|
||||
renderer.domElement.height, {
|
||||
shape: postProcessing.halftone.shape,
|
||||
radius: postProcessing.halftone.radius,
|
||||
rotateR: postProcessing.halftone.rotateR * (Math.PI / 180),
|
||||
rotateB: postProcessing.halftone.rotateB * (Math.PI / 180),
|
||||
rotateG: postProcessing.halftone.rotateG * (Math.PI / 180),
|
||||
scatter: postProcessing.halftone.scatter,
|
||||
blending: postProcessing.halftone.blending,
|
||||
blendingMode: postProcessing.halftone.blendingMode,
|
||||
greyscale: postProcessing.halftone.greyscale,
|
||||
});
|
||||
composer.addPass(effect);
|
||||
effects.push(effect);
|
||||
}
|
||||
|
||||
if (postProcessing.bokeh && postProcessing.bokeh.enabled) {
|
||||
effect = new THREE.BokehPass(scene, camera, {
|
||||
focus: postProcessing.bokeh.focus,
|
||||
|
||||
11
images/README.md
Normal file
11
images/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# 更多截图
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 854 KiB After Width: | Height: | Size: 854 KiB |
|
Before Width: | Height: | Size: 1021 KiB After Width: | Height: | Size: 1021 KiB |
Loading…
x
Reference in New Issue
Block a user