partial texture update in three.js - three.js

I upload a large texture to my fragment shader as a map in three.js.
This texture is a 2D canvas on which I draw random shapes.
While drawing, I just want to update the part of the texture that has changed. Updating the whole texture (up to 3000x3000 pixel) is just too slow.
However, I can not get to perform a partial update. texSubImage2D doesn't have any effect in my scene.
No errors in the console - I expect that I am missing a step but can not figure it out yet.
//
// create drawing canvas 2D and related textures
//
this._canvas = document.createElement('canvas');
this._canvas.width = maxSliceDimensionsX;
this._canvas.height = maxSliceDimensionsY;
this._canvasContext = this._canvas.getContext('2d')!;
this._canvasContext.fillStyle = 'rgba(0, 0, 0, 0)';
this._canvasContext.fillRect(0, 0, window.innerWidth, window.innerHeight);
this._texture = new Texture(this._canvas);
this._texture.magFilter = NearestFilter;
this._texture.minFilter = NearestFilter;
// const data = new Uint8Array(maxSliceDimensionsX * maxSliceDimensionsY * 4);
// this._texture2 = new THREE.DataTexture(data, maxSliceDimensionsX, maxSliceDimensionsY, THREE.RGBAFormat);
this._brushMaterial = new MeshBasicMaterial({
map: this._texture,
side: DoubleSide,
transparent: true,
});
...
// in the render loop
renderLoop() {
...
// grab random values as a test
const imageData = this._canvasContext.getImageData(100, 100, 200, 200);
const uint8Array = new Uint8Array(imageData.data.buffer);
// activate texture?
renderer.setTexture2D(this._texture, 0 );
// update subtexture
const context = renderer.getContext();
context.texSubImage2D( context.TEXTURE_2D, 0, 0, 0, 200, 200, context.RGBA, context.UNSIGNED_BYTE, uint8Array);
// updating the whole texture works as expected but is slow
// this._texture.needsUpdate = true;
// Render new scene
renderer.render(scene, this._camera);
}
Thanks,
Nicolas

Right after creating the texture, we must notify three that the texture needs to be uploaded to the GPU.
We just have to set it 1 time at creation time and not in the render loop.
this._texture = new Texture(this._canvas);
this._texture.magFilter = NearestFilter;
this._texture.minFilter = NearestFilter;
// MUST BE ADDED
this._texture.needsUpdate = true;

Related

THREE.JS postprocessing: Duplicating postprocessed shader material to render properly on different meshes in the same scene

