three.js failing to use pre-created canvas - three.js

I want to use a pre-created canvas with three.js. From the tutorials and other posts I've read, this should work:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext("2d"); // <--- This causes the error below!
const renderer = new THREE.WebGLRenderer( { canvas: canvas } );
However, in my browser console (Safari v14 and Chrome v86), I get the following error:
THREE.WebGLRenderer: Error creating WebGL context.
I've also tried adding
<canvas id='myCanvas'></canvas>
and using:
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d"); // <--- This causes the same error!
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
});
and get the same issues.
I've also tried adding:
window.onload = function() {
...
};
to ensure the DOM has loaded, etc.
If I remove the getContext("2d") lines then it works?
I'm using three.js version 0.120.
Why does this cause an issue?

Three.js only throws this error in one place, and luckily it's doing something very simple: Getting a context from a canvas. It uses HTMLCanvasElement.getContext to do this, and only throws the error if the result is null.
HTMLCanvasElement.getContext will only allow you to request one context type. You can request the same type (or compatible, in the case of webgl and webgl2) again, but it will return the original context created on the canvas. After the first request establishes the in-use context, subsequent requests for incompatible types will return null.
let ctx_2d_1 = mycanvas.getContext( '2d' )
let ctx_2d_2 = mycanvas.getContext( '2d' )
console.log( ctx_2d_1 === ctx_2d_2 ) // true
let ctx_2d = mycanvas.getContext( '2d' )
let ctx_webgl = mycanvas.getContext( 'webgl' )
console.log( ctx_2d ) // CanvasRenderingContext2D
console.log( ctx_webgl ) // null
let ctx_webgl = mycanvas.getContext( 'webgl' )
let ctx_2d = mycanvas.getContext( '2d' )
console.log( ctx_webgl ) // WebGLRenderingContext
console.log( ctx_2d ) // null
Because you are creating the 2d context before calling the WebGLRenderer constructor, the constructor can't get a valid WebGLRenderingContext from the canvas. Instead, it gets a null, and so it throws the error.
If you want to draw 2D on top of 3D (or vice versa), you will either need to layer canvases of different contexts, or use a plane to render the 2D information as a texture within the WebGL (three.js) context.

Related

OutlinePass is not rendered to scene in THREE.js

I followed these examples to make the outline for objects when they are selected:
https://threejs.org/examples/?q=out#webgl_postprocessing_outline
https://github.com/scqilin/three-OutlinePass
No error is found, yet outline does not appear when the object is selected. The highlightSelectedObject function is correcly triggered when an object is selected. selectedObjects is not null.
In my case, THREE.js is installed in the project file. Scene, camera and renderer are instantiated elsewhere.
import * as THREE from "../../build/three.module.js";
import {OutlinePass} from "../../examples/jsm/postprocessing/OutlinePass.js";
import {RenderPass} from "../../examples/jsm/postprocessing/RenderPass.js";
import {EffectComposer} from "../../examples/jsm/postprocessing/EffectComposer.js";
Function:
function highlightSelectedObject(selectedObjects) {
if (selectedObjects != null) {
const scene = project.currentScene.scene;
const camera = project.currentScene.camera;
const renderer = project.renderer;
var composer = new EffectComposer(renderer);
var renderPass = new RenderPass(scene, camera);
var outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera, selectedObjects);
outlinePass.renderToScreen = true;
outlinePass.selectedObjects = selectedObjects;
composer.addPass(renderPass);
composer.addPass(outlinePass);
const params = {
edgeStrength: 2,
edgeGlow: 1,
edgeThickness: 1.0,
pulsePeriod: 0,
usePatternTexture: false
};
outlinePass.edgeStrength = params.edgeStrength;
outlinePass.edgeGlow = params.edgeGlow;
outlinePass.visibleEdgeColor.set(0xffffff);
outlinePass.hiddenEdgeColor.set(0xffffff);
composer.render(scene, camera);
}
}
The path to THREE.js should be correct. Is it a problem with render?
I had a similar issue. Upon looking at another example, I found that setting outlinePass.renderToScreen = true allowed it to work. It might not be there depending what version of the the outlinePass.js you are using. I looked at the code on the deployed example and it is there.

A-Frame & Three.js: Color map makes object white

