Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions build/three.cjs

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions build/three.core.js
Original file line number Diff line number Diff line change
Expand Up @@ -46705,6 +46705,15 @@ class RectAreaLight extends Light {
*/
this.height = height;

/**
* A texture that modulates the light color. The texture is
* projected onto the light's surface and sampled during shading.
*
* @type {?Texture}
* @default null
*/
this.map = null;

}

/**
Expand Down Expand Up @@ -46733,6 +46742,7 @@ class RectAreaLight extends Light {

this.width = source.width;
this.height = source.height;
this.map = source.map;

return this;

Expand All @@ -46745,6 +46755,8 @@ class RectAreaLight extends Light {
data.object.width = this.width;
data.object.height = this.height;

if ( this.map && this.map.isTexture ) data.object.map = this.map.toJSON( meta ).uuid;

return data;

}
Expand Down
2 changes: 1 addition & 1 deletion build/three.core.min.js

Large diffs are not rendered by default.

27 changes: 23 additions & 4 deletions build/three.module.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/three.module.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"webgl_lights_spotlight",
"webgl_lights_spotlights",
"webgl_lights_rectarealight",
"webgl_lights_rectarealight_map",
"webgl_lines_colors",
"webgl_lines_dashed",
"webgl_lines_fat",
Expand Down
42 changes: 20 additions & 22 deletions examples/jsm/helpers/RectAreaLightHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import {
BackSide,
BufferGeometry,
Float32BufferAttribute,
Line,
LineBasicMaterial,
Mesh,
MeshBasicMaterial
} from 'three';
Expand All @@ -19,10 +17,10 @@ import {
* light.add( helper );
* ```
*
* @augments Line
* @augments Mesh
* @three_import import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
*/
class RectAreaLightHelper extends Line {
class RectAreaLightHelper extends Mesh {

/**
* Constructs a new rect area light helper.
Expand All @@ -33,13 +31,15 @@ class RectAreaLightHelper extends Line {
*/
constructor( light, color ) {

const positions = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ];
const positions = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ];
const uvs = [ 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ];

const geometry = new BufferGeometry();
geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
geometry.computeBoundingSphere();

const material = new LineBasicMaterial( { fog: false } );
const material = new MeshBasicMaterial( { side: BackSide, fog: false } );

super( geometry, material );

Expand All @@ -59,16 +59,6 @@ class RectAreaLightHelper extends Line {

this.type = 'RectAreaLightHelper';

//

const positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ];

const geometry2 = new BufferGeometry();
geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );
geometry2.computeBoundingSphere();

this.add( new Mesh( geometry2, new MeshBasicMaterial( { side: BackSide, fog: false } ) ) );

}

