Search code examples
videothree.jstextures

Video texture playing but showing black


I'm trying to create a video texture. My code will add the video to my scene, however my cube is just black. The video is there somewhere as I can hear the audio playing in my browser. I'm new to three.js so I'm not sure what I'm doing wrong. I have a camera and three lights. I can see the other objects in my scene and their textures are visible. My code is adapted from here. Here is my HTML code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script src="libraries/three.js"></script>
    <script src="libraries/OBJLoader2.js"></script>
    <script src="libraries/OrbitControls.js"></script>
    <script src="libraries/MtlLoader.js"></script>
    <script src="libraries/DDSLoader.js"></script>
</head>

<body>
<script src="libraries/main.js"></script>
</body>
</html>

And here is my main.js code:

//global variables
var renderer;
var scene;
var camera;
var cameraControl;
var loader;
var updateFcts  = [];

function createRenderer() {
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
}

function createCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth /window.innerHeight, 0.1, 1000);
camera.position.x = 0;
camera.position.y = 100;
camera.position.z = 450;
camera.lookAt(scene.position);
cameraControl = new THREE.OrbitControls(camera);
}

var THREEx = THREEx || {}
THREEx.VideoTexture = function(url){
var video   = document.createElement('video');
video.width = 320;
video.height    = 240;
video.autoplay  = true;
video.loop  = true;
video.src   = url;
this.video  = video;
var texture = new THREE.Texture( video );
this.texture    = texture;
this.update = function(){
    if( video.readyState !== video.HAVE_ENOUGH_DATA )   return;
    texture.needsUpdate = true;     
}
  this.destroy  = function(){
    video.pause()
  }
}

function createVideo() {
var videoTexture= new THREEx.VideoTexture('models/textures/video.mp4')
var video   = videoTexture.video;
updateFcts.push(function(delta, now){
    videoTexture.update(delta, now)
});
var geometry    = new THREE.CubeGeometry(10,50,10);
var material    = new THREE.MeshBasicMaterial({
    map : videoTexture.texture,
    side: THREE.DoubleSide
});
var mesh    = new THREE.Mesh( geometry, material );
scene.add( mesh );
updateFcts.push(function(delta, now){
    mesh.rotation.x += 1 * delta;
    mesh.rotation.y += 2 * delta;       
});
}

function createLight() {
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(10, 40, 20);
spotLight.shadowCameraNear = 20;
spotLight.shadowCameraFar = 50;
spotLight.castShadow = true;
scene.add(spotLight);
var ambient = new THREE.AmbientLight( 0x444444 );
ambient.castShadow = true;
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0.5, 0.5 ).normalize();
scene.add( directionalLight );
}

function init() {
scene = new THREE.Scene();
createRenderer();
createCamera();
createLight();
createVideo();
document.body.appendChild(renderer.domElement);
render();
}

 function render() {
cameraControl.update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}

init();

Solution

  • You have to create a canvas for the video so you can use it for build texture. Also you have to refresh every frame in render loop.

    var renderer;
    var scene;
    var camera;
    var cameraControl;
    var loader;
    var updateFcts = [];
    var video;
    var videoImageContext;
    var videoTexture;
    
    function createRenderer() {
        renderer = new THREE.WebGLRenderer();
        renderer.setClearColor(0x111111, 1.0);
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMap.enabled = true;
    }
    
    function createCamera() {
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.x = 0;
        camera.position.y = 100;
        camera.position.z = 450;
        camera.lookAt(scene.position);
        cameraControl = new THREE.OrbitControls(camera);
    }
    
    var THREEx = THREEx || {}
    THREEx.VideoTexture = function(url) {
        video = document.createElement('video');
    
        video.autoplay = true;
        video.loop = true;
        video.src = url;
        video.load();
        video.play();
    
        videoImage = document.createElement('canvas');
        videoImage.width = 320;
        videoImage.height = 240;
        videoImageContext = videoImage.getContext('2d');
        videoImageContext.fillStyle = '#000000';
        videoImageContext.fillRect(0, 0, videoImage.width, videoImage.height);
    
        this.texture = new THREE.Texture(videoImage);
        this.texture.minFilter = THREE.LinearFilter;
        this.texture.magFilter = THREE.LinearFilter; 
    
        this.destroy = function() {
            video.pause()
        }
    }
    
    function createVideo() {
        videoTexture = new THREEx.VideoTexture('myvideo.mp4')
        updateFcts.push(function(delta, now) {
            videoTexture.update(delta, now)
        });
        var geometry = new THREE.CubeGeometry(10, 50, 10);
        var material = new THREE.MeshBasicMaterial({
            map: videoTexture.texture,
            side: THREE.DoubleSide
        });
        var mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);
        updateFcts.push(function(delta, now) {
            mesh.rotation.x += 1 * delta;
            mesh.rotation.y += 2 * delta;
        });
    }
    
    function createLight() {
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(10, 40, 20);
        spotLight.shadowCameraNear = 20;
        spotLight.shadowCameraFar = 50;
        spotLight.castShadow = true;
        scene.add(spotLight);
        var ambient = new THREE.AmbientLight(0x444444);
        ambient.castShadow = true;
        scene.add(ambient);
        var directionalLight = new THREE.DirectionalLight(0xffeedd);
        directionalLight.position.set(0, 0.5, 0.5).normalize();
        scene.add(directionalLight);
    }
    
    function init() {
        scene = new THREE.Scene();
        createRenderer();
        createCamera();
        createLight();
        createVideo();
        document.body.appendChild(renderer.domElement);
        render();
    }
    
    function render() {
        cameraControl.update();
        if (video.readyState === video.HAVE_ENOUGH_DATA) {
            videoImageContext.drawImage(video, 0, 0);
            if (videoTexture)
                videoTexture.texture.needsUpdate = true;
        }
        renderer.render(scene, camera);
        requestAnimationFrame(render);
    }
    
    init();
    

    I have changed the THREEx.VideoTexture function and render loop and tested with r75 and works!