I have a house model in my game, and I have some materials for the house geometry. There is a material for the wall of the house, and I have a texture-map-image to show the bricks.
var mat = new THREE.MeshPhongMaterial( {
ambient: 0x969696,
map: THREE.ImageUtils.loadTexture( 'textures/G/G0.jpg' ),
overdraw: true,combine: THREE.MultiplyOperation
} );
In this way above, the texture map appears like GL_CLAMP I want it to show like GL_REPEAT.
What should I do?
If you can not see the images check this.
I have posted a full working example at:
http://stemkoski.github.com/Three.js/Texture-Repeat.html
The relevant part of the code example is:
// for example, texture repeated twice in each direction
var lavaTexture = THREE.ImageUtils.loadTexture( 'images/lava.jpg' );
lavaTexture.wrapS = lavaTexture.wrapT = THREE.RepeatWrapping;
lavaTexture.repeat.set( 2, 2 );
var lavaMaterial = new THREE.MeshBasicMaterial( { map: lavaTexture } );
var lavaBall = new THREE.Mesh( THREE.GeometryUtils.clone(sphereGeom), lavaMaterial );
scene.add( lavaBall );
It's called THREE.RepeatWrapping there. The loadTexture defaults to THREE.ClampToEdgeWrapping (see ctor function from previous link). Don't know if you can use the callback (because this in JS is a bit weird (looks like it points to the created Image, not the created Texture)). Signature:
loadTexture: function ( path, mapping, callback ) {
Better you just name the texture locally and set the wrap modes manually:
var t = THREE.ImageUtils.loadTexture( 'textures/G/G0.jpg' );
t.wrapS = t.wrapT = THREE.RepeatWrapping;
Looks like you're not going far with threejs without looking at the actual code...
Image must be 8x8, 16x16, 32x32, 128x128, 256x256, 512x512 etc.
And all be working.
=)
Related
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!
I try to import a Ready Player Me GLB avatar into a Matterport scene (in ThreeJS).
It works but the avatar is very flat (left picture) compared to the rendering into RPM (right picture) or basic GLTF Viewer. I don't understand what I missed ? Pseudo code :
const gltfLoader = new THREE.GLTFLoader();
gltfLoader.load('https://api.readyplayer.me/v1/avatars/63580183b445da7aa57b9ce3.glb',
(gltf) => {
this.mesh = gltf.scene;
const light = new THREE.AmbientLight( 0xFFFFFF, 1 );
light.castShadow = true
this.mesh.add( light );
...
}, undefined);
On the WebGL part I have access to Matterport renderer :
(renderer, THREE, effectComposer) => {
renderer.outputEncoding = THREE.sRGBEncoding
renderer.toneMapping = THREE.ACESFilmicToneMapping
}
Is it related to Antialias ? Or a paramter or lighting that flatten hair and hands ? Or is it related to texture ?
Thanks !
EDIT 11/12/2022:
Thanks to #Mugen87, I find how to use the EquirectangularShader from GLTFLoader sample. This code works with Matterport context.
let pmremGenerator = new THREE.PMREMGenerator( this.context.renderer );
pmremGenerator.compileEquirectangularShader();
let envMap = pmremGenerator.fromScene( new THREE.RoomEnvironment() ).texture;
this.context.scene.environment = envMap;
Instead of using a single instance of AmbientLight, apply a more advanced type of environmental lighting to your scene. Especially if you already enabled tone mapping, consider to use a HDR environment map. The basic GLTFLoader example of three.js is a good code template. The most important part is:
new RGBELoader()
.load( 'textures/equirectangular/royal_esplanade_1k.hdr', function ( texture ) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
// more scene setup
} );
Applying an environment map to Scene.environment ensures all PBR materials in the scene are going to use it.
I'm very new with Three.js and I'm trying to make a ring:
http://www.websuvius.it/atma/myring/preview.html
I have a background texture ( the silver one ) and another one with a text.
I want the text only on the ring external face.
This is part of my code:
var loader = new THREE.OBJLoader( manager );
var textureLoader = new THREE.TextureLoader( manager );
loader.load( 'assets/3d/ring.obj', function ( event ) {
var object = event;
var geometry = object.children[ 0 ].geometry;
var materials = [];
var backgroundTexture = textureLoader.load('img/texture/silver.jpg');
backgroundTexture.flipY = false;
var background = new THREE.MeshBasicMaterial({
map: backgroundTexture,
color: 0xffffff
});
materials.push(background);
var customTexture = textureLoader.load('img/text.png');
customTexture.flipY = false;
var custom = new THREE.MeshBasicMaterial({
map: customTexture,
transparent: true,
opacity: 1,
color: 0xffffff
});
materials.push(custom);
mesh = THREE.SceneUtils.createMultiMaterialObject(geometry, materials);
mesh.position.y=-50;
scene.add(mesh);
}, onProgress, onError );
It is possible?
Thanks
The reason behind your issue appears to be in your .obj file. Judging from a quick glance at the texture coordinates stored in the file, the inside of the ring uses the same part of the texture image as the outside of the ring.
Increasing the transparent parts of the image won't help. Neither will the attempts to stop the texture from repeating. Those would help if the texture coordinates were larger than 1 but this is not your case unfortunately.
However, there are several solutions:
Split the object in a 3D modeling software to two objects - outside and inside of the ring - and apply the texture only to the first one.
Adjust the UV coordinates of the object in a 3D modeling software.
Adjust the UV coordinates of the vertices programmatically after loading the object to Three.JS
I am trying to use THREE.Raycaster to show an html label when the user hover an object. It works fine if I use THREE.Mesh but with THREE.Sprite it looks like that there is a space that increases with the scale of the object.
The creation process is the same for both scenario, I only change the type based on USE_SPRITE variable.
if ( USE_SPRITE ) {
// using SpriteMaterial / Sprite
m = new THREE.SpriteMaterial( { color: 0xff0000 } );
o = new THREE.Sprite( m );
} else {
// using MeshBasicMaterial / Material
m = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
o = new THREE.Mesh(new THREE.PlaneGeometry( 1, 1, 1 ), m );
}
https://plnkr.co/edit/J0HHFMpDB5INYLSCTWHG?p=preview
I am not sure if it is a bug with THREE.Sprite or if I am doing something wrong.
Thanks in advance.
three.js r73
I would consider this a bug in three.js r.75.
Raycasting with meshes in three.js is exact. However, with sprites, it is an approximation.
Sprites always face the camera, can have different x-scale and y-scale applied (be non-square), and can be rotated (sprite.material.rotation = Math.random()).
In THREE.Sprite.prototype.raycast(), make this change:
var guessSizeSq = this.scale.x * this.scale.y / 4;
That should work much better for square sprites. The corners of the sprite will be missed, as the sprite is treated as a disk.
three.js r.75
I'm pretty new to three.js and I tried for hours to create a skybox/skydome for a better visual feeling to my world (in this case space). Googled, checked tutorials, asked here on StackOverflow. And nothing worked, or I got a silly and dumb answer here on SO. Question is simple: how to make a skybox/dome?
This is how you do a skydome in threejs.
var skyGeo = new THREE.SphereGeometry(100000, 25, 25);
First the geometry. I wanted it big and made it big
var loader = new THREE.TextureLoader(),
texture = loader.load( "images/space.jpg" );
Loads the texture of your background space. One thing here is that you need it to run through a server to be able to load the texture. I use wamp or brackets preview.
Create the material for the skybox here
var material = new THREE.MeshPhongMaterial({
map: texture,
});
Set everything together and add it to the scene here.
var sky = new THREE.Mesh(skyGeo, material);
sky.material.side = THREE.BackSide;
scene.add(sky);
This might not be the best solution for this, but it´s easy specially for a beginner in threejs. Easy to understand and create.
This is how you can load image as texture and apply that on innerside of a sphere geometry to emulate skydome.
Complete solution with error callback for future reference
//SKY
var loader = new THREE.TextureLoader();
loader.load(
"./assets/universe.png",
this.onLoad,
this.onProgress,
this.onError
);
onLoad = texture => {
var objGeometry = new THREE.SphereBufferGeometry(30, 60, 60);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
objMaterial.side = THREE.BackSide;
this.earthMesh = new THREE.Mesh(objGeometry, objMaterial);
scene.add(this.earthMesh);
//start animation
this.start();
};
onProgress = xhr => {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
};
// Function called when download errors
onError = error => {
console.log("An error happened" + error);
};