I am trying to change the color of a DirectionalLight based on the theme of the webpage, either Light mode or Night Mode, it works but you have to refresh the webpage in order to see the changes
Here is my code which reads a hex color from a JSON of styled-components and then convert it to a ThreeJS color like 0xffffff
var directionalLight = new THREE.DirectionalLight(
new THREE.Color(theme.background),
0.6
);
directionalLight.position.set(-10, 10, 5);
directionalLight.castShadow = true;
directionalLight.shadow.bias = -0.0005;
scene.add(directionalLight);
is there a way to update the light each frame so i can see the change in real time?
You can simply update the light’s .color property. For example, this will update the color to orange:
directionalLight.color.set(0xff9900);
This way you can switch the light’s color without having to refresh the page.
Related
I am currently trying to create a mesh that is colored using a datatexture, my initial coloring shows up just fine, but now my next goal is to offset the texture along the y axis. very similar to this example.
http://math.hws.edu/graphicsbook/demos/c5/textures.html
How I create my texture / mesh:
this.colorTexture = new DataTexture(colors, this.frameWidth, frameCount, RGBFormat, FloatType, UVMapping, RepeatWrapping, RepeatWrapping);
const material = new MeshBasicMaterial({
side: FrontSide,
vertexColors: true,
wireframe: false,
map: this.colorTexture
});
this.mesh = new Mesh(geometry, material);
How I attempt to animate the texture using offset:
this.mesh.material.map.offset.y -= 0.001;
this.mesh.material.map.needsUpdate = true;
this.mesh.material.needsUpdate = true;
this.mesh.needsUpdate = true;
I have confirmed that the function I'm using to try to offset is being called during each animation frame, however the visualization itself is not animating or showing changes apart from the initial positioning of the colors I wrote to the texture.
Any help is greatly appreciated :)
The uv transformation matrix of a texture is updated automatically as long as Texture.matrixAutoUpdate is set to true (which is also the default value). You can simply modulate Texture.offset. There is no need to set any needsUpdate flags (Mesh.needsUpdate does not exist anyway).
It's best if you strictly stick to the code from the webgl_materials_texture_rotation example. If this code does not work, please demonstrate the issue with a live example.
If I do not set initial position to the camera, the WEB borwser and Oculus Go browser behave differently (see images below).
const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
// camera position is Vector3(0, 0, 0)
scene.add( camera );
Pic. 1 - Initial camera position in web browser (e.g. Google Chrome)
Pic. 2 - Initial camera position in VR browser (e.g. default browser of Oculus Go)
It looks like Three.js's scene knows in which environment it runs and automatically adjust the camera position. How can I change the initial position of the camera?
Currently, I am doing somethink like this:
const cameraHolder = new Group();
cameraHolder.add(camera);
cameraHolder.position.set(0, 1, 0);
scene.add(cameraHolder);
but again, it doesn't resolve the issue with different positions in different environments.
I believe this is the behaviour of the reference space. If you use reference space type of *-floor the device knows about how tall you are. The camera y is then set to your device y position, which makes sense. As I couldn't find where to grab the device height from I just set the reference space type to local.
this.renderer.xr.setReferenceSpaceType( 'local' );
For me the scene.add(cameraHolder) was necessary, otherwise the camera wouldn't receive the position/rotation. Old HMDs had the cameras attached to a "head" model, then you would turn the head instead of the camera.
You can use
renderer.xr.addEventListener()
Example
camera = new THREE.PerspectiveCamera(35, width / height, 1, 15);
camera.position.set(0, 0.6, 3); // Set the initial Camera Position.
const cameraGroup = new THREE.Group();
cameraGroup.position.set(0, -1, 1.5); // Set the initial VR Headset Position.
//When user turn on the VR mode.
renderer.xr.addEventListener('sessionstart', function () {
scene.add(cameraGroup);
cameraGroup.add(camera);
});
//When user turn off the VR mode.
renderer.xr.addEventListener('sessionend', function () {
scene.remove(cameraGroup);
cameraGroup.remove(camera);
});
I am working on a POC in which I have to customize a watch in 3d model.
I am using thee.js for doing webgl stuff. I was able to change strap color , dial color and load texture images on strap. But I am stuck when I try to add dynamic text on the the watch , I have to update text on watch's back dial as user types in Engrave Text text box . For this I tried https://github.com/jeromeetienne/threex.dynamictexture library , which works on a new geometry but I am not able to add it watch's existing model.
I have pushed all code in github : https://github.com/bhupendra1011/watch-3d-engrave , as I was not able to load external 3d models from my fiddle account {CORS issue}.
Demo of the POC can be seen here : https://bhupendra1011.github.io/watch-3d-engrave/index.html
Below is the code to add text on watch dial; For loading text click on use textures textbox in index.html
function engraveTextOnWatch(val = "IWC") {
dynamicTexture = new THREEx.DynamicTexture(512, 512);
dynamicTexture.context.font = "bolder 90px Verdana";
// watch back dial geometry & material where text needs to be engraved
var backDialGeometry = object3d.children[1].children[0].children[0].geometry;
var backDialMaterial = object3d.children[1].children[0].children[0].material;
// geometry to add dynamic text
var geometry = new THREE.CubeGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
map: dynamicTexture.texture
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh); // adding mesh to scene , but this needs to be attache to watch's back dial geomtry
/* tried adding dynamicTexture to watch back dial material, but this is not working , text not appearing */
//backDialMaterial.map = dynamicTexture.texture;
dynamicTexture.texture.needsUpdate = true;
dynamicTexture.drawText(val, 2, 256, "orange");
}
Kindly let me know how can I add dynamic text on existing model.
Thanks.
I was able to reproduce your problem in Blender and it comes from your model's UV map.
I downloaded your model, opened it in Three.js editor and converted it to .obj to open it in blender to reproduve your problem
As you can see on the image, applying a texture is just as messy. You need to rework the model's uvmap to achieve the desired effect like so (I used a random image from your folder)
And while you're at it, you might want to consider optimizing your model, which is pretty heavy and could be simplified for faster load without losing visual quality (but it's entirely up to you, I'm just suggesting).
Here's the current model
Here's a quick possible replacement. Cylinder that's open on one end and beveled on the edge loop
I have a DirectionalLight in my scene. When I first create the light the shadow covers the intended area. When the viewport is resized (canvas size) I update the shadowCamera parameters (right,left,top,bottom), and the light's position and target, but the shadow only covers the area before the update.
What flag do I have to set to get the shadow properly updated to reflect the new settings?
Here is the pattern you need to follow to update a shadow camera on-the-fly:
light.shadowCameraLeft = -100;
light.shadowCameraRight = 100;
light.shadowCamera.left = light.shadowCameraLeft;
light.shadowCamera.right = light.shadowCameraRight;
...
light.shadowCamera.updateProjectionMatrix();
three.js r.64
I want to render my scene at twice the resolution of my canvas and then downscale it before displaying it. How would I do that using threejs?
for me the best way to have a perfect AA with not too much work (see the code below)
ps :if you increase more than 2 its start to be too sharpen
renderer = new THREE.WebGLRenderer({antialiasing:true});
renderer.setPixelRatio( window.devicePixelRatio * 1.5 );
This is my solution. The source comments should explain what's going on. Setup (init):
var renderer;
var composer;
var renderModel;
var effectCopy;
renderer = new THREE.WebGLRenderer({canvas: canvas});
// Disable autoclear, we do this manually in our animation loop.
renderer.autoClear = false;
// Double resolution (twice the size of the canvas)
var sampleRatio = 2;
// This render pass will render the big result.
renderModel = new THREE.RenderPass(scene, camera);
// Shader to copy result from renderModel to the canvas
effectCopy = new THREE.ShaderPass(THREE.CopyShader);
effectCopy.renderToScreen = true;
// The composer will compose a result for the actual drawing canvas.
composer = new THREE.EffectComposer(renderer);
composer.setSize(canvasWidth * sampleRatio, canvasHeight * sampleRatio);
// Add passes to the composer.
composer.addPass(renderModel);
composer.addPass(effectCopy);
Change your animation loop to:
// Manually clear you canvas.
renderer.clear();
// Tell the composer to produce an image for us. It will provide our renderer with the result.
composer.render();
Note: EffectComposer, RenderPass, ShaderPass and CopyShader are not part of the default three.js file. You have to include them in addition to three.js. At the time of writing they can be found in the threejs project under the examples folder:
/examples/js/postprocessing/EffectComposer.js
/examples/js/postprocessing/RenderPass.js
/examples/js/postprocessing/ShaderPass.js
/examples/js/shaders/CopyShader.js
Here's how you might be able to work it out: In your three.js initialization code, when you create your renderer, make it double the dimensions of your primary canvas, and set it to render to a secondary, hidden canvas element that is twice as large as your primary canvas. Perform the necessary image manipulation on the secondary canvas, and then display the result on the primary canvas.