three.js webcam as background - javascript

I am trying to get the camera video of background of a threejs scene. Below is the code. Currently the background mesh is coming as blank. Not sure why this is happening.
I was following the below link to replace the image with the camera video.
http://jeremy.assenat.free.fr/Lab/threejsbg/
(function () {
var video, videoImage, videoImageContext, videoTexture;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
window.URL = window.URL || window.webkitURL;
var camvideo = document.getElementById('monitor');
if (!navigator.getUserMedia)
{
document.getElementById('errorMessage').innerHTML =
'Sorry. <code>navigator.getUserMedia()</code> is not available.';
} else {
navigator.getUserMedia({video: true}, gotStream, noStream);
}
function gotStream(stream)
{
if (window.URL)
{
camvideo.src = window.URL.createObjectURL(stream);
} else // Opera
{
camvideo.src = stream;
}
camvideo.onerror = function (e)
{
stream.stop();
};
stream.onended = noStream;
}
function noStream(e)
{
var msg = 'No camera available.';
if (e.code == 1)
{
msg = 'User denied access to use camera.';
}
document.getElementById('errorMessage').textContent = msg;
}
var color = 0x000000;
// Create your main scene
var scene = new THREE.Scene();
// Create your main camera
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// Create lights
var light = new THREE.PointLight(0xEEEEEE);
light.position.set(20, 0, 20);
scene.add(light);
var lightAmb = new THREE.AmbientLight(0x777777);
scene.add(lightAmb);
// Create your renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.domElement.className = "mainCanvas"
// Create a cube
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshLambertMaterial({
color: 0xff00ff,
ambient: 0x121212,
emissive: 0x121212
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Set up the main camera
camera.position.z = 5;
///////////
// VIDEO //
///////////
video = document.getElementById('monitor');
videoImage = document.getElementById('videoImage');
videoImageContext = videoImage.getContext('2d');
videoImageContext.translate(320, 0);
videoImageContext.scale(-1, 1);
// background color if no video present
videoImageContext.fillStyle = '#000000';
videoImageContext.fillRect(0, 0, videoImage.width, videoImage.height);
videoTexture = new THREE.Texture(videoImage);
videoTexture.minFilter = THREE.LinearFilter;
videoTexture.magFilter = THREE.LinearFilter;
var movieMaterial = new THREE.MeshBasicMaterial({map: videoTexture, overdraw: true, side: THREE.DoubleSide});
// the geometry on which the movie will be displayed;
// movie image will be scaled to fit these dimensions.
var movieGeometry = new THREE.PlaneGeometry(100, 100, 1, 1);
var movieScreen = new THREE.Mesh(movieGeometry, movieMaterial);
movieScreen.position.set(0, 50, 0);
/* scene.add(movieScreen);
camera.position.set(0, 150, 300);
camera.lookAt(movieScreen.position);
*/
// Load the background texture
/* var texture = THREE.ImageUtils.loadTexture('images/checkerboard.jpg');
var backgroundMesh = new THREE.Mesh(
new THREE.PlaneGeometry(2, 2, 0, 0),
new THREE.MeshBasicMaterial({
map: videoTexture
})
);*/
var backgroundMesh = movieScreen;
backgroundMesh.material.depthTest = false;
backgroundMesh.material.depthWrite = false;
// Create your background scene
var backgroundScene = new THREE.Scene();
var backgroundCamera = new THREE.Camera();
backgroundScene.add(backgroundCamera);
backgroundScene.add(backgroundMesh);
// Rendering function
var render = function () {
requestAnimationFrame(render);
// Update the color to set
if (color < 0xdddddd)
color += 0x0000ff;
// Update the cube color
cube.material.color.setHex(color);
// Update the cube rotations
cube.rotation.x += 0.05;
cube.rotation.y += 0.02;
renderer.autoClear = false;
renderer.clear();
renderer.render(backgroundScene, backgroundCamera);
renderer.render(scene, camera);
};
render();
})();

If you want the video to be static in background, I wouldn't transfer the video into the 3D scene as texture. Instead I would leave it in a div placed behind the canvas. The canvas can be set transparent, so only the meshes will be rendered, but background stays clear.
var renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setClearAlpha(0.0);
.parent {
position: relative;
width: 1024px;
height: 768px;
}
#monitor, #mainCanvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#monitor {
transform: scaleX(-1);
}
<div class="parent">
<div id="monitor"></div>
<div id="mainCanvas"></div>
</div>

There doesn't seem to be any code that actually draws to the texture as the video plays. You will probably need to add something like
videoImageContext.drawImage(video, 0, 0);
videoTexture.needsUpdate = true;
to your renderloop.

Related

I want to import 3d gltf model via html's input tag for file in three.js (javascript) but it's giving me an error

I want to load my 3d model from an input tag of html . but its giving an error πŸ‘‡πŸ»
t.lastIndexOf is not a function
This is my html code πŸ‘‡πŸ»
<input type="file" id="MODEL" />
<button onclick="GLTFLoader()" id="LOAD" type="submit">Load_model</button>
And this is my javascript code for loading modelπŸ‘‡πŸ»
function GLTFLoader() {
const MODEL = document.getElementById("MODEL").files[0]
var Mesh ;
let LOADER = new THREE.GLTFLoader();
LOADER.load(MODEL, (gltf) =>
{Mesh = gltf.scene;
Mesh.scale.set(0.2,0.2,0.2);
scene.add(Mesh);
Mesh.position.x=0;
Mesh.position.y=10;
Mesh.position.z=15;
});
}
Listen for the change event on the file input, then convert the file blob into a blob URL, like this:
const loader = new GLTFLoader();
const input = document.querySelector("input");
input.addEventListener("change", (event) => {
const file = event.target.files[0];
const url = URL.createObjectURL(file);
loader.load(url, (gltf) => {
scene.add(gltf.scene);
});
});
Here's an demo:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
canvas {
display: block;
}
input {
position: absolute;
z-index: 1;
top: 1em;
left: 1em;
}
<input type="file" />
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.121.1/build/three.module.js";
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three#0.121.1/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "https://cdn.jsdelivr.net/npm/three#0.121.1/examples/jsm/loaders/GLTFLoader.js";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const cameraMin = 0.0001;
const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.PerspectiveCamera(75, aspect, cameraMin, 1000);
const controls = new OrbitControls(camera, renderer.domElement);
const scene = new THREE.Scene();
camera.position.z = 5;
const cube = new THREE.Mesh(
new THREE.BoxBufferGeometry(),
new THREE.MeshNormalMaterial()
);
const spotLight1 = new THREE.SpotLight(0xffffff);
const spotLight2 = new THREE.SpotLight(0xffffff);
const spotLight3 = new THREE.SpotLight(0xffffff);
const spotLight4 = new THREE.SpotLight(0xffffff);
spotLight1.position.set(0, 0, -5);
spotLight2.position.set(0, 0, 5);
spotLight3.position.set(5, 5, 5);
spotLight4.position.set(-5, -5, -5);
scene.add(spotLight1);
scene.add(spotLight2);
scene.add(spotLight3);
scene.add(spotLight4);
const loader = new GLTFLoader();
const input = document.querySelector("input");
input.addEventListener("change", (event) => {
const file = event.target.files[0];
const url = URL.createObjectURL(file);
loader.load(url, (gltf) => {
scene.add(gltf.scene);
});
});
(function animate() {
requestAnimationFrame(animate);
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
controls.update();
renderer.render(scene, camera);
})();
</script>
From docs for GLTFloader:
.load ( url : String, onLoad : Function, onProgress : Function, onError : Function ) : undefined
url β€” A string containing the path/URL of the .gltf or .glb file.
Make sure that the url parameter you pass to .load() is a String.
So, maybe try:
let url = "" + MODEL;
LOADER.load( url, (gltf) => ...
-jg-

Three.js Geometry isn't showing in browser, but the scene is

was following a tutorial and got the whole scene working in the body tag, then I tried moving it inside a div, and now only the scene will render. I'm not getting any errors, and I've console logged everything to make sure it is available to access. But I can't see why I'm nothing displayed.
// console.log ("Java Script is working!")
// get div for 3D area
const container = document.querySelector('#container')
// console.log (container)
var scene = new THREE.Scene()
var camera = new THREE.PerspectiveCamera(
75,
container.innerWidth / container.innerHeight,
0.1,
1000
)
camera.position.z = 3
var renderer = new THREE.WebGLRenderer({
antialias: true
})
renderer.setClearColor('#0D1033')
renderer.setSize(container.innerWidth, container.innerHeight)
container.appendChild(renderer.domElement)
container.addEventListener('resize', () => {
renderer.setSize(container.innerWidth, container.innerHeight)
camera.aspect = container.innerWidth / container.innerHeight
camera.updateProjectionMatrix()
})
var geometry = new THREE.BoxGeometry(10, 1, 1)
var material = new THREE.MeshLambertMaterial({
color: 0x90e9f9,
side: THREE.DoubleSide
})
var mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
var light = new THREE.PointLight(0xffffff, 5, 500)
light.position.set(10, 0, 25)
scene.add(light)
var render = function() {
requestAnimationFrame(render)
renderer.render(scene, camera)
}
console.log(mesh)
console.log(camera)
render()
* {
box-sizing: border-box;
}
body {
padding: 0;
margin: 0;
height: 100vh;
}
canvas {
display: block;
height: 100%;
width: 100%;
}
#container {
background-color: black;
height: 100vh;
}
<!DOCTYPE html>
<body>
<div id="container">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.2/TweenMax.min.js"></script>
<script src="main.js"></script>
</body>
</html>
I'm trying to get this working first so I know this can work before I move it into my main project, where I'm trying to have a 3d space inside a div on the website, but not take up the full page
You can only use innerWidth and innerHeight on the window object. Use clientWidth and clientHeight instead:
const container = document.querySelector('#container')
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
)
camera.position.z = 3
const renderer = new THREE.WebGLRenderer({
antialias: true
})
renderer.setClearColor('#0D1033')
renderer.setSize(container.clientWidth, container.clientHeight)
container.appendChild(renderer.domElement)
container.addEventListener('resize', () => {
renderer.setSize(container.clientWidth, container.clientHeight)
camera.aspect = container.clientWidth / container.clientHeight
camera.updateProjectionMatrix()
})
const geometry = new THREE.BoxGeometry(10, 1, 1)
const material = new THREE.MeshLambertMaterial({
color: 0x90e9f9,
side: THREE.DoubleSide
})
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
const light = new THREE.PointLight(0xffffff, 5, 500)
light.position.set(10, 0, 25)
scene.add(light)
const render = function() {
requestAnimationFrame(render)
renderer.render(scene, camera)
}
render()
* {
box-sizing: border-box;
}
body {
padding: 0;
margin: 0;
height: 100vh;
}
canvas {
display: block;
height: 100%;
width: 100%;
}
#container {
background-color: black;
height: 100vh;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.133.1/build/three.min.js"></script>
<div id="container">
</div>

