123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- import * as THREE from 'three';
- import { threejsLessonUtils } from './threejs-lesson-utils.js';
- {
- function makeSphere( widthDivisions, heightDivisions ) {
- const radius = 7;
- return new THREE.SphereGeometry( radius, widthDivisions, heightDivisions );
- }
- const highPolySphereGeometry = function () {
- const widthDivisions = 100;
- const heightDivisions = 50;
- return makeSphere( widthDivisions, heightDivisions );
- }();
- const lowPolySphereGeometry = function () {
- const widthDivisions = 12;
- const heightDivisions = 9;
- return makeSphere( widthDivisions, heightDivisions );
- }();
- function smoothOrFlat( flatShading, radius = 7 ) {
- const widthDivisions = 12;
- const heightDivisions = 9;
- const geometry = new THREE.SphereGeometry( radius, widthDivisions, heightDivisions );
- const material = new THREE.MeshPhongMaterial( {
- flatShading,
- color: 'hsl(300,50%,50%)',
- } );
- return new THREE.Mesh( geometry, material );
- }
- function basicLambertPhongExample( MaterialCtor, lowPoly, params = {} ) {
- const geometry = lowPoly ? lowPolySphereGeometry : highPolySphereGeometry;
- const material = new MaterialCtor( {
- color: 'hsl(210,50%,50%)',
- ...params,
- } );
- return {
- obj3D: new THREE.Mesh( geometry, material ),
- trackball: lowPoly,
- };
- }
- function sideExample( side ) {
- const base = new THREE.Object3D();
- const size = 6;
- const geometry = new THREE.PlaneGeometry( size, size );
- [
- { position: [ - 1, 0, 0 ], up: [ 0, 1, 0 ], },
- { position: [ 1, 0, 0 ], up: [ 0, - 1, 0 ], },
- { position: [ 0, - 1, 0 ], up: [ 0, 0, - 1 ], },
- { position: [ 0, 1, 0 ], up: [ 0, 0, 1 ], },
- { position: [ 0, 0, - 1 ], up: [ 1, 0, 0 ], },
- { position: [ 0, 0, 1 ], up: [ - 1, 0, 0 ], },
- ].forEach( ( settings, ndx ) => {
- const material = new THREE.MeshBasicMaterial( { side } );
- material.color.setHSL( ndx / 6, .5, .5 );
- const mesh = new THREE.Mesh( geometry, material );
- mesh.up.set( ...settings.up );
- mesh.lookAt( ...settings.position );
- mesh.position.set( ...settings.position ).multiplyScalar( size * .75 );
- base.add( mesh );
- } );
- return base;
- }
- function makeStandardPhysicalMaterialGrid( elem, physical, update ) {
- const numMetal = 5;
- const numRough = 7;
- const meshes = [];
- const MatCtor = physical ? THREE.MeshPhysicalMaterial : THREE.MeshStandardMaterial;
- const color = physical ? 'hsl(160,50%,50%)' : 'hsl(140,50%,50%)';
- for ( let m = 0; m < numMetal; ++ m ) {
- const row = [];
- for ( let r = 0; r < numRough; ++ r ) {
- const material = new MatCtor( {
- color,
- roughness: r / ( numRough - 1 ),
- metalness: 1 - m / ( numMetal - 1 ),
- } );
- const mesh = new THREE.Mesh( highPolySphereGeometry, material );
- row.push( mesh );
- }
- meshes.push( row );
- }
- return {
- obj3D: null,
- trackball: false,
- render( renderInfo ) {
- const { camera, scene, renderer } = renderInfo;
- const rect = elem.getBoundingClientRect();
- const width = ( rect.right - rect.left ) * renderInfo.pixelRatio;
- const height = ( rect.bottom - rect.top ) * renderInfo.pixelRatio;
- const left = rect.left * renderInfo.pixelRatio;
- const bottom = ( renderer.domElement.clientHeight - rect.bottom ) * renderInfo.pixelRatio;
- const cellSize = Math.min( width / numRough, height / numMetal ) | 0;
- const xOff = ( width - cellSize * numRough ) / 2;
- const yOff = ( height - cellSize * numMetal ) / 2;
- camera.aspect = 1;
- camera.updateProjectionMatrix();
- if ( update ) {
- update( meshes );
- }
- for ( let m = 0; m < numMetal; ++ m ) {
- for ( let r = 0; r < numRough; ++ r ) {
- const x = left + xOff + r * cellSize;
- const y = bottom + yOff + m * cellSize;
- renderer.setViewport( x, y, cellSize, cellSize );
- renderer.setScissor( x, y, cellSize, cellSize );
- const mesh = meshes[ m ][ r ];
- scene.add( mesh );
- renderer.render( scene, camera );
- scene.remove( mesh );
- }
- }
- },
- };
- }
- threejsLessonUtils.addDiagrams( {
- smoothShading: {
- create() {
- return smoothOrFlat( false );
- },
- },
- flatShading: {
- create() {
- return smoothOrFlat( true );
- },
- },
- MeshBasicMaterial: {
- create() {
- return basicLambertPhongExample( THREE.MeshBasicMaterial );
- },
- },
- MeshLambertMaterial: {
- create() {
- return basicLambertPhongExample( THREE.MeshLambertMaterial );
- },
- },
- MeshPhongMaterial: {
- create() {
- return basicLambertPhongExample( THREE.MeshPhongMaterial );
- },
- },
- MeshBasicMaterialLowPoly: {
- create() {
- return basicLambertPhongExample( THREE.MeshBasicMaterial, true );
- },
- },
- MeshLambertMaterialLowPoly: {
- create() {
- return basicLambertPhongExample( THREE.MeshLambertMaterial, true );
- },
- },
- MeshPhongMaterialLowPoly: {
- create() {
- return basicLambertPhongExample( THREE.MeshPhongMaterial, true );
- },
- },
- MeshPhongMaterialShininess0: {
- create() {
- return basicLambertPhongExample( THREE.MeshPhongMaterial, false, {
- color: 'red',
- shininess: 0,
- } );
- },
- },
- MeshPhongMaterialShininess30: {
- create() {
- return basicLambertPhongExample( THREE.MeshPhongMaterial, false, {
- color: 'red',
- shininess: 30,
- } );
- },
- },
- MeshPhongMaterialShininess150: {
- create() {
- return basicLambertPhongExample( THREE.MeshPhongMaterial, false, {
- color: 'red',
- shininess: 150,
- } );
- },
- },
- MeshBasicMaterialCompare: {
- create() {
- return basicLambertPhongExample( THREE.MeshBasicMaterial, false, {
- color: 'purple',
- } );
- },
- },
- MeshLambertMaterialCompare: {
- create() {
- return basicLambertPhongExample( THREE.MeshLambertMaterial, false, {
- color: 'black',
- emissive: 'purple',
- } );
- },
- },
- MeshPhongMaterialCompare: {
- create() {
- return basicLambertPhongExample( THREE.MeshPhongMaterial, false, {
- color: 'black',
- emissive: 'purple',
- shininess: 0,
- } );
- },
- },
- MeshToonMaterial: {
- create() {
- return basicLambertPhongExample( THREE.MeshToonMaterial );
- },
- },
- MeshStandardMaterial: {
- create( props ) {
- return makeStandardPhysicalMaterialGrid( props.renderInfo.elem, false );
- },
- },
- MeshPhysicalMaterial: {
- create( props ) {
- const settings = {
- clearcoat: .5,
- clearcoatRoughness: 0,
- };
- function addElem( parent, type, style = {} ) {
- const elem = document.createElement( type );
- Object.assign( elem.style, style );
- parent.appendChild( elem );
- return elem;
- }
- function addRange( elem, obj, prop, min, max ) {
- const outer = addElem( elem, 'div', {
- width: '100%',
- textAlign: 'center',
- 'font-family': 'monospace',
- } );
- const div = addElem( outer, 'div', {
- textAlign: 'left',
- display: 'inline-block',
- } );
- const label = addElem( div, 'label', {
- display: 'inline-block',
- width: '12em',
- } );
- label.textContent = prop;
- const num = addElem( div, 'div', {
- display: 'inline-block',
- width: '3em',
- } );
- function updateNum() {
- num.textContent = obj[ prop ].toFixed( 2 );
- }
- updateNum();
- const input = addElem( div, 'input', {
- } );
- Object.assign( input, {
- type: 'range',
- min: 0,
- max: 100,
- value: ( obj[ prop ] - min ) / ( max - min ) * 100,
- } );
- input.addEventListener( 'input', () => {
- obj[ prop ] = min + ( max - min ) * input.value / 100;
- updateNum();
- } );
- }
- const { elem } = props.renderInfo;
- addRange( elem, settings, 'clearcoat', 0, 1 );
- addRange( elem, settings, 'clearcoatRoughness', 0, 1 );
- const area = addElem( elem, 'div', {
- width: '100%',
- height: '400px',
- } );
- return makeStandardPhysicalMaterialGrid( area, true, ( meshes ) => {
- meshes.forEach( row => row.forEach( mesh => {
- mesh.material.clearcoat = settings.clearcoat;
- mesh.material.clearcoatRoughness = settings.clearcoatRoughness;
- } ) );
- } );
- },
- },
- MeshDepthMaterial: {
- create( props ) {
- const { camera } = props;
- const radius = 4;
- const tube = 1.5;
- const radialSegments = 8;
- const tubularSegments = 64;
- const p = 2;
- const q = 3;
- const geometry = new THREE.TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q );
- const material = new THREE.MeshDepthMaterial();
- camera.near = 7;
- camera.far = 20;
- return new THREE.Mesh( geometry, material );
- },
- },
- MeshNormalMaterial: {
- create() {
- const radius = 4;
- const tube = 1.5;
- const radialSegments = 8;
- const tubularSegments = 64;
- const p = 2;
- const q = 3;
- const geometry = new THREE.TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q );
- const material = new THREE.MeshNormalMaterial();
- return new THREE.Mesh( geometry, material );
- },
- },
- sideDefault: {
- create() {
- return sideExample( THREE.FrontSide );
- },
- },
- sideDouble: {
- create() {
- return sideExample( THREE.DoubleSide );
- },
- },
- } );
- }
|