I have a < a-sphere> with an SVG wrapped around but it shows pixel. However, it seems like a-frame doesn't support Vectorgraphics.
Is there any solution to this?
I'd try drawing the svg onto a canvas element, and then turning the canvas into a texture using threejs:
html
<a-box svg-load></a-box>
js
AFRAME.registerComponent('svg-load', {
init: function() {
// grab the canvas element
var canvas = document.getElementById('my-canvas')
var ctx = canvas.getContext('2d');
// create an image, which will contain the svg data
var img = new Image();
// this will be triggered when the provided .svg is loaded
img.onload = () => {
// draw the image on the canvas
ctx.drawImage(img, 0, 0, 256, 256);
// create the texture and material
let texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
let material = new THREE.MeshBasicMaterial({ map: texture });
// grab the mesh, replace the material, dispose the old one
let mesh = this.el.getObject3D("mesh")
let tmp = mesh.material
mesh.material = material
tmp.dispose()
}
// provide the .svg file as the image source
img.src = "file.svg";
}
)}
Glitch here
You can also provide the canvas as a material source, but it still looks glitchy to me.
<a-box material="src: my-canvas">
Related
I was hoping that ThreeJS has some way of animating text besides on top of a plane. I would prefer to animate the text as 2D and just floating above a model. I attempted to use divs outside of the canvas, but that was having issues pointing to the correct place and still remaining responsive.
I want it to look something like this, while animating the line underneath the text.
What you can do is you want to create a 2d canvas and use that as the texture, something like this
var canvas1 = document.createElement('canvas');
var context1 = canvas1.getContext('2d');
context1.font = "Bold 40px Arial";
context1.fillStyle = "rgba(255,0,0,0.95)";
context1.fillText('Hello, world!', 0, 50);
// canvas contents will be used for a texture
var texture1 = new THREE.Texture(canvas1)
texture1.needsUpdate = true;
var material1 = new THREE.MeshBasicMaterial( {map: texture1, side:THREE.DoubleSide } );
material1.transparent = true;
var mesh1 = new THREE.Mesh(
new THREE.PlaneGeometry(canvas1.width, canvas1.height),
material1
);
I'm experiencing with Three JS after reading an article about augmented reality using a webcam.
Like experiment, i try to put a simple canvas in a scene, where before i have put a webglrender, by insert an webcam stream.
I read in this article (https://hacks.mozilla.org/2013/10/an-ar-game-technical-overview/) that for the virtual scene, is needed create a virtual scene, where is renderer by the object webglrenderer, and a reality scene(the webcam). In my file testingthreejs.js, between the line 30 and 41 is where i wrote the lines for create the new scene (the virtual), and where i created and inserting the canvas inside my mesh object.
But nothing happen, ¿how i can insert the canvas like a Mesh object?
I try to avoid draw a canvas, as it is supposed that the rendering by canvas is more slow that rendering with webgl (i also think use the ray class)
This is my gist : https://gist.github.com/fernandosg/75ec701c0295761a77e6, inside are the files testingthreejs.js and index.html.
Thanks for your help.
You can take a look at the Stemkoski s website which has a lot of examples with very nice explanations.
I think this is what you are looking for, take a look at the script.
More precisely, here is the commented code :
/////// draw text on canvas /////////
// create a canvas element
var canvas1 = document.createElement('canvas');
var context1 = canvas1.getContext('2d');
context1.font = "Bold 40px Arial";
context1.fillStyle = "rgba(255,0,0,0.95)";
context1.fillText('Hello, world!', 0, 50);
// canvas contents will be used for a texture
var texture1 = new THREE.Texture(canvas1)
texture1.needsUpdate = true;
var material1 = new THREE.MeshBasicMaterial( {map: texture1, side:THREE.DoubleSide } );
material1.transparent = true;
var mesh1 = new THREE.Mesh(
new THREE.PlaneGeometry(canvas1.width, canvas1.height),
material1
);
mesh1.position.set(0,50,0);
scene.add( mesh1 );
/////// draw image on canvas /////////
// create a canvas element
var canvas2 = document.createElement('canvas');
var context2 = canvas2.getContext('2d');
// canvas contents will be used for a texture
var texture2 = new THREE.Texture(canvas2);
// load an image
var imageObj = new Image();
imageObj.src = "images/Dice-Blue-1.png";
// after the image is loaded, this function executes
imageObj.onload = function()
{
context2.drawImage(imageObj, 0, 0);
if ( texture2 ) // checks if texture exists
texture2.needsUpdate = true;
};
var material2 = new THREE.MeshBasicMaterial( {map: texture2, side:THREE.DoubleSide} );
material2.transparent = true;
var mesh2 = new THREE.Mesh(
new THREE.PlaneGeometry(canvas2.width, canvas2.height),
material2
);
mesh2.position.set(0,50,-50);
scene.add( mesh2 );
}
I think that maybe i am getting closer to the solution, finally i can put the canvas (using a MeshBasicMaterial and a PlaneGeometry), but now the canvas was drawed, the video was not rendered:
problem canvas
The code:
var canvas=document.createElement("canvas");
canvas.width=512;
canvas.height=512;
var video=document.createElement("video");
streamVideo(video);
var canvasContext=canvas.getContext("2d");
var glCanvas=document.createElement("canvas");
var renderer_webgl=new THREE.WebGLRenderer({canvas:glCanvas});
renderer_webgl.setSize(512,512);
renderer_webgl.autoClear=false;
document.getElementById("container").appendChild(glCanvas);
var camera = new THREE.Camera();
var scene=new THREE.Scene();
var geometry=new THREE.PlaneGeometry(2,2,0);
var texture=new THREE.Texture(canvas);
var material=new THREE.MeshBasicMaterial({
map:texture,
depthTest:false,
depthWrite:false
});
var mesh=new THREE.Mesh(geometry,material);
canvas_draw=document.createElement("canvas");
canvas_draw.width=128;
canvas_draw.height=128;
ctx=canvas_draw.getContext("2d");
ctx.fillStyle = "#AAA";
ctx.fillRect(1,1,20,25);
// HERE IS WHEN INSERT THE CANVAS
var texture_canvas=new THREE.Texture(canvas_draw);
texture_canvas.needsUpdate=true;
var basic_material_canvas=new THREE.MeshBasicMaterial({
map:texture_canvas
});
var mesh_canvas=new THREE.Mesh(new THREE.PlaneGeometry(2,2,0),basic_material_canvas);
scene.add(mesh);
scene.add(mesh_canvas);
function renderScenes(){
renderer_webgl.render(scene,camera);
}
function loop(){
if(video.readyState===video.HAVE_ENOUGH_DATA){
canvas.changed=true;
canvasContext.drawImage(video,0,0);
verifyDetector();
texture.needsUpdate=true;
texture_canvas.needsUpdate=true;
renderScenes();
}
requestAnimationFrame(loop);
}
loop();
function verifyDetector(){
detector = new AR.Detector();
imageData = canvasContext.getImageData(0, 0, canvas.width, canvas.height);
var markers = detector.detect(imageData);
/*if(markers.length>0)
canvasContext.drawImage(canvas_draw,markers[0].corners[0].x,markers[0].corners[0].y);
*/
}
¿Why this happen?
I'm a three.js beginner. Trying to apply an html canvas as texture to plane geometry. Works fine when there is no rotation. When rotated, a) outside edges of the mesh become jagged (edges/lines within the image are fine) and b) text becomes blurry.
What do I have to do to keep outside edges and text crisp?
My texture is power-of-two (128 x 512). Turning antialiasing on doesn't help.
Here is a screenshot without rotation
And here with rotation.
Code looks like this:
var eltTexture = toTexture2(d, this, go.w, go.Res.ow[0]);
// map texture to material
var material = new THREE.MeshBasicMaterial(
{ map : eltTexture } );
//define sub image of texture to be applied to mesh
var cutout = [
new THREE.Vector2(0, (128 - go.Res.ow[0])/128),
new THREE.Vector2(go.w/512, (128 - go.Res.ow[0])/128),
new THREE.Vector2(go.w/512, 1),
new THREE.Vector2(0, 1)];
// geometry and UV mapping
var geometry = new THREE.PlaneGeometry (go.w, go.Res.ow[0]);
geometry.faceVertexUvs[0] = []; // initialize
geometry.faceVertexUvs[0][0] =
[cutout[3], cutout[0], cutout[2]];
geometry.faceVertexUvs[0][1] =
[cutout[0], cutout[1], cutout[2]];
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set(d.threeX, d.threeY, d.threeX);
mesh.rotation.set(0,0.6,0);
scene.add( mesh );
});
renderer.render( scene, camera );
function toTexture2 (d, svgNode, svgWidth, svgHeight){
// step 1: serialize eltSVG to xml
var eltXML = new XMLSerializer().serializeToString(svgNode);
eltXML = 'data:image/svg+xml;charset = utf8,' + eltXML;
// step 2: draw eltXML to image
var eltImage = document.body.appendChild(
document.createElement("img"));
eltImage.id = "eltImage";
eltImage.style.display = "none";
eltImage.width = svgWidth;
eltImage.height = svgHeight;
eltImage.src = eltXML;
// step 3: draw image to canvas
// NOTE: define canvas parameters position, width and
// height in html, NOT IN CSS, otherwise image
// will become blurry - don't ask why!
var eltCanvas = document.body.appendChild(
document.createElement("canvas"));
eltCanvas.id = "eltCanv";
eltCanvas.style.display = "none";
eltCanvas.width = 512;
eltCanvas.height = 128;
// get context
var ctx = eltCanvas.getContext("2d", {alpha: false});
// draw svg element to canvas, not including portrait image
ctx.drawImage(eltImage, parseInt(0), parseInt(0),
svgWidth, svgHeight);
// draw portrait image to canvas
var portrait = document.getElementById(d.nameConcat + "Img");
ctx.globalAlpha = 0.6; // opacity of portrait image
ctx.drawImage(portrait, go.Res.strw[0], go.Res.strw[0],
go.Res.iw[0], go.Res.iw[0]);
var texture = new THREE.Texture(eltCanvas);
texture.needsUpdate = true;
return texture;
} // function toTexture2
Many thanks in advance for your help!
Even though it's been a long time since you've posted this, I have the solution in case anyone else is experiencing the same issue.
Add this to your renderer:
const renderer = new THREE.WebGLRenderer({antialias: true});
I have an idea to create, but since my knowledge about Three.js and 3D programming in general is limited, I am stuck...
Idea: User is doing some things. At some point - whole front screen crashes, and reveals something different behind.
At the beginning, my idea was - make screenshot, of what is happening right now, put that image in front of everything (still, having difficulties making that plane to take 100% size - idea - when it shows, user cannot tell the difference, between old 3d renderings, and new 2d picture), and then - shatter it. So - by looking around the Web for different examples, I made some... thing - Created screenshot, made plane and applied screenshot as a texture to it. To create shattering effect, I used TessellateModifier to divide that plane, and ExplodeModifier to create each face as separated face.
Here will be the code I made so far.
function drawSS()
{
var img = new Image;
img.onload = function() { // When screenshot is ready
var canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var context = canvas.getContext('2d');
// CANVAS DRAWINGS
// Draws screenshot
// END
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var multiMaterial = [
new THREE.MeshBasicMaterial({map: texture, side: THREE.FrontSide}), // canvas drawings
new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: true, transparent: true}) // for displaying wireframe
];
var geometry = new THREE.PlaneGeometry(canvas.width, canvas.height); // create plane
for (var i = 0, len = geometry.faces.length; i < len; i++) { // Snippet from some stackoverflow post, that works.
var face = geometry.faces[i].clone();
face.materialIndex = 1;
geometry.faces.push(face);
geometry.faceVertexUvs[0].push(geometry.faceVertexUvs[0][i].slice(0));
}
geometry.dynamic = true;
THREE.GeometryUtils.center( geometry ); // ?
var tessellateModifier = new THREE.TessellateModifier( 10 );
for ( var i = 0; i < 5; i ++ )
tessellateModifier.modify( geometry );
new THREE.ExplodeModifier().modify( geometry );
geometry.vertices[0].x -=300;
geometry.vertices[1].x -=300;
geometry.vertices[2].x -=300;
var mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(multiMaterial) );
scene.add( mesh );
};
// THEN, set the src
img.src = THREEx.Screenshot.toDataURL(renderer);
}
For now - I moved 1 face, by changing coordinates of 3 vertices. I'm asking - if this approach is the way to go? Result looks like this. (white lines - wireframe, black lines - (drawings in canvas) my desired wireframes - problem for later). Note - by moving this way - texture goes along, I don't know, if I make new triangles using these vertices, how would I set texture.
My question is how can I bind texture to a particle in
use of THREE.ParticleCanvasMaterial ?
I mean what is the syntax to bind texture to particle?
You should try to use a ParticleBasicMaterial and pass in your Texture as the map property of the material:
var material = new THREE.ParticleBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'yourTexture.png' ) } );
Also, have a look at the canvas_particle_sprites example. Notice that it's generating a texture on the fly, but you can load your own texture as described above. From experience, the CanvasRenderer is slow with many/large textures.
Update 2019: ParticleBasicMaterial is deprecated
ParticleBasicMaterial has been renamed to PointsMaterial.
https://threejs.org/docs/#api/en/deprecated/DeprecatedList
You need to use PointsMaterial instead of ParticleBasicMaterial
var snowMaterial = new THREE.PointsMaterial({
size: 7,
blending: THREE.AdditiveBlending,
map: new THREE.TextureLoader().load("./assets/snowflake.png"),
//createCircleTexture("#ffffff", 20),
transparent: true,
depthWrite: false
});
Alternatively you can use canvas to draw Circular shapes for particle texture as suggest by George:
export const createCircleTexture = (color, size) => {
var matCanvas = document.createElement("canvas");
matCanvas.width = matCanvas.height = size;
var matContext = matCanvas.getContext("2d");
// create texture object from canvas.
var texture = new THREE.Texture(matCanvas);
// Draw a circle
var center = size / 2;
matContext.beginPath();
matContext.arc(center, center, size / 2, 0, 2 * Math.PI, false);
matContext.closePath();
matContext.fillStyle = color;
matContext.fill();
// need to set needsUpdate
texture.needsUpdate = true;
// return a texture made from the canvas
return texture;
};
Test Image
Result