updateMatrixWorld() {
Expand All @@ -78,7 +68,6 @@ class RectAreaLightHelper extends Line {
if ( this.color !== undefined ) {

this.material.color.set( this.color );
this.children[ 0 ].material.color.set( this.color );

} else {

Expand All @@ -89,15 +78,26 @@ class RectAreaLightHelper extends Line {
const max = Math.max( c.r, c.g, c.b );
if ( max > 1 ) c.multiplyScalar( 1 / max );

this.children[ 0 ].material.color.copy( this.material.color );
}

// Apply light's map texture to the helper mesh
if ( this.material.map !== this.light.map ) {

this.material.map = this.light.map;
this.material.needsUpdate = true;

}

if ( this.light.map !== null && this.color === undefined ) {

const intensityScale = Math.sqrt( this.light.intensity / 5 );
this.material.color.setScalar( Math.min( intensityScale, 2.0 ) );

}

// ignore world scale on light
this.matrixWorld.extractRotation( this.light.matrixWorld ).scale( this.scale ).copyPosition( this.light.matrixWorld );

this.children[ 0 ].matrixWorld.copy( this.matrixWorld );

}

/**
Expand All @@ -108,8 +108,6 @@ class RectAreaLightHelper extends Line {

this.geometry.dispose();
this.material.dispose();
this.children[ 0 ].geometry.dispose();
this.children[ 0 ].material.dispose();

}

Expand Down
Binary file added examples/models/gltf/ferrari_ao_.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/textures/veo-oledtv.mp4
Binary file not shown.
225 changes: 225 additions & 0 deletions examples/webgl_lights_rectarealight_map.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - lights - rect area light</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - RectAreaLight with texture map<br/>
</div>

<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">

import * as THREE from 'three';

import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js';

let renderer, scene, camera, controls;
let stats, carModel, bodyMaterial, glassMaterial;

init();

function init() {

renderer = new THREE.WebGLRenderer( { antialias: true, outputBufferType: THREE.HalfFloatType } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animation );
renderer.toneMapping = THREE.NeutralTonemapping;
document.body.appendChild( renderer.domElement );

camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( - 15, 5, - 5 );

scene = new THREE.Scene();

RectAreaLightUniformsLib.init();

// Create video element for center light
const video = document.createElement( 'video' );
video.crossOrigin = 'anonymous';
video.src = 'textures/veo-oledtv.mp4';
video.loop = true;
video.muted = true;
video.playsInline = true;
video.autoplay = true;
video.play();

const videoTexture = new THREE.VideoTexture( video );
videoTexture.colorSpace = THREE.SRGBColorSpace;

const rectLight = new THREE.RectAreaLight( 0xffffff, 5, 12, 8 );
rectLight.map = videoTexture;
// rectLight.map = new THREE.TextureLoader().load( 'textures/uv_grid_opengl.jpg' );
rectLight.map.generateMipmaps = true;
rectLight.map.minFilter = THREE.LinearMipmapLinearFilter;
rectLight.position.set( 0, 5, 5 );
scene.add( rectLight );

scene.add( new RectAreaLightHelper( rectLight ) );

const geoFloor = new THREE.BoxGeometry( 2000, 0.1, 2000 );
const matStdFloor = new THREE.MeshStandardMaterial( { color: 0x444444 } );
matStdFloor.roughnessMap = createCheckerTexture( 400 );
const mshStdFloor = new THREE.Mesh( geoFloor, matStdFloor );
scene.add( mshStdFloor );

// Car shadow (AO plane)
const aoTexture = new THREE.TextureLoader().load( 'models/gltf/ferrari_ao_.png' );
const shadowPlane = new THREE.Mesh(
new THREE.PlaneGeometry( 256, 512 ),
new THREE.MeshBasicMaterial( {
color: 0x000000,
// map: aoTexture,
alphaMap: aoTexture,
// blending: THREE.SubstractiveBlending,
depthWrite: false,
transparent: true
} )
);
shadowPlane.rotation.x = - Math.PI / 2;
shadowPlane.rotation.z = Math.PI / 2;
shadowPlane.position.y = 0.06;
shadowPlane.scale.setScalar( 0.031 );
scene.add( shadowPlane );

// Load car model
bodyMaterial = new THREE.MeshStandardMaterial( {
color: 0xff4400,
roughness: 0.2,
metalness: 0
} );
glassMaterial = new THREE.MeshStandardMaterial( {
color: 0xffffff,
roughness: 0,
metalness: 0,
transparent: true,
premultipliedAlpha: true,
opacity: 0.3
} );

const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'jsm/libs/draco/gltf/' );

const loader = new GLTFLoader();
loader.setDRACOLoader( dracoLoader );
loader.load( 'models/gltf/ferrari.glb', function ( gltf ) {

carModel = gltf.scene;
carModel.rotation.y = Math.PI / 2;
carModel.scale.setScalar( 3 );
carModel.position.set( 0, 0, 0 );

carModel.traverse( function ( child ) {

if ( child.isMesh ) {

// Replace car body material
if ( child.name === 'body' ) {

child.material = bodyMaterial;

}

// Replace glass material with transmissive material
if ( child.name === 'glass' ) {

child.material = glassMaterial;

}

}

} );

scene.add( carModel );

} );

controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 2, 3 );
controls.enableDamping = true;
controls.update();

//

window.addEventListener( 'resize', onWindowResize );

stats = new Stats();
document.body.appendChild( stats.dom );

const gui = new GUI();
gui.add( rectLight, 'intensity', 0, 20 ).name( 'Light Intensity' );
gui.add( matStdFloor, 'roughness', 0, 1 ).name( 'Floor Roughness' );
gui.add( matStdFloor, 'metalness', 0, 1 ).name( 'Floor Metalness' );
gui.add( bodyMaterial, 'roughness', 0, 1 ).name( 'Car Roughness' );
gui.add( bodyMaterial, 'metalness', 0, 1 ).name( 'Car Metalness' );
gui.add( glassMaterial, 'opacity', 0, 1 ).name( 'Glass Opacity' );
gui.add( glassMaterial, 'roughness', 0, 1 ).name( 'Glass Roughness' );

}

function createCheckerTexture( repeat = 1 ) {

const canvas = document.createElement( 'canvas' );
canvas.width = 2;
canvas.height = 2;

const ctx = canvas.getContext( '2d' );
ctx.fillStyle = '#000';
ctx.fillRect( 0, 0, 2, 2 );
ctx.fillStyle = '#fff';
ctx.fillRect( 0, 0, 1, 1 );
ctx.fillRect( 1, 1, 1, 1 );

const texture = new THREE.CanvasTexture( canvas );
texture.repeat.set( repeat, repeat );
texture.magFilter = THREE.NearestFilter;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;

return texture;

}

function onWindowResize() {

renderer.setSize( window.innerWidth, window.innerHeight );
camera.aspect = ( window.innerWidth / window.innerHeight );
camera.updateProjectionMatrix();

}

function animation() {

controls.update();

renderer.render( scene, camera );

stats.update();

}

</script>
</body>
</html>
Loading
Loading