ThreeJS Update scene when changing opacity

Here is the constructor of my object :
<code>class Something {
constructor(mesh = undefined, material = undefined) {
this.mesh = undefined;
material = material;
this.gui = undefined;
if (mesh) {
this.mesh_opacity = 1;
this.created = true;
this.mesh = new THREE.Mesh(mesh.geometry, new THREE.MeshPhongMaterial({
// wireframe: true,
transparent : true,
opacity: this.mesh_opacity,
color : 0xffffff
}));
}
</code>
I add this object to the scene, and in my interface, I have a slider where I can change the opacity of my object using this function :
<code><script type="text/javascript">
$('#slider').on('change', function(){
val = $("#slider").val();
scene.something.mesh_opacity = val;
scene.something.material = new THREE.MeshPhongMaterial({
transparent : true,
opacity: val,
color : 0xffffff
});
scene.something.material.needsUpdate = true;
})
</script></code>
but in my scene I don't see any updates. How can I update the change of the opacity in scene instantly ?
Opacity takes a value between 0.0 - 1.0. You need to divide the value you take from your slider (usually 0 - 100) by 100. Something like the following:
val = $("#slider").val() / 100;
Other option, as mentioned by prisoner849, is to set your slider values manually:
<input type="range" id="slider" min="0" max="1" step="0.01" value="0.5" class="slider">
Which allows you to get the value directly:
val = $("#slider").val();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
100
);
var renderer = new THREE.WebGLRenderer();
var light = new THREE.PointLight(0xcccccc, 1);
light.castShadows = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.SphereGeometry(1, 20, 20);
var material = new THREE.MeshPhongMaterial({
transparent: true,
opacity: 1,
color: 0xffffff
});
var sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
scene.add(light);
camera.position.z = 5;
light.position.x = 5;
light.position.y = 5;
light.position.z = 5;
document.getElementById("slider").addEventListener("change", function() {
sphere.material.opacity = document.getElementById("slider").value;
});
var render = function() {
requestAnimationFrame(render);
renderer.render(scene, camera);
};
render();
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%
}
.slider {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
<input type="range" id="slider" min="0" max="1" step="0.01" value="0.5" class="slider">
Also, you don't need to create a new material every time (it slows down the whole rendering process). You should directly edit the material applied, like so:
scene.something.material.opacity = val;
r87

Three js keyboard rotation

can anyone help me in making a 3D object rotate on a y axis? with a press of keyboard button? (B)
I've been sitting on this issue for many days now, my code breaks every time i try to do something,
please please please
this is the 3D object that needs turning
https://www.dropbox.com/s/8yefhx3yc11zbik/b.obj?dl=0
and this is the code
var lesson6 = {
scene: null,
camera: null,
renderer: null,
container: null,
controls: null,
clock: null,
stats: null,
init: function() { // Initialization
this.scene = new THREE.Scene();
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
// prepare camera
var VIEW_ANGLE = 15, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 1, FAR = 0;
this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
this.scene.add(this.camera);
this.camera.position.set(10, 200, 0);
this.camera.lookAt(new THREE.Vector3(0,30,0));
// prepare renderer
this.renderer = new THREE.WebGLRenderer({ antialias:true });
this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
this.renderer.shadowMapEnabled = true;
this.renderer.shadowMapSoft = true;
// prepare container
this.container = document.createElement('div');
document.body.appendChild(this.container);
this.container.appendChild(this.renderer.domElement);
// events
THREEx.WindowResize(this.renderer, this.camera);
// prepare controls (OrbitControls)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.target = new THREE.Vector3(0, 0, 0);
this.controls.maxDistance = 2000;
//keyboard control
// L I G H T S
//bottom
var light = new THREE.DirectionalLight(0xffffff, 1);
light.castShadow = true;
light.shadowCameraVisible = true;
light.shadowCameraNear = 100;
light.shadowCameraFar = 200;
light.shadowCameraLeft = -20; // CHANGED
light.shadowCameraRight = 20; // CHANGED
light.shadowCameraTop = 20; // CHANGED
light.shadowCameraBottom = -20; // CHANGED
light.position.set(180 ,20, 0); // CHANGED
this.scene.add(light);
this.scene.add( new THREE.DirectionalLightHelper(light, 0.2) );
//left
var dlight = new THREE.DirectionalLight(0xffffff, 1);
dlight.castShadow = true;
dlight.shadowCameraVisible = true;
dlight.shadowCameraNear = 100;
dlight.shadowCameraFar = 200;
dlight.shadowCameraLeft = -20; // CHANGED
dlight.shadowCameraRight = 20; // CHANGED
dlight.shadowCameraTop = 20; // CHANGED
dlight.shadowCameraBottom = -20; // CHANGED
dlight.position.set(0, 20, 100); // CHANGED
this.scene.add(dlight);
this.scene.add( new THREE.DirectionalLightHelper(dlight, 0.2) );
// add simple ground
// load a model
this.loadModel();
},
loadModel: function() {
// prepare loader and load the model
var oLoader = new THREE.OBJLoader();
oLoader.load('b.obj', function( geometry ) {
var material = new THREE.MeshLambertMaterial({ color: "red" });
var mesh = new THREE.Mesh (geometry, material);
geometry.traverse( function(child) {
if (child instanceof THREE.Mesh) {
// apply custom material
child.material = material;
child.castShadow = true;
child.receiveShadow = true;
}
});
geometry.rotation.y = -Math.PI / -1.7;
geometry.position.x = 0;
geometry.position.y = 0;
geometry.position.z = 0;
geometry.scale.set(1, 1, 1);
lesson6.scene.add(geometry);
});
}
};
// Animate the scene
function animate() {
requestAnimationFrame(animate);
render();
}
// Render the scene
function render() {
if (lesson6.renderer) {
lesson6.renderer.render(lesson6.scene, lesson6.camera);
}
}
// Initialize lesson on page load
function initializeLesson() {
lesson6.init();
animate();
}
if (window.addEventListener)
window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;
When dealing with that sort of input, you want the button press to NOT modify any object. You want your keyup/keydown/other input events to do as little as possible: toggle a true/false variable or change a number.
In this example:
function handleKeyDown(event) {
if (event.keyCode === 66) { //66 is "b"
window.isBDown = true;
}
}
function handleKeyUp(event) {
if (event.keyCode === 66) {
window.isBDown = false;
}
}
window.addEventListener('keydown', handleKeyDown, false);
window.addEventListener('keyup', handleKeyUp, false);
From there, you can modify your animate() function to be:
function animate() {
requestAnimationFrame(animate);
if (window.isBDown) {
//Increment your object's Y rotation value a little bit.
//Ideally, you would listen to animate()'s first argument,
//which is a high resolution timestamp that says the current time
//in milliseconds since the tab was opened.
}
render();
}
Note how all the work happens in animate(). Choose the correct speed, and the object will slowly rotate until you release the key. (Once comfortable with this concept, you can also add acceleration and momentum toanimate().)
Gamepad joysticks work the same way, except that instead of true or false, you are given a decimal number. You can multiply this into whatever value you use for "speed". Usually 0 means the joystick is centered, 1 means the joystick is maxed out at that axis, and some number between means input is being delivered, but not all the way (or is being pulled diagonally).

Recursive Call in Javascript Class (requestanimationframe)

I have a simple class that I am using to manage scenes in three.js. I am having issues with the requestAnimationFrame loop finding the function reference. I know I am missing something fundamental here, trapped in some this nightmare. Do I need to use bind or call to pass the this reference to requestAnimationFrame?
var THREE = THREE || {};
var SceneBuddy = SceneBuddy || {};
SceneBuddy = function(scene, camera) {
this.scene = scene;
this.camera = camera;
this.sceneClock = new THREE.Clock();
this.renderer = {};
this.resolution = {};
this.controls = {};
};
//Start Animate
SceneBuddy.prototype.startAnimate = function() {
requestAnimationFrame(this.startAnimate); //this does not work, type error
this.render.call(this);
};
//Render Function
SceneBuddy.prototype.render = function() {
var delta = this.sceneClock.getDelta();
this.controls.update(delta);
this.renderer.render(this.scene,this.camera);
};
//Setup Renderer
SceneBuddy.prototype.initRenderer = function(resolution) {
if (!Detector.webgl) {
Detector.addGetWebGLMessage();
return;
}
else {
var renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true
});
renderer.setSize(resolution.x, resolution.y);
renderer.shadowMapEnabled = true;
this.resolution = resolution;
this.renderer = renderer;
}
};
I am using currently using SceneBuddy like this :
var camera = new THREE.PerspectiveCamera(75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 100000);
var scene = new THREE.Scene();
var sceneBuddy = new SceneBuddy(scene, camera);
sceneBuddy.initRenderer({x: 940, y: 400});
sceneBuddy.attachRenderer(container); //attaches renderer to dom element
sceneBuddy.initControls();
sceneBuddy.startAnimate(); // broken.
Use bind when passing a function to specify what this will be when the function is called:
SceneBuddy.prototype.startAnimate = function() {
requestAnimationFrame(this.startAnimate.bind(this));
this.render();
};

Categories

Resources