470 lines
13 KiB
HTML
470 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>three.js webgl - materials - custom blending</title>
|
|
<meta charset="utf-8">
|
|
<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>
|
|
</head>
|
|
<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>
|
|
|
|
<script type="module">
|
|
|
|
import * as THREE from '../build/three.module.js';
|
|
|
|
let camera, scene, renderer;
|
|
|
|
let materialBg;
|
|
const materials = [];
|
|
|
|
const mapsPre = [];
|
|
const mapsNoPre = [];
|
|
|
|
let currentMaps = mapsNoPre;
|
|
let currentIndex = 4;
|
|
|
|
init();
|
|
animate();
|
|
|
|
function init() {
|
|
|
|
// CAMERA
|
|
|
|
camera = new THREE.PerspectiveCamera( 80, window.innerWidth / window.innerHeight, 1, 1000 );
|
|
camera.position.z = 700;
|
|
|
|
// 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
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
// FOREGROUND OBJECTS
|
|
const src = [
|
|
{ name: 'Zero', constant: THREE.ZeroFactor },
|
|
{ name: 'One', constant: THREE.OneFactor },
|
|
{ name: 'SrcColor', constant: THREE.SrcColorFactor },
|
|
{ name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor },
|
|
{ name: 'SrcAlpha', constant: THREE.SrcAlphaFactor },
|
|
{ name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor },
|
|
{ name: 'DstAlpha', constant: THREE.DstAlphaFactor },
|
|
{ name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor },
|
|
{ name: 'DstColor', constant: THREE.DstColorFactor },
|
|
{ name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor },
|
|
{ name: 'SrcAlphaSaturate', constant: THREE.SrcAlphaSaturateFactor }
|
|
];
|
|
|
|
const dst = [
|
|
{ name: 'Zero', constant: THREE.ZeroFactor },
|
|
{ name: 'One', constant: THREE.OneFactor },
|
|
{ name: 'SrcColor', constant: THREE.SrcColorFactor },
|
|
{ name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor },
|
|
{ name: 'SrcAlpha', constant: THREE.SrcAlphaFactor },
|
|
{ name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor },
|
|
{ name: 'DstAlpha', constant: THREE.DstAlphaFactor },
|
|
{ name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor },
|
|
{ name: 'DstColor', constant: THREE.DstColorFactor },
|
|
{ name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor }
|
|
];
|
|
|
|
const geo1 = new THREE.PlaneGeometry( 100, 100 );
|
|
const geo2 = new THREE.PlaneGeometry( 100, 25 );
|
|
|
|
for ( let i = 0; i < dst.length; i ++ ) {
|
|
|
|
const blendDst = dst[ i ];
|
|
|
|
for ( let j = 0; j < src.length; j ++ ) {
|
|
|
|
const blendSrc = src[ j ];
|
|
|
|
const material = new THREE.MeshBasicMaterial( { map: currentMaps[ currentIndex ] } );
|
|
material.transparent = true;
|
|
|
|
material.blending = THREE.CustomBlending;
|
|
material.blendSrc = blendSrc.constant;
|
|
material.blendDst = blendDst.constant;
|
|
material.blendEquation = THREE.AddEquation;
|
|
|
|
const x = ( j - src.length / 2 ) * 110;
|
|
const z = 0;
|
|
const y = ( i - dst.length / 2 ) * 110 + 50;
|
|
|
|
const mesh = new THREE.Mesh( geo1, material );
|
|
mesh.position.set( x, - y, z );
|
|
mesh.matrixAutoUpdate = false;
|
|
mesh.updateMatrix();
|
|
scene.add( mesh );
|
|
|
|
materials.push( material );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for ( let j = 0; j < src.length; j ++ ) {
|
|
|
|
const blendSrc = src[ j ];
|
|
|
|
const x = ( j - src.length / 2 ) * 110;
|
|
const z = 0;
|
|
const y = ( 0 - dst.length / 2 ) * 110 + 50;
|
|
|
|
const mesh = new THREE.Mesh( geo2, generateLabelMaterial( blendSrc.name, 'rgba( 0, 150, 0, 1 )' ) );
|
|
mesh.position.set( x, - ( y - 70 ), z );
|
|
mesh.matrixAutoUpdate = false;
|
|
mesh.updateMatrix();
|
|
scene.add( mesh );
|
|
|
|
}
|
|
|
|
for ( let i = 0; i < dst.length; i ++ ) {
|
|
|
|
const blendDst = dst[ i ];
|
|
|
|
const x = ( 0 - src.length / 2 ) * 110 - 125;
|
|
const z = 0;
|
|
const y = ( i - dst.length / 2 ) * 110 + 165;
|
|
|
|
const mesh = new THREE.Mesh( geo2, generateLabelMaterial( blendDst.name, 'rgba( 150, 0, 0, 1 )' ) );
|
|
mesh.position.set( x, - ( y - 120 ), z );
|
|
mesh.matrixAutoUpdate = false;
|
|
mesh.updateMatrix();
|
|
scene.add( mesh );
|
|
|
|
}
|
|
|
|
// RENDERER
|
|
|
|
renderer = new THREE.WebGLRenderer();
|
|
renderer.setPixelRatio( window.devicePixelRatio );
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
|
renderer.domElement.style.position = 'absolute';
|
|
renderer.domElement.style.left = '215px';
|
|
|
|
document.body.appendChild( renderer.domElement );
|
|
|
|
// EVENTS
|
|
|
|
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 ];
|
|
|
|
}
|
|
|
|
currentIndex = index;
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
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';
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
//
|
|
|
|
function onWindowResize() {
|
|
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
|
|
}
|
|
|
|
//
|
|
|
|
function generateLabelMaterial( text, bg ) {
|
|
|
|
const canvas = document.createElement( 'canvas' );
|
|
const ctx = canvas.getContext( '2d' );
|
|
canvas.width = 128;
|
|
canvas.height = 32;
|
|
|
|
ctx.fillStyle = bg;
|
|
ctx.fillRect( 0, 0, 128, 32 );
|
|
|
|
ctx.fillStyle = 'white';
|
|
ctx.font = '12pt arial bold';
|
|
ctx.fillText( text, 8, 22 );
|
|
|
|
const map = new THREE.CanvasTexture( canvas );
|
|
|
|
const material = new THREE.MeshBasicMaterial( { map: map, transparent: true } );
|
|
return material;
|
|
|
|
}
|
|
|
|
function animate() {
|
|
|
|
requestAnimationFrame( animate );
|
|
|
|
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;
|
|
|
|
materialBg.map.offset.set( ox, oy );
|
|
|
|
renderer.render( scene, camera );
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|