I have an A-Frame scene that contains, among others, a <canvas> element that is the material source for a 3D scene object. I can paint on the canvas programmatically, and it shows up as texture. So far, so good.
However, I'd now also like to enable the user to paint something on the canvas using the controllers. I have added two raycasters/controls:
<a-entity laser-controls="hand: left" raycaster="objects: table2"></a-entity>
<a-entity laser-controls="hand: right" raycaster="objects: table2"></a-entity>
And on the table2 object, I have added a raycaster-listen mixin as described in https://aframe.io/docs/1.3.0/components/raycaster.html#listening-for-raycaster-intersection-data-change.
This works in so far as I get the console log entries with the world coordinates of the intersection point, but I'm absolutely stuck at how to get from the world coordinates back to the canvas coordinates I need to actually paint in the right spot.
In addition, it seems no canvas draw commands I issue in the raycaster-listen tick callback actually have any visible effect (regardless of coordinates).
Any hints appreciated!
As usual, I figured it out the next day 😉
[...] I'm absolutely stuck at how to get from the world coordinates back to the canvas coordinates I need to actually paint in the right spot.
Solution found at https://discourse.threejs.org/t/convert-camera-frustrum-to-uv-coordinate-on-texture/16791/2 - just use intersection.uv which actually contains the normalized texture coordinates of the intersection point. Scale by canvas width/height and you're done.
[...] it seems no canvas draw commands I issue in the raycaster-listen tick callback actually have any visible effect.
Solution found at aframe not rendering lottie json texture mapped to canvas but works in three.js - set texture.needsUpdate = true; in the tick callback after drawing on the canvas.
Related
I've been looking for a solution to this problem for a project I'm working on. I'd like to know if there is a way to click on a plane geometry and draw onto it, paint style, and have the texture update as you draw on it.
After this, is there a way to save that texture while in the browser now that you've edited it?
You could use raycasting to determine the UV texture position where the mouse intersection takes place. Raycaster.intersectObject returns an array of objects, one of its properties is .uv. You can see it in action in this demo.
You can use a 2D <canvas> as the source of the texture. Once you have the UVs from the intersection, you could use that position to draw on the canvas as demonstrated in this other demo
I have an existing WebGL canvas that is being rendered without using ThreeJS, and is for all intents and purposes a black box to me, apart from two facts: (1) I have access to the underlying webgl canvas DOM element and can position and resize it on the screen, and (2) I know the properties of the camera for the scene, and get updates on every render cycle for that camera.
The problem I need to solve can be simplified to the following: I need to have my own separate ThreeJS canvas that displays both the black box canvas data, and then elements that I draw, like a cube for a simple example. I can already easily overlay the two canvases, set the transparency on my canvas for everything but the cube, and then align the two with the camera events from the black box library. This works quite well.
The issue with this is that when I draw my objects, like a cube, they don't respect the depth buffer of the black box canvas. So I might have a cube that is properly aligned with the backing scene and movements of the scene, but then it isn't properly masked when something in the black box canvas is closer to the camera than the cube. My thought is that I need to solve this in one of two ways: (1) I can have my renderer write to the other canvas with autoClear = false and preserveDrawingBuffer = true, or (2) I can somehow copy the depth buffer from the black box canvas into my canvas, and then set up my renderer so that it respects the new depth buffer.
I haven't been successful with either approach yet, so I'm wondering if this is possible, and if so which of the above approaches, or what other approach, can solve this problem?
--Edit--
See https://jsfiddle.net/zdxyoajb/ for angular/typescript implementation of the above attempts. In the following animate loop, if I comment out the overlayRenderer lines, the below sphere will be red and offset from the center (as it should be), but if I don't comment the lines, I get the below image. I also get the following error:
WebGL: INVALID_OPERATION: uniformMatrix4fv: location is not from current program
animate() {
requestAnimationFrame(() => this.animate());
this.blackBoxCamera.copy(this.overlayCamera);
this.blackBoxRenderer.render(this.blackBoxScene, this.blackBoxCamera);
this.overlayRenderer.state.reset();
this.overlayRenderer.render(this.overlayScene, this.overlayCamera);
}
I'm working with an orthographic view in three.js/WebGL renderer, and I want a magnifying glass that tracks with the user mouse. I'm looking for the best way of doing this that's efficient.
When working with html5 canvas raw commands, this was easy: I simply defined a circular clip region, zoomed my coordinates, and re-drew the whole scene. With 3d objects, it's less obvious how do to it.
The method I've found so far is to do the following:
Define a second camera that looks into the zoomed region. Set the orthographic clip coordinates to be small so that it doesn't need to do much work
Create a THREE.WebGLRenderTarget
Tell the renderer and all my line textures that the resolution is about to change
Render the scene into the RenderTarget
Add a CircleGeometry as a MeshObject at the spot at the mouse position (in world coordinate but above the rest of the scene, close to the camera). Call this the lens.
Give the lens the WebGLRenderTarget as a texture.
Go back to my default camera, reset all my resolution parameters, and redraw the scene with the 'lens' object added.
This works (see image below) but I'm worried about parts of it:
I have to render twice per frame
Lines don't draw well, because the resolution problems. I have to keep track of all materials that need to know screen resolution and update all of them twice per screen render.
Related problems:
I want to overlay some plot axes on top of this, and possibly gridlines. These would change as the view pans. I'm not sure if I should make these 3d objects, or do it in a 2d canvas context I lay overtop.
I want to overlay some plot lines, and have them show up sensibly in the zoomed view. "Sensible" here is hard to figure out: I don't want them too fat in the zoomed view, but I also don't want to scale them up as much as the image detail (which is being rendered as a texture onto Plane objects behind).
This is a long post, but I'm still new to three.js and looking for good ideas.
When I add to the scene two objects and set their transparency as true with some opacity and using TrackballControls I rotate the scene by mouse, the object which was initially further from camera loses its transparency.
I read that this is Z-buffer problem and further objects from camera will be displayed first. But when I rotate the scene using TrackballControls, camera changes its position, so transparent objects should be displayed correctly. But it is not like that.
Here in this picture - on the right is frontview, on the left is backview which is not displayed correctly:
http://www.foto-ondruskova.cz/Experiment/lenses.jpg
Please, any solutions?
I have come across this problem and setting alphaTest: 0.5 to the material as suggested here solved my problem. But it is hit and miss. Give it a try. Hopefully it works!
Is it possible in three.js to create transparent/invisible plane, which can receive shadows? My aim is to draw some object on 3d canvas without background (so I can see webpage behind the canvas element). I want the object to cast shadows on the background and I thought, if I could make an invisble plane, then webpage background is still visible. Shadows are casted on the plane and it seems like shadows are on the webpage.
Right now I can make a white plane with opacity 0.5 (or similar), but this way the plane is visible. Ideally the plane should be completely invisble (except for shadows).
EDIT: I created an exampled (based on this): http://jsfiddle.net/s5YGu/2/
Here I have opacity 0.5, but you can see the plane. If I set opacity to 0, then the whole plane and the shadows disappear.
I made this work by hacking on basic shader, here is working shader code http://pastie.org/5088640
It has no drawbacks AFAIK, like opacity: 0.1
Yes, you can achieve the effect you want, and it looks pretty good on my machine at least. :-)
Here is a fiddle: http://jsfiddle.net/ZXdV3/25/
There are a couple of issues. First, you will have to set alpha: true in the renderer constructor args.
Second, although I assume you'd like the plane to be completely transparent, you'll have to settle for material.opacity = 0.1, or so.
Third, if you are placing a canvas over the webpage, and you want the web page to be interactive, you are going to have to suppress pointer events in the canvas (I'm not sure if IE supports this, though): container.style.pointerEvents = 'none';
three.js r.67