|
@@ -4,78 +4,10 @@
|
|
<title>three.js webgl - materials - custom blending</title>
|
|
<title>three.js webgl - materials - custom blending</title>
|
|
<meta charset="utf-8">
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
- <style>
|
|
|
|
- body {
|
|
|
|
- margin: 0px;
|
|
|
|
- background-color: #111;
|
|
|
|
- overflow: hidden;
|
|
|
|
- font-family: arial;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- .menu { color: #fff; font-weight: bold; font-size: 12px; z-index: 100; width: 75px; position: absolute; top: 0px; padding: 16px; }
|
|
|
|
- .menu img, .menu canvas { width: 75px; margin: 10px 0 }
|
|
|
|
-
|
|
|
|
- #images { background: rgba(0,0,0,0); left: 0px; }
|
|
|
|
- #backgrounds { background: rgba(0,0,0,0.0); left: 107px; }
|
|
|
|
- #labels { background: rgba(0,0,0,0.75); left: 214px; width: 100px }
|
|
|
|
-
|
|
|
|
- .lbl { color: #fff; z-index: 150; float:left; padding: 0.25em; width: 75px; display: block }
|
|
|
|
- #lbl_dst { background:#800; }
|
|
|
|
- #lbl_src { background:green; }
|
|
|
|
-
|
|
|
|
- .btn { background: darkorange; width: 100px; cursor: pointer }
|
|
|
|
-
|
|
|
|
- #btn_sub { background: transparent }
|
|
|
|
- #btn_rsub { background: transparent }
|
|
|
|
- #btn_min { background: transparent }
|
|
|
|
- #btn_max { background: transparent }
|
|
|
|
-
|
|
|
|
- #btn_pre { background: transparent }
|
|
|
|
-
|
|
|
|
- #btn_max, #btn_nopre { margin-bottom: 2em }
|
|
|
|
- </style>
|
|
|
|
|
|
+ <link type="text/css" rel="stylesheet" href="main.css">
|
|
</head>
|
|
</head>
|
|
<body>
|
|
<body>
|
|
|
|
|
|
- <div id="images" class="menu">
|
|
|
|
- Foreground
|
|
|
|
- <a id="img_0" href="#"><img src="textures/disturb.jpg" /></a>
|
|
|
|
- <a id="img_1" href="#"><img src="textures/sprite0.jpg" /></a>
|
|
|
|
- <a id="img_2" href="#"><img src="textures/sprite0.png" /></a>
|
|
|
|
- <a id="img_3" href="#"><img src="textures/lensflare/lensflare0.png" /></a>
|
|
|
|
- <a id="img_4" href="#"><img src="textures/lensflare/lensflare0_alpha.png" /></a>
|
|
|
|
- <a id="img_5" href="#"><img src="textures/sprites/ball.png" /></a>
|
|
|
|
- <a id="img_6" href="#"><img src="textures/sprites/snowflake7_alpha.png" /></a>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div id="backgrounds" class="menu">
|
|
|
|
- Background
|
|
|
|
- <a id="bg_0" href="#"><img src="textures/disturb.jpg" /></a>
|
|
|
|
- <a id="bg_1" href="#"></a>
|
|
|
|
- <a id="bg_2" href="#"></a>
|
|
|
|
- <a id="bg_3" href="#"><img src="textures/crate.gif" /></a>
|
|
|
|
- <a id="bg_4" href="#"><img src="textures/lava/lavatile.jpg" /></a>
|
|
|
|
- <a id="bg_5" href="#"><img src="textures/water.jpg" /></a>
|
|
|
|
- <a id="bg_6" href="#"><img src="textures/lava/cloud.png" /></a>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div id="labels" class="menu">
|
|
|
|
- Equation<br/><br/>
|
|
|
|
- <div class="lbl btn" id="btn_add">Add</div>
|
|
|
|
- <div class="lbl btn" id="btn_sub">Subtract</div>
|
|
|
|
- <div class="lbl btn" id="btn_rsub">ReverseSubtract</div>
|
|
|
|
- <div class="lbl btn" id="btn_min">Min</div>
|
|
|
|
- <div class="lbl btn" id="btn_max">Max</div>
|
|
|
|
-
|
|
|
|
- Premultiply alpha<br/><br/>
|
|
|
|
- <div class="lbl btn" id="btn_pre">On</div>
|
|
|
|
- <div class="lbl btn" id="btn_nopre">Off</div>
|
|
|
|
-
|
|
|
|
- Labels<br/><br/>
|
|
|
|
- <div class="lbl" id="lbl_src">Source</div>
|
|
|
|
- <div class="lbl" id="lbl_dst">Destination</div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
<!-- Import maps polyfill -->
|
|
<!-- Import maps polyfill -->
|
|
<!-- Remove this when import maps will be widely supported -->
|
|
<!-- Remove this when import maps will be widely supported -->
|
|
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
|
|
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
|
|
@@ -92,16 +24,18 @@
|
|
|
|
|
|
import * as THREE from 'three';
|
|
import * as THREE from 'three';
|
|
|
|
|
|
|
|
+ import { GUI } from './jsm/libs/lil-gui.module.min.js';
|
|
|
|
+
|
|
let camera, scene, renderer;
|
|
let camera, scene, renderer;
|
|
|
|
|
|
- let materialBg;
|
|
|
|
|
|
+ let mapBg;
|
|
const materials = [];
|
|
const materials = [];
|
|
|
|
|
|
- const mapsPre = [];
|
|
|
|
- const mapsNoPre = [];
|
|
|
|
|
|
+ const params = {
|
|
|
|
+ blendEquation: THREE.AddEquation
|
|
|
|
+ };
|
|
|
|
|
|
- let currentMaps = mapsNoPre;
|
|
|
|
- let currentIndex = 4;
|
|
|
|
|
|
+ const equations = { Add: THREE.AddEquation, Subtract: THREE.SubtractEquation, ReverseSubtract: THREE.ReverseSubtractEquation, Min: THREE.MinEquation, Max: THREE.MaxEquation };
|
|
|
|
|
|
init();
|
|
init();
|
|
animate();
|
|
animate();
|
|
@@ -117,103 +51,30 @@
|
|
|
|
|
|
scene = new THREE.Scene();
|
|
scene = new THREE.Scene();
|
|
|
|
|
|
- // TEXTURE LOADER
|
|
|
|
-
|
|
|
|
- const textureLoader = new THREE.TextureLoader();
|
|
|
|
-
|
|
|
|
- // BACKGROUND IMAGES
|
|
|
|
-
|
|
|
|
- const canvas1 = document.createElement( 'canvas' );
|
|
|
|
- const ctx1 = canvas1.getContext( '2d' );
|
|
|
|
- canvas1.width = canvas1.height = 128;
|
|
|
|
- ctx1.fillStyle = '#eee';
|
|
|
|
- ctx1.fillRect( 0, 0, 128, 128 );
|
|
|
|
- ctx1.fillStyle = '#999';
|
|
|
|
- ctx1.fillRect( 0, 0, 64, 64 );
|
|
|
|
- ctx1.fillStyle = '#aaa';
|
|
|
|
- ctx1.fillRect( 32, 32, 32, 32 );
|
|
|
|
- ctx1.fillStyle = '#999';
|
|
|
|
- ctx1.fillRect( 64, 64, 64, 64 );
|
|
|
|
- ctx1.fillStyle = '#bbb';
|
|
|
|
- ctx1.fillRect( 96, 96, 32, 32 );
|
|
|
|
-
|
|
|
|
- document.getElementById( 'bg_1' ).appendChild( canvas1 );
|
|
|
|
-
|
|
|
|
- const canvas2 = document.createElement( 'canvas' );
|
|
|
|
- const ctx2 = canvas2.getContext( '2d' );
|
|
|
|
- canvas2.width = canvas2.height = 128;
|
|
|
|
- ctx2.fillStyle = '#444';
|
|
|
|
- ctx2.fillRect( 0, 0, 128, 128 );
|
|
|
|
- ctx2.fillStyle = '#000';
|
|
|
|
- ctx2.fillRect( 0, 0, 64, 64 );
|
|
|
|
- ctx2.fillStyle = '#111';
|
|
|
|
- ctx2.fillRect( 32, 32, 32, 32 );
|
|
|
|
- ctx2.fillStyle = '#000';
|
|
|
|
- ctx2.fillRect( 64, 64, 64, 64 );
|
|
|
|
- ctx2.fillStyle = '#222';
|
|
|
|
- ctx2.fillRect( 96, 96, 32, 32 );
|
|
|
|
-
|
|
|
|
- document.getElementById( 'bg_2' ).appendChild( canvas2 );
|
|
|
|
-
|
|
|
|
- const mapBg0 = new THREE.CanvasTexture( canvas1 );
|
|
|
|
- mapBg0.wrapS = mapBg0.wrapT = THREE.RepeatWrapping;
|
|
|
|
- mapBg0.repeat.set( 128, 64 );
|
|
|
|
-
|
|
|
|
- const mapBg1 = new THREE.CanvasTexture( canvas2 );
|
|
|
|
- mapBg1.wrapS = mapBg1.wrapT = THREE.RepeatWrapping;
|
|
|
|
- mapBg1.repeat.set( 128, 64 );
|
|
|
|
-
|
|
|
|
- const mapBg2 = textureLoader.load( 'textures/disturb.jpg' );
|
|
|
|
- mapBg2.wrapS = mapBg2.wrapT = THREE.RepeatWrapping;
|
|
|
|
- mapBg2.repeat.set( 8, 4 );
|
|
|
|
-
|
|
|
|
- const mapBg3 = textureLoader.load( 'textures/crate.gif' );
|
|
|
|
- mapBg3.wrapS = mapBg3.wrapT = THREE.RepeatWrapping;
|
|
|
|
- mapBg3.repeat.set( 32, 16 );
|
|
|
|
-
|
|
|
|
- const mapBg4 = textureLoader.load( 'textures/lava/lavatile.jpg' );
|
|
|
|
- mapBg4.wrapS = mapBg4.wrapT = THREE.RepeatWrapping;
|
|
|
|
- mapBg4.repeat.set( 8, 4 );
|
|
|
|
-
|
|
|
|
- const mapBg5 = textureLoader.load( 'textures/water.jpg' );
|
|
|
|
- mapBg5.wrapS = mapBg5.wrapT = THREE.RepeatWrapping;
|
|
|
|
- mapBg5.repeat.set( 8, 4 );
|
|
|
|
-
|
|
|
|
- const mapBg6 = textureLoader.load( 'textures/lava/cloud.png' );
|
|
|
|
- mapBg6.wrapS = mapBg6.wrapT = THREE.RepeatWrapping;
|
|
|
|
- mapBg6.repeat.set( 2, 1 );
|
|
|
|
-
|
|
|
|
// BACKGROUND
|
|
// BACKGROUND
|
|
|
|
|
|
- materialBg = new THREE.MeshBasicMaterial( { map: mapBg1 } );
|
|
|
|
-
|
|
|
|
- const meshBg = new THREE.Mesh( new THREE.PlaneGeometry( 4000, 2000 ), materialBg );
|
|
|
|
- meshBg.position.set( 0, 0, - 1 );
|
|
|
|
- scene.add( meshBg );
|
|
|
|
-
|
|
|
|
- // FOREGROUND IMAGES
|
|
|
|
-
|
|
|
|
- const images = [ 'textures/disturb.jpg',
|
|
|
|
- 'textures/sprite0.jpg',
|
|
|
|
- 'textures/sprite0.png',
|
|
|
|
- 'textures/lensflare/lensflare0.png',
|
|
|
|
- 'textures/lensflare/lensflare0_alpha.png',
|
|
|
|
- 'textures/sprites/ball.png',
|
|
|
|
- 'textures/sprites/snowflake7_alpha.png' ];
|
|
|
|
-
|
|
|
|
- for ( let i = 0; i < images.length; i ++ ) {
|
|
|
|
-
|
|
|
|
- const map = textureLoader.load( images[ i ] );
|
|
|
|
- mapsNoPre.push( map );
|
|
|
|
-
|
|
|
|
- const mapPre = textureLoader.load( images[ i ] );
|
|
|
|
- mapPre.premultiplyAlpha = true;
|
|
|
|
- mapPre.needsUpdate = true;
|
|
|
|
- mapsPre.push( mapPre );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
|
|
+ const canvas = document.createElement( 'canvas' );
|
|
|
|
+ const ctx = canvas.getContext( '2d' );
|
|
|
|
+ canvas.width = canvas.height = 128;
|
|
|
|
+ ctx.fillStyle = '#ddd';
|
|
|
|
+ ctx.fillRect( 0, 0, 128, 128 );
|
|
|
|
+ ctx.fillStyle = '#555';
|
|
|
|
+ ctx.fillRect( 0, 0, 64, 64 );
|
|
|
|
+ ctx.fillStyle = '#999';
|
|
|
|
+ ctx.fillRect( 32, 32, 32, 32 );
|
|
|
|
+ ctx.fillStyle = '#555';
|
|
|
|
+ ctx.fillRect( 64, 64, 64, 64 );
|
|
|
|
+ ctx.fillStyle = '#777';
|
|
|
|
+ ctx.fillRect( 96, 96, 32, 32 );
|
|
|
|
+
|
|
|
|
+ mapBg = new THREE.CanvasTexture( canvas );
|
|
|
|
+ mapBg.wrapS = mapBg.wrapT = THREE.RepeatWrapping;
|
|
|
|
+ mapBg.repeat.set( 64, 32 );
|
|
|
|
+
|
|
|
|
+ scene.background = mapBg;
|
|
|
|
|
|
// FOREGROUND OBJECTS
|
|
// FOREGROUND OBJECTS
|
|
|
|
+
|
|
const src = [
|
|
const src = [
|
|
{ name: 'Zero', constant: THREE.ZeroFactor },
|
|
{ name: 'Zero', constant: THREE.ZeroFactor },
|
|
{ name: 'One', constant: THREE.OneFactor },
|
|
{ name: 'One', constant: THREE.OneFactor },
|
|
@@ -244,6 +105,8 @@
|
|
const geo1 = new THREE.PlaneGeometry( 100, 100 );
|
|
const geo1 = new THREE.PlaneGeometry( 100, 100 );
|
|
const geo2 = new THREE.PlaneGeometry( 100, 25 );
|
|
const geo2 = new THREE.PlaneGeometry( 100, 25 );
|
|
|
|
|
|
|
|
+ const texture = new THREE.TextureLoader().load( 'textures/lensflare/lensflare0_alpha.png' );
|
|
|
|
+
|
|
for ( let i = 0; i < dst.length; i ++ ) {
|
|
for ( let i = 0; i < dst.length; i ++ ) {
|
|
|
|
|
|
const blendDst = dst[ i ];
|
|
const blendDst = dst[ i ];
|
|
@@ -252,7 +115,7 @@
|
|
|
|
|
|
const blendSrc = src[ j ];
|
|
const blendSrc = src[ j ];
|
|
|
|
|
|
- const material = new THREE.MeshBasicMaterial( { map: currentMaps[ currentIndex ] } );
|
|
|
|
|
|
+ const material = new THREE.MeshBasicMaterial( { map: texture } );
|
|
material.transparent = true;
|
|
material.transparent = true;
|
|
|
|
|
|
material.blending = THREE.CustomBlending;
|
|
material.blending = THREE.CustomBlending;
|
|
@@ -310,123 +173,22 @@
|
|
|
|
|
|
// RENDERER
|
|
// RENDERER
|
|
|
|
|
|
- const canvas = document.createElement( 'canvas' );
|
|
|
|
- const context = canvas.getContext( 'webgl2', { alpha: false } ); // TODO Remove workaround
|
|
|
|
-
|
|
|
|
- renderer = new THREE.WebGLRenderer( { canvas: canvas, context: context } );
|
|
|
|
|
|
+ renderer = new THREE.WebGLRenderer();
|
|
renderer.setPixelRatio( window.devicePixelRatio );
|
|
renderer.setPixelRatio( window.devicePixelRatio );
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
-
|
|
|
|
- renderer.domElement.style.position = 'absolute';
|
|
|
|
- renderer.domElement.style.left = '215px';
|
|
|
|
-
|
|
|
|
document.body.appendChild( renderer.domElement );
|
|
document.body.appendChild( renderer.domElement );
|
|
|
|
|
|
// EVENTS
|
|
// EVENTS
|
|
|
|
|
|
window.addEventListener( 'resize', onWindowResize );
|
|
window.addEventListener( 'resize', onWindowResize );
|
|
|
|
|
|
- addImgHandler( 'img_0', 0 );
|
|
|
|
- addImgHandler( 'img_1', 1 );
|
|
|
|
- addImgHandler( 'img_2', 2 );
|
|
|
|
- addImgHandler( 'img_3', 3 );
|
|
|
|
- addImgHandler( 'img_4', 4 );
|
|
|
|
- addImgHandler( 'img_5', 5 );
|
|
|
|
- addImgHandler( 'img_6', 6 );
|
|
|
|
-
|
|
|
|
- addBgHandler( 'bg_0', mapBg2 );
|
|
|
|
- addBgHandler( 'bg_1', mapBg0 );
|
|
|
|
- addBgHandler( 'bg_2', mapBg1 );
|
|
|
|
- addBgHandler( 'bg_3', mapBg3 );
|
|
|
|
- addBgHandler( 'bg_4', mapBg4 );
|
|
|
|
- addBgHandler( 'bg_5', mapBg5 );
|
|
|
|
- addBgHandler( 'bg_6', mapBg6 );
|
|
|
|
-
|
|
|
|
- addEqHandler( 'btn_add', THREE.AddEquation );
|
|
|
|
- addEqHandler( 'btn_sub', THREE.SubtractEquation );
|
|
|
|
- addEqHandler( 'btn_rsub', THREE.ReverseSubtractEquation );
|
|
|
|
- addEqHandler( 'btn_min', THREE.MinEquation );
|
|
|
|
- addEqHandler( 'btn_max', THREE.MaxEquation );
|
|
|
|
-
|
|
|
|
- addPreHandler( 'btn_pre', mapsPre );
|
|
|
|
- addPreHandler( 'btn_nopre', mapsNoPre );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- //
|
|
|
|
-
|
|
|
|
- function addImgHandler( id, index ) {
|
|
|
|
-
|
|
|
|
- const el = document.getElementById( id );
|
|
|
|
-
|
|
|
|
- el.addEventListener( 'click', function () {
|
|
|
|
-
|
|
|
|
- for ( let i = 0; i < materials.length; i ++ ) {
|
|
|
|
-
|
|
|
|
- materials[ i ].map = currentMaps[ index ];
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
|
|
+ // GUI
|
|
|
|
|
|
- currentIndex = index;
|
|
|
|
|
|
+ //
|
|
|
|
+ const gui = new GUI( { width: 300 } );
|
|
|
|
|
|
- } );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function addEqHandler( id, eq ) {
|
|
|
|
-
|
|
|
|
- const el = document.getElementById( id );
|
|
|
|
-
|
|
|
|
- el.addEventListener( 'click', function () {
|
|
|
|
-
|
|
|
|
- for ( let i = 0; i < materials.length; i ++ ) {
|
|
|
|
-
|
|
|
|
- materials[ i ].blendEquation = eq;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- document.getElementById( 'btn_add' ).style.backgroundColor = 'transparent';
|
|
|
|
- document.getElementById( 'btn_sub' ).style.backgroundColor = 'transparent';
|
|
|
|
- document.getElementById( 'btn_rsub' ).style.backgroundColor = 'transparent';
|
|
|
|
- document.getElementById( 'btn_min' ).style.backgroundColor = 'transparent';
|
|
|
|
- document.getElementById( 'btn_max' ).style.backgroundColor = 'transparent';
|
|
|
|
-
|
|
|
|
- el.style.backgroundColor = 'darkorange';
|
|
|
|
-
|
|
|
|
- } );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function addBgHandler( id, map ) {
|
|
|
|
-
|
|
|
|
- const el = document.getElementById( id );
|
|
|
|
- el.addEventListener( 'click', function () {
|
|
|
|
-
|
|
|
|
- materialBg.map = map;
|
|
|
|
-
|
|
|
|
- } );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function addPreHandler( id, marray ) {
|
|
|
|
-
|
|
|
|
- const el = document.getElementById( id );
|
|
|
|
- el.addEventListener( 'click', function () {
|
|
|
|
-
|
|
|
|
- currentMaps = marray;
|
|
|
|
-
|
|
|
|
- for ( let i = 0; i < materials.length; i ++ ) {
|
|
|
|
-
|
|
|
|
- materials[ i ].map = currentMaps[ currentIndex ];
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- document.getElementById( 'btn_pre' ).style.backgroundColor = 'transparent';
|
|
|
|
- document.getElementById( 'btn_nopre' ).style.backgroundColor = 'transparent';
|
|
|
|
-
|
|
|
|
- el.style.backgroundColor = 'darkorange';
|
|
|
|
-
|
|
|
|
- } );
|
|
|
|
|
|
+ gui.add( params, 'blendEquation', equations ).onChange( updateBlendEquation );
|
|
|
|
+ gui.open();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -464,15 +226,25 @@
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ function updateBlendEquation( value ) {
|
|
|
|
+
|
|
|
|
+ for ( const material of materials ) {
|
|
|
|
+
|
|
|
|
+ material.blendEquation = value;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
function animate() {
|
|
function animate() {
|
|
|
|
|
|
requestAnimationFrame( animate );
|
|
requestAnimationFrame( animate );
|
|
|
|
|
|
const time = Date.now() * 0.00025;
|
|
const time = Date.now() * 0.00025;
|
|
- const ox = ( time * - 0.01 * materialBg.map.repeat.x ) % 1;
|
|
|
|
- const oy = ( time * - 0.01 * materialBg.map.repeat.y ) % 1;
|
|
|
|
|
|
+ const ox = ( time * - 0.01 * mapBg.repeat.x ) % 1;
|
|
|
|
+ const oy = ( time * - 0.01 * mapBg.repeat.y ) % 1;
|
|
|
|
|
|
- materialBg.map.offset.set( ox, oy );
|
|
|
|
|
|
+ mapBg.offset.set( ox, oy );
|
|
|
|
|
|
renderer.render( scene, camera );
|
|
renderer.render( scene, camera );
|
|
|
|
|