I'm trying to assign a new material to an object, but when I assign a new (color) map, the object renders as white, and the AO and shadows no longer show up. It's as if the emissive attribute is 100%. I can change the color attribute (e.g. 'red' or 'blue'), ao, normal, etc. without issues. The glb loaded in already has a working material with a color map and ao, but I want to be able to replace it.
I'm using 8th Wall with A-Frame, but I've registered the following as a custom Three.js component.
const customMat = {
schema: {}, // will pass textures via aframe later
init() {
this.el.addEventListener('model-loaded', (e) => {
const material = new THREE.MeshStandardMaterial()
const texLoader = new THREE.TextureLoader()
texLoader.crossOrigin = ''
const mapColor = texLoader.load('assets/cover_color.jpg')
const mapAO = texLoader.load('assets/cover_ao.jpg')
material.map = mapColor // makes everything 100% white likes it's emissive
// material.color = new THREE.Color('red') // works fine no problem
material.aoMap = mapAO
material.aoMapIntensity = 1
e.detail.model.traverse((mesh) => {
if (mesh.isMesh) {
mesh.material = material
mesh.material.needsUpdate = true // not sure if needed
}
})
})
},
}
export {customMat}
Any suggestions would be much appreciated. I've tried this with primitive geometry too, but the same issue occurs. I don't seem to be able to modify the existing material's attributes either, so maybe my approach is fundamentally wrong.

A-Frame THREE.TextureLoader loading texture whice is looking whitewashed

I am updating an a-image scr using THREE.TextureLoader
let loader = new THREE.TextureLoader()
const imgload = loader.load(
'./test.png',
function ( texture ) {
firstFrameImage.getObject3D('mesh').material.map = texture
firstFrameImage.getObject3D('mesh').material.needsUpdate = true
},
// onProgress callback currently not supported
undefined,
// onError callback
function ( err ) {
console.error( 'An error happened.' );
}
)
Its updating the texture but its making the texture whitewashed. Can any one help?
Original image:
original
Updated texture coming as:
after update
Try to fix this by doing this:
texture.encoding = THREE.sRGBEncoding;
Color deviations like this mostly occur because of wrong color space definitions.

how to Combe three.js page into A-frame page

Here is a Three.js Example from stemkoski, now I want to use this Texture-Animation plane or box in A-frame page, how can I Combine it.
A-frame Version: 0.9.0
I couldn't find any examples.
When integrating three.js pieces into aframe, it's recommended to use custom components. Here's a simple example:
js
AFRAME.registerComponent('foo', {
// this is called upon initialization
init: function() {
// we'll need this later on for updating the animation
this.animator = null
// wait until the component is loaded
this.el.addEventListener('loaded', e => {
// copied straight from stemkoski's code:
var runnerTexture = new THREE.ImageUtils.loadTexture( 'images/run.png' );
this.animator = new TextureAnimator( runnerTexture, 10, 1, 10, 75 );
// apply the texture to our element
let mesh = this.el.getObject3D('mesh')
mesh.material.map = runnerTexture
mesh.material.needsUpdate = true
})
},
// this is called before each render loop
tick: function(time, delta) {
// update only if animator was created
if (!this.animator) return
this.animator.update(1000 * delta);
}
})
HTML:
<a-plane foo></a-plane>
glitch here. To make it work with a glitch i had to preload the image with a-assets due to cors issues.

WebGLRenderingContext ERROR loading texture maps

First of all, thank you for this wonderfull work, i'm having a lot of fun working with three.js.
I tried to find answer about a recurent issue, .WebGLRenderingContext: GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 2
I'm making a website in webgl, i spend few week understanding all about three.js but i can't fix this issue.
I get this message on Chrome and firefox (latest) each time i try to load a canvas into a map, bumpmap and specmap.
All my mesh are loaded from obj files, by the way i rewrote OBJMTLLoader.js to be able to load more parameters from obj files and more.
here the code used to load image.
THREE.MTLLoader.loadTexture = function ( url, mapping, onLoad, onError ) {
var isCompressed = url.toLowerCase().endsWith( ".dds" );
var texture = null;
if ( isCompressed ) {
texture = THREE.ImageUtils.loadCompressedTexture( url, mapping, onLoad, onError );
} else {
var image = new Image();
texture = new THREE.Texture( image, mapping );
var loader = new THREE.ImageLoader();
loader.addEventListener( 'load', function ( event ) {
texture.image = THREE.MTLLoader.ensurePowerOfTwo_( event.content );
texture.needsUpdate = true;
if ( onLoad )
onLoad( texture );
} );
loader.addEventListener( 'error', function ( event ) {
if ( onError ) onError( event.message );
} );
loader.crossOrigin = this.crossOrigin;
loader.load( url, image );
}
return texture;
};
I'm pretty sure it is from this, because when i disable this function, no more warning.
Is it because the mesh has a texture with an empty image while loading datas ?
Is there any restriction on the dimensions of image ?
For now everything works fines, but i feel strange having those message in console.
Thanks
This error become because the Three.js buffers are outdated. When your add some textures (map,bumpMap ...) to a Mesh, you must recompose the buffers like this :
ob is THREE.Mesh, mt is a Material, tex is a texture.
tex.needsUpdate = true;
mt.map = tex;
ob.material = mt;
ob.geometry.buffersNeedUpdate = true;
ob.geometry.uvsNeedUpdate = true;
mt.needsUpdate = true;
That's all folks !
Hope it's help.
Regards.
Sayris

Resources