Think of my question as a more complicated version of this question here:
Three.js, sharing ShaderMaterial between meshes but with different uniform sets
tl;dr: I'm relying on a skin shader from THREE.js r100 to enable subsurface scattering on the meshes to make them appear more realistic but only one of them actually has the proper postprocessing to enable the effects.
The postprocessing effect is desired on all three meshes but only exists on one, the center. You can tell because the lighting looks right, and you can see where light travels "through" the mesh in thinner areas, like on the bottom part of the neck area and the ears (and if you get reaaaallly close it's in the nose too :D ).
Where the code is hosted has some wild things going on to generate the mesh, but that's beside the point, the main problem that I'm having is what you can see, only the center mesh (the one that was first added to the scene) actually has the proper effects applied to its material, the others have cloned versions of that shader but they don't render with the proper post-processing, I have a feeling it's because they're sharing uniforms or something related to that, but I'm not sure how to duplicate it properly. I was wondering how I can fix it? Do I add more render passes?
Is the way to adjust the render passes for the shader materials by adding even more passes relevant to the materials or just merely editing the uniforms (as stated in the linked question)?
I'm a bit lost and I've tried a lot to get this to work (though I'm definitely new to THREE.js post-processing passes), but you're my last hope. To someone experienced with this I feel like the solution will be very straightforward, I have a feeling I'm missing something very basic.
to view the output: https://abrasive-likeable-gateway.glitch.me/index-shader.html
to view the code: https://glitch.com/edit/#!/abrasive-likeable-gateway?path=index-shader.html%3A655%3A0
the filesystem is visible on the left side, you can remix the project on glitch (w/ a free account) to edit the code. I would've used codepen but I didn't want to deal with linking all of the three.js dependencies.
at index-shader.html on line 655 is where the setup begins for postprocessing
and at SS-xfer-placement.js on line 2838 is where rendering happens
in the same document, between lines 1900 - 2048 is the following code, which I suspect is where things are wrong, it looks like this, and imports the mesh, adds a material to it (that was set up in the html file after line 655) and adds it to the scene
the code looks like this:
setTimeout(()=>{
updateFaceParams()
setTimeout(()=>{
console.log(scene)
//send the model from ONE to THREE, init THREE
// document.querySelector("#ONEJS").style.height = "10vh!important;"
// document.querySelector("#ONEJS").style.width = "10vw!important;"
document.querySelector("#ONEJS").style.position = `absolute`
document.querySelector("#ONEJS").style.top = `0px`
document.querySelector("#ONEJS").style.right = `0px`
initTHREE();
let materialClone = window.THREEmaterial.clone()
var facePortion = scene.getObjectByName("face").geometry
console.log("FACE", scene.getObjectByName("face"))
var geometryConvert = convertGeometryToFiveBufferGeometry(facePortion)
var transferMesh = new THREE.Mesh( geometryConvert, window.THREEmaterial );
transferMesh.position.y = - 50;
transferMesh.rotation.y=3*Math.PI
// transferMesh.scale.set( scale, scale, scale );
transferMesh.scale.set(200,200,200)
transferMesh.doubleSided = true;
// console.log(transferMesh)
transferMesh.name = "face"
transferMesh.rotateY(Math.PI/180 * 180);
transferMesh.material.flatShading = false
transferMesh.material.shading = THREE.SmoothShading
THREEscene.add( transferMesh );
// console.log("test",transferMesh)
// console.log(THREEscene)
//
}, 1000)
setTimeout(()=>{
updateFaceParams()
setTimeout(()=>{
var facePortion = scene.getObjectByName("face").geometry
console.log("FACE", scene.getObjectByName("face"))
var geometryConvert = convertGeometryToFiveBufferGeometry(facePortion)
var transferMesh = new THREE.Mesh( geometryConvert, window.THREEmaterial.clone() );
transferMesh.position.y = - 50;
transferMesh.rotation.y=3*Math.PI
// transferMesh.scale.set( scale, scale, scale );
transferMesh.scale.set(200,200,200)
transferMesh.doubleSided = true;
// console.log(transferMesh)
transferMesh.name = "face"
transferMesh.rotateY(Math.PI/180 * 180);
transferMesh.material.flatShading = false
transferMesh.material.shading = THREE.SmoothShading
transferMesh.position.set(transferMesh.position.x+200, transferMesh.position.y, transferMesh.position.z)
THREEscene.add( transferMesh );
var THREErenderModelUV = new THREE.RenderPass( THREEscene,THREEcamera, window.THREEmaterialUV.clone(), new THREE.Color( 0x575757 ) );
THREEcomposer.addPass( THREErenderModelUV );
//TODO: write a stack overflow question about copying shaders!!!
setTimeout(()=>{
updateFaceParams()
setTimeout(()=>{
var facePortion = scene.getObjectByName("face").geometry
console.log("FACE", scene.getObjectByName("face"))
var geometryConvert = convertGeometryToFiveBufferGeometry(facePortion)
var transferMesh = new THREE.Mesh( geometryConvert, window.THREEmaterial.clone() );
// \var transferMesh = new THREE.Mesh( geometryConvert, new THREE.MeshPhongMaterial({color:0xffffff}) );
transferMesh.position.y = - 50;
transferMesh.rotation.y=3*Math.PI
// transferMesh.scale.set( scale, scale, scale );
transferMesh.scale.set(200,200,200)
transferMesh.doubleSided = true;
// console.log(transferMesh)
transferMesh.name = "face"
transferMesh.rotateY(Math.PI/180 * 180);
transferMesh.material.flatShading = false
transferMesh.material.shading = THREE.SmoothShading
transferMesh.position.set(transferMesh.position.x-200, transferMesh.position.y, transferMesh.position.z)
THREEscene.add( transferMesh );
// var THREErenderModelUV = new THREE.RenderPass( THREEscene,THREEcamera, window.THREEmaterialUV.clone(), new THREE.Color( 0x575757 ) );
// THREEcomposer.addPass( THREErenderModelUV );
var THREErenderModelUV = new THREE.RenderPass( THREEscene,THREEcamera, THREEmaterialUV.clone(), new THREE.Color( 0x575757 ) );
// var THREEeffectCopy = new THREE.ShaderPass( THREE.CopyShader );
// var THREEeffectBloom1 = new THREE.BloomPass( 1, 15, 2, 512 );
// var THREEeffectBloom2 = new THREE.BloomPass( 1, 25, 3, 512 );
// var THREEeffectBloom3 = new THREE.BloomPass( 1, 25, 4, 512 );
// THREEeffectBloom1.clear = true;
// THREEeffectBloom2.clear = true;
// THREEeffectBloom3.clear = true;
// THREEeffectCopy.renderToScreen = true;
// //
// var THREEpars = {
// generateMipmaps: true,
// minFilter: THREE.LinearMipmapLinearFilter,
// magFilter: THREE.LinearFilter,
// format: THREE.RGBFormat,
// stencilBuffer: false
// };
// var THREErtwidth = 512;
// var THREErtheight = 512;
//
// THREEcomposer = new THREE.EffectComposer( THREErenderer, new THREE.WebGLRenderTarget( THREErtwidth, THREErtheight, THREEpars ) );
THREEcomposer.addPass( THREErenderModelUV );
console.log(THREEcomposer)
}, 2000)
}, 2000)
}, 2000)
}, 2000)
},1000)
in other areas of the project, I wouldn't recommend looking at since it's really not relevant to this issue, the points that I highlighted are the only areas that deal with rendering, adding the mesh, and applying postprocessing.
the uniforms for the material are set up in the html file "index-shader.html" between lines 655 and 700 which may also be where I'd need to duplicate the uniforms and apply them properly, but I can't seem to figure out how to do that.
Please let me know if you have any help, thank you for reading!

Effect Composer in three.js throwing error and not rendering

I made two effect composer one for the main screen and one for mini-map view of the whole scene in minified form in top of screen.
I did this:
function postProcessing(renderer) {
composerScreen = new EffectComposer(renderer)
// RenderPass is normally placed at the beginning of the chain in order to provide the rendered scene as an input for the next post-processing step.
const renderPass = new RenderPass(scene, camera)
// When using post-processing with WebGL, you have to use FXAA for antialiasing
// Passing { antialias: true } to true when creating WebGLRenderer activates MSAA but only if you render to the default framebuffer
// (directly to screen).
const pixelRatio = 1
let fxaaPass = new ShaderPass(FXAAShader)
fxaaPass.material.uniforms['resolution'].value.x = 1 / (window.innerWidth * pixelRatio)
fxaaPass.material.uniforms['resolution'].value.y = 1 / (window.innerHeight * pixelRatio)
// const copyPass = new ShaderPass(CopyShader)
// meaning of renderToScreen. I have to set to true only for the 'last' effect
fxaaPass.renderToScreen = true
console.log(composerScreen)
composerScreen.addPass(renderPass)
composerScreen.addPass(fxaaPass)
// when screen is resized update fxaa resolution uniform as well
// The width and height of the THREE.EffectComposer.renderTarget must match that of the WebGLRenderer.
// #TODO: WHAT? Why? https://stackoverflow.com/questions/16167897/three-js-how-to-add-copyshader-after-fxaashader-r58
composerMap = new EffectComposer(renderer, renderTarget)
composerMap.setSize(512, 512)
let renderPassMap = new RenderPass(scene, mapCamera)
composerMap.addPass(renderPassMap)
var effectFXAA_Map = new ShaderPass(FXAAShader)
effectFXAA_Map.uniforms['resolution'].value.set(1 / 512, 1 / 512)
composerMap.addPass(effectFXAA_Map)
const copyPass = new ShaderPass(CopyShader)
copyPass.renderToScreen = true
composerMap.addPass(copyPass)
}
and I call
postProcessing(renderer)
and renderer variable passed in that function is:
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.autoClear = false
and mapCamera is
function initMapCamera() {
mapCamera = new THREE.OrthographicCamera(left, right, top, bottom, 0.1, 1000)
// for camera to see down up should be on z axis
mapCamera.up = new THREE.Vector3(0, 0, -1)
mapCamera.lookAt(0, 0, 0)
mapCamera.position.y = 512
scene.add(mapCamera)
}
my render code is:
function animate(now: number) {
requestAnimationFrame(animate)
const delta = now - lastTime
lastTime = now
TWEEN.update()
KeyBoardHandler.keyUpdate(handlers, keys, delta)
render()
}
function render() {
renderer.setViewport(0, 0, window.innerWidth, window.innerHeight)
console.log('composer screen', composerScreen)
composerScreen.render()
renderer.clear(false, true, false)
renderer.setViewport(20, window.innerHeight - 512, 256, 256)
composerMap.render()
// controls.update(clock.getDelta())
// labelRenderer.render(scene, camera)
// renderer.render(scene, camera)
}
I tried to add all relevant code here and this is the code repo:
https://github.com/pravinpoudel/building-annotation/blob/2bc49e956cd9a4af590aae3b66d4d64d2b9093b4/src/client/client.ts#L150
I have two branches, one is main and another is FPC which have the code with post-processing
It is not able to render anything and it is showing my error:
first one is
console.log(composerScreen)
and when i click on the error in console log
and it goes to composerScreen.render() inside render function

Change from Three.js v122 to v123 hides the shadows on a custom layer for Mapbox

I'm adding a custom layer to mapbox using three.js, using the official example from mapbox. I found that the shadows that worked perfectly in v122 are not working in v123. After reading carefully the release changelog for v123 and and the migration guide from v122, I cannot find any related commit or change that is making the shadows disappear. I also tested with the latest build available of three.js and it happens the same, but I found the change happens between these two releases. I have tested with different materials apart from ShadowMaterial but same result.
Exactly the same code, just changing the package version from:
https://unpkg.com/three#0.122.0/examples/js/loaders/GLTFLoader.js
https://unpkg.com/three#0.122.0/build/three.min.js
to
https://unpkg.com/three#0.123.0/examples/js/loaders/GLTFLoader.js
https://unpkg.com/three#0.123.0/build/three.min.js
From this:
Here is the Fiddle v122
To this...
Here is the Fiddle v123
Code is exactly the same:
mapboxgl.accessToken = 'pk.eyJ1IjoianNjYXN0cm8iLCJhIjoiY2s2YzB6Z25kMDVhejNrbXNpcmtjNGtpbiJ9.28ynPf1Y5Q8EyB_moOHylw';
var map = (window.map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
zoom: 18,
center: [148.9819, -35.3981],
pitch: 60,
bearing: 45,
antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
}));
// parameters to ensure the model is georeferenced correctly on the map
var modelOrigin = [148.9819, -35.39847];
var modelAltitude = 0;
var modelRotate = [Math.PI / 2, 0, 0];
var modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat(
modelOrigin,
modelAltitude
);
// transformation parameters to position, rotate and scale the 3D model onto the map
var modelTransform = {
translateX: modelAsMercatorCoordinate.x,
translateY: modelAsMercatorCoordinate.y,
translateZ: modelAsMercatorCoordinate.z,
rotateX: modelRotate[0],
rotateY: modelRotate[1],
rotateZ: modelRotate[2],
/* Since our 3D model is in real world meters, a scale transform needs to be
* applied since the CustomLayerInterface expects units in MercatorCoordinates.
*/
scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
};
var THREE = window.THREE;
// configuration of the custom layer for a 3D model per the CustomLayerInterface
var customLayer = {
id: '3d-model',
type: 'custom',
renderingMode: '3d',
onAdd: function(map, gl) {
this.camera = new THREE.Camera();
this.scene = new THREE.Scene();
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(0, 70, 100);
let d = 100;
let r = 2;
let mapSize = 1024;
dirLight.castShadow = true;
dirLight.shadow.radius = r;
dirLight.shadow.mapSize.width = mapSize;
dirLight.shadow.mapSize.height = mapSize;
dirLight.shadow.camera.top = dirLight.shadow.camera.right = d;
dirLight.shadow.camera.bottom = dirLight.shadow.camera.left = -d;
dirLight.shadow.camera.near = 1;
dirLight.shadow.camera.far = 400;
dirLight.shadow.camera.visible = true;
this.scene.add(dirLight);
this.scene.add(new THREE.DirectionalLightHelper(dirLight, 10));
this.scene.add(new THREE.CameraHelper(dirLight.shadow.camera))
// use the three.js GLTF loader to add the 3D model to the three.js scene
var loader = new THREE.GLTFLoader();
loader.load(
'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',
function(gltf) {
gltf.scene.traverse(function(model) {
if (model.isMesh) {
model.castShadow = true;
}
});
this.scene.add(gltf.scene);
// we add the shadow plane automatically
const s = new THREE.Box3().setFromObject(gltf.scene).getSize(new THREE.Vector3(0, 0, 0));
const sizes = [s.x, s.y, s.z];
const planeSize = Math.max(...sizes) * 10;
const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
//const planeMat = new THREE.MeshStandardMaterial({ color: 0xffffff, side: THREE.DoubleSide});
const planeMat = new THREE.ShadowMaterial();
planeMat.opacity = 0.5;
let plane = new THREE.Mesh(planeGeo, planeMat);
plane.rotateX(-Math.PI / 2);
//plane.layers.enable(1); plane.layers.disable(0); // it makes the object invisible for the raycaster
plane.receiveShadow = true;
this.scene.add(plane);
}.bind(this)
);
this.map = map;
// use the Mapbox GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true
});
this.renderer.autoClear = false;
this.renderer.shadowMap.enabled = true;
},
render: function(gl, matrix) {
var rotationX = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(1, 0, 0),
modelTransform.rotateX
);
var rotationY = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 1, 0),
modelTransform.rotateY
);
var rotationZ = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 0, 1),
modelTransform.rotateZ
);
var m = new THREE.Matrix4().fromArray(matrix);
var l = new THREE.Matrix4()
.makeTranslation(
modelTransform.translateX,
modelTransform.translateY,
modelTransform.translateZ
)
.scale(
new THREE.Vector3(
modelTransform.scale,
-modelTransform.scale,
modelTransform.scale
)
)
.multiply(rotationX)
.multiply(rotationY)
.multiply(rotationZ);
this.camera.projectionMatrix = m.multiply(l);
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
};
map.on('style.load', function() {
map.addLayer(customLayer, 'waterway-label');
});
It seems like a bug, but honestly I'd love is something I'm missing or I didn't realize. I even checked if with the files from different CDNs happens the same, and yes, it happens the same. Hoping one of the Three.js contributors or other devs can help me with this as I'm completely blocked with this and stopping me to migrate
Thanks in advance for any pointer!
A new implementation of WebGLState.reset() will be available with r126. It does not only reset engine internal state flags but also calls WebGL commands to reset the actual WebGL state. This approach should solve the reported issue.
Link to the PR at GitHub: https://github.com/mrdoob/three.js/pull/21281
I included this suggestion in the comments for the pull request on github https://github.com/mrdoob/three.js/pull/20732, but I'll add it here as well incase someone only finds this Stack Overflow question when searching.
I ran into a similar issue using another library that was sharing the WebGL context. What I found was that the other library (imgui-js) was setting gl.BLEND to true and then, with the change in the mentioned pull request, WebGLState.Reset() is now setting currentBlendingEnabled to null.
This caused a lot of textures in my scene to be displayed incorrectly because when setBlending is subsequently called on the WebGLState, it assumes that if the desired blending method is NoBlending and currentBlendingEnabled is null, that gl.BLEND is already disabled:
function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) {
if (blending === NoBlending) {
if ( currentBlendingEnabled ) {
disable(3042);
However, with reset nulling the currentBlendingEnabled value each frame but not setting gl.BLEND to false, I believe this assumption is no longer correct. Looking closer, even after removing the external library I was using that sets the gl.BLEND value to true, I found that the change in the pull request was having a negative impacting some of the textures in my scene. In my case I found that updating the setBlending function to honor NoBlending requests, including when currentBlendingEnabled is null, seems to have remedied the situation. Maybe that will work in your case too?
function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) {
if (blending === NoBlending) {
if (currentBlendingEnabled !== false) {
disable(3042);

ThreeJS image texture on merged objects

Help! I'm trying to create and merge two different objects in THREEjs. I have one image and I need it to be stretched over the entire merged object once. Currently it is doing it twice, once on the top and once on the bottom, like they are still two separate objects.
var texture = new THREE.TextureLoader().load('//s1.postimg.org/26dva6d58v/peace.png');
texture.alphaTest = 0.9;
texture.needsUpdate = true;
color = new THREE.MeshStandardMaterial( {
side: THREE.DoubleSide,
color: 0x949494,
map: texture
} );
var size = {'top': 1.395, 'bottom': 1.11, 'height': 3.5};
cylinderGeo1 = new THREE.CylinderGeometry( size.top, size.bottom, size.height, 50, null, true );
cylinderGeo1.applyMatrix( new THREE.Matrix4().makeTranslation(0, size.height/2, 0) );
cylinder1 = new THREE.Mesh(cylinderGeo1);
cylinderGeo2 = new THREE.CylinderGeometry( size.top * 2,size.top, size.height, 50, null, true );
cylinderGeo2.applyMatrix( new THREE.Matrix4().makeTranslation(0, (size.height/2) + size.height, 0) );
cylinder2 = new THREE.Mesh(cylinderGeo2);
cupGeo = new THREE.Geometry();;
cylinder1.updateMatrix();
cupGeo.merge(cylinderGeo1, cylinder1.matrix);
cylinder2.updateMatrix();
cupGeo.merge(cylinderGeo2, cylinder2.matrix);
cup = new THREE.Mesh(cupGeo, color);
cup.updateMatrix();
scene.add(cup);
https://jsfiddle.net/dj2k6y8t/16/
[edit] You may need to zoom on the canvas to get it render.

threejs merge plane geometries or create a texture from multiple images

I have a set of plane geometries, each having a their own texture from a url. At any point during navigation(zoom/pan) the container (THREE.Object3D) contains 26 plane geometries. How will i merge them to a single big plane so that i could apply a heighmap for all tiles in the merge geometry.
Or How could I get all the texture from the 36 images.(currently as a map property for MeshPhongMaterial) in to a single geometry?
EDIT:
Currently I create a Big geometry as suggested by Dainel. and put a texture to the geometry which is a combined texture of a set of images via canvas.
context = canvas.getContext( '2d' );
var img = Image();
img.src = url //url of the texture tile
context.drawImage(img,xpos, ypos);
This is done or every images. Each image have a url, xpos, ypos. Images are preloaded and after loading every images a callback is called which creates the geometry(plane) and add texture from the canvas.
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({ map : texture });
var geom = new THREE.PlaneGeometry(canvas.width, canvas.height, 57, 40);
var mesh = new THREE.Mesh(geom, material);
scene.add( mesh );
also i update the z vertex of geometry with the height values.
u may create one "big" plane and apply a merged texture to it
or u could manually set the vertex Y-values of each plane to match the corresponding part of the height map. so u have a set of separate planes that all share the same "base" height map.
regards,
daniel
Using terrainLoader class for threejs and canvas texture I was able to acheive
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, mywidth / myheight, 0.01, 10000);
camera.position.set(0, 0, 240);
scene.add(new THREE.AmbientLight(0xeeeeee));
renderer = new THREE.WebGLRenderer();
renderer.setSize(mywidth, myheight);
var group = new THREE.Object3D();
var canvas = document.createElement( 'canvas' );
canvas.width = mywidth;
canvas.height = myheight;
var ctx = canvas.getContext('2d');
/* adjust width, height, segments based on height data;*/
/* create a plane geometry */
var geom = new THREE.PlaneGeometry(800, 692, 40, 55);
/*
read height values from .bin file uing terrainLoader
and update the Z values of the plane
*/
heightdata = 'assets/heightmap.bin';
terrainLoader.load(heightdata, function(data) {
/* bin file generated from USGS SRTM tile */
for (var i = 0, l = geom.vertices.length ; i < l; i++) {
geom.vertices[i].z = data[i] / 200; /*simple scale */
}
geom.verticesNeedUpdate = true;
});
/*
create a texture from canvas created above with width and height
of the of the scene. This is to be careful as the size of texture
image may not be the same but as same ratio as height map.
Atleast for my case it was the situation. But geometry width and
texture width are in same ratio. So that i need a small array of
heightmap from bin but a much bigger texture
*/
var texture = new THREE.Texture(canvas);
var material = new THREE.MeshPhongMaterial({ map : texture });
var mesh = new THREE.Mesh(geom, material);
group.add( mesh );
scene.add(group);
Now to draw multiple images to canvas check this page
I will just duplicate the code from HTML5 Canvas Image Loader Tutorial
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg',
yoda: 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg'
};
You can call the loadImages function after updating the Plane geometry from heightmap in above code.
loadImages(sources, function(images) {
//context = context of the texture put in threejs texture
context.drawImage(images.darthVader, 100, 30, 200, 137);
context.drawImage(images.yoda, 350, 55, 93, 104);
});

Resources