Unusual antialias while using basic texture material in three.js - javascript

In the following code I am setting up a very basic scene with an orthographic camera and a canvas mapped as a texture to a plane geometry.
I put some white text on to a transparent canvas, and if I use canvas.toDataURL(), the effect isn't present.
However, when I apply the canvas contents to a material as a texture and render it within a super-standard 2d scene, a black line outlines my text, probably the result of weird antialias stuff happening. In this example, the renderer clear color, material, and text are all pure white.
Here is a screenshot:
var camera = new THREE.OrthographicCamera(window.innerWidth / - 2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / - 2, 0, 10);
var scene = new THREE.Scene();
canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 300;
var context = canvas.getContext('2d');
context.fillStyle = "white";
context.font = "bold 72px Arial";
context.fillText("Zibri", 50, 100);
var texture = new THREE.Texture(canvas);
var geometry = new THREE.PlaneGeometry(canvas.width, canvas.height);
texture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial({ color: 0xffffff, map: texture, transparent: true });
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({ antialias: false });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0xffffff)
document.body.appendChild( renderer.domElement );
camera.lookAt(scene.position);
renderer.render(scene, camera);

I've also been struggling with this problem, WestLangley's solution came part way to fixing the issue but left the rendered text with very poorly antialiased edges. After extensive research, I've come up with a solution that I'm happy with which I've outline here.
Before drawing the text, fill the canvas with the same colour as the text but with alpha set to 0.01
context.fillStyle = 'rgba(255,255,255,0.01)';
context.fillRect(0,0,canvas.width,canvas.height);
and then use the material's alphaTest property to discard pixels of this opacity:
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
map: texture,
transparent: true,
alphaTest:0.01
});
finally, set the texture map's premultiplyAlpha value to false:
texture.map.premultiplyAlpha = false;
texture.map.needsUpdate = true;

Related

scaling down and repeating a texture in threejs

I have quite a large plane with a set displacement map and scale which I do not want to be changed. I simply want the loaded texture to apply to that mesh without it having to scale up so largely.
Currently, a floor texture doesn't look like a floor as it has been upscaled to suit the large plane.
How would I be able to scale down the texture and multiply it across the plane so it looks more like actual terrain?
const tilesNormalMap = textureLoader.load(
"./textures/Stylized_Stone_Floor_005_normal.jpg"
);
function createGround() {
let disMap = new THREE.TextureLoader().load("./models/Heightmap.png");
disMap.wrapS = disMap.wrapT = THREE.RepeatWrapping;
disMap.repeat.set(4, 2);
const groundMat = new THREE.MeshStandardMaterial({
map: tilesBaseColor,
normalMap: tilesNormalMap,
displacementMap: disMap,
displacementScale: 2
});
const groundGeo = new THREE.PlaneGeometry(300, 300, 800, 800);
let groundMesh = new THREE.Mesh(groundGeo, groundMat);
scene.add(groundMesh);
groundMesh.rotation.x = -Math.PI / 2;
groundMesh.position.y -= 1.5;
I tried using the .repeat method as shown below but i can't figure out how this would be implemented
tilesBaseColor.repeat.set(0.9, 0.9);
tilesBaseColor.offset.set(0.001, 0.001);
a photo of the current ground texture
enter image description here
First of all what you want to achieve does currently not work with three.js since it's only possible to a have a single uv transform for all textures (except for light and ao map). And map has priority in your case so you can't have different repeat settings for the displacement map. Related issue at GitHub: https://github.com/mrdoob/three.js/issues/9457
Currently, a floor texture doesn't look like a floor as it has been upscaled to suit the large plane. How would I be able to scale down the texture and multiply it across the plane so it looks more like actual terrain?
In this case, you have to use repeat values great 1 otherwise you zoom into the texture. Do it like in the following live example:
let camera, scene, renderer;
init().then(render);
async function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 1;
scene = new THREE.Scene();
const loader = new THREE.TextureLoader();
const texture = await loader.loadAsync('https://threejs.org/examples/textures/uv_grid_opengl.jpg');
// repeat settings
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);
const geometry = new THREE.PlaneGeometry();
const material = new THREE.MeshBasicMaterial({map: texture});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function render() {
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.148/build/three.min.js"></script>

Setting the position of a text geometry?

I have looked through stack overflow and google and I have found how to CENTER a text geometry but that is not what I want to do.
I have a scene that just has a block of text that says "Buy Here!". Using the documentation in the three.js website and examples here I was able to do that after some struggling. I had some trouble finding out how to refer to that mesh since I had created the geometry inside a function, and it took hours for me to know about setting a name for it as a string so it can be accessible from different parent/child levels.
What I am NOT able to do now is to offset the text by some arbitrary number of units. I tried shifting it down by 5 units. No matter how I try to do it it isn't working. I either manage to make the text geometry disappear OR my whole scene is black.
Here is my code...
I have the basic scene setup working properly and I'll include it here but feel free to skip since I'm pretty sure this has nothing to do with the issue...
import './style.css'
import * as THREE from 'three';
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three#0.117.0/examples/jsm/controls/OrbitControls.js';
import TWEEN from 'https://cdn.jsdelivr.net/npm/#tweenjs/tween.js#18.5.0/dist/tween.esm.js';
//BASIC SCENE SETUP
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
//LIGHTS (POINT AND AMBIENT)
const pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.set(5, 5, 5);
const ambientLight = new THREE.AmbientLight(0xFFFFFF);
scene.add(pointLight, ambientLight);
//RESIZE WINDOW
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
render();
}, false);
//ORBIT CONTROLS
const controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 5;
controls.maxDistance = 70;
controls.enablePan = false;
controls.enableRotate = false;
controls.enableZoom = false;
controls.target.set(0,0,-1);
camera.position.setZ(25);
window.addEventListener("click", (event) => {
onClick(event);
})
window.addEventListener("mousemove", onMouseMove);
var animate = function() {
requestAnimationFrame(animate);
controls.update();
render();
TWEEN.update();
};
function render() {
renderer.render(scene, camera);
}
animate();
and here is my code for the text object....
var loaderF = new THREE.FontLoader();
loaderF.load( 'https://threejs.org/examples/fonts/optimer_regular.typeface.json', function ( font ) {
var geometry = new THREE.TextGeometry( 'Buy Here!', {
font: font,
size: 2.3,
height: 0.1,
curveSegments: 15,
bevelEnabled: true,
bevelThickness: 0.5,
bevelSize: 0.31,
bevelSegments: 7
} );
geometry.center();
var material = new THREE.MeshLambertMaterial({color: 0x686868});
var mesh = new THREE.Mesh( geometry, material );
mesh.name = "bhText"
scene.add( mesh );
mesh.userData = { URL: "http://google.com"};
} );
Here's what I have tried.....
under "var geometry ({...});" I typed....
geometry.position.setX(-5);
but the text object disappears completely so I tried
geometry.position.setX = -5;
but there was no difference so i tried taking out
geometry.center();
but it had the same results.
So then I tried using
mesh.position.x = -5;
with AND without
geometry.center();
but again, they all just make my text object disappear.
So now I tried to set the position from outside the function by typing the following code OUTSIDE of everything that is contained in
loaderF.load ('https.....', function (font){var geometry = .....})
using the reference I learned....
scene.getObjectByName("bhText").position.x(-5);
but this makes my entire scene go blank (black). So I tried variations of like
scene.getObjectByName("bhText").position.x = -5;
scene.getObjectByName("bhText").position.setX(-5);
scene.getObjectByName("bhText").position.setX = -5;
mesh.position.setX = -5;// I was pretty sure this wasn't going to work since I wasn't
//using the mesh name specifically for when it's inside something
//I can't reach because of parent-child relations
and again trying each of those with AND without
geometry.center();
but they all made my scene go black.
I just wanna move it down a couple of units. Sheesh.
Could anyone be kind enough to tell me WHERE in my code I can set the position of the text geometry? Thank you please.
I just wanna move it down a couple of units.
In this case use mesh.position.y = - 5;. Changing the x coordinate will move the mesh to the left or right. Here is a complete live example based on your code:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set( 0, 0, 10 );
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.set(5, 5, 5);
const ambientLight = new THREE.AmbientLight(0xFFFFFF);
scene.add(pointLight, ambientLight);
const loader = new THREE.FontLoader();
loader.load('https://threejs.org/examples/fonts/optimer_regular.typeface.json', function(font) {
const geometry = new THREE.TextGeometry('Buy Here!', {
font: font,
size: 2,
height: 0.5
});
geometry.center();
const material = new THREE.MeshLambertMaterial({
color: 0x686868
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = - 1; // FIX
mesh.name = "bhText"
scene.add(mesh);
renderer.render(scene, camera);
});
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/build/three.min.js"></script>

ThreeJS different shapes object texture not working true

I am using dynamic texture on threejs. When i use different shapes object, texture not working fine. But, When i use boxGeometry , it's working fine.
setCanvas =()=>{
this.canvas = window.document.getElementById("canvas");
var ctx = this.canvas.getContext("2d");
ctx.font = "20pt Arial";
ctx.fillStyle = "red";
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
ctx.fillStyle = "white";
ctx.fillRect(10, 10, this.canvas.width - 20, this.canvas.height - 20);
ctx.fillStyle = "yellow";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("asdasdasd", this.canvas.width / 2, this.canvas.height / 2);
}
different shapes object creator
threeObjectCreator(mesh, position, color) {
const object = this.objectLoader.parse(mesh);
object.position.set(position.x, position.z, position.y);
object.rotation.x = Math.PI / 2;
let texture = new THREE.Texture(this.canvas);
object.material = new THREE.MeshPhongMaterial({
color,
side: THREE.DoubleSide,
shininess: 30,
//flatShading: THREE.FlatShading,
transparent: false,
map:texture
});
texture.needsUpdate = true;
return object;
}
with box geometry
let geometry = new THREE.BoxGeometry( 200, 200, 200 );
let mesh = new THREE.Mesh(geometry, material);
this.scene.add(mesh);
How can i solve this problem ?
When using textures in OpenGL (ThreeJS uses this via WebGL), its important that you properly set the UV parameters on your faces.
These UV parameters tell how to morph and transform an 2d texture into 3d space.
Lets see the UV parameters on our simple box model (new THREE.BoxGeometry()):
[[
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}]
]]
This looks scary
To understand these coordinates, you have to understand that our box is made up of 12 "faces", or triangles in this case, 2 triangles per side.
Interesting things happen when we modify 1 of the coordinates in the UV grid:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 3,3,3 );
var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
new THREE.TextureLoader().load( "https://threejs.org/examples/textures/uv_grid_opengl.jpg", (texture) => {
material.map = texture;
renderer.render( scene, camera );
});
geometry.faceVertexUvs =
[[
[{"x":0,"y":1},{"x":0,"y":-1},{"x":2,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}],
[{"x":0,"y":1},{"x":0,"y":0},{"x":1,"y":1}],
[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1}]
]]
var cube = new THREE.Mesh( geometry, material );
cube.rotation.x = Math.PI / 3
cube.rotation.y = 0
cube.rotation.z = Math.PI / 3
scene.add( cube );
camera.position.z = 5;
body { margin: 0; font-size: 0; }
canvas { width: 100%; height: 100% }
<script src="https://threejs.org/build/three.js"></script>
Notice how the texture shifts? This is how UV coordinates help you apply a 2d texture to any 3d surface. (Try to also play around with Texture.wrapS)
The problem
At the moment, inside your code, you are loading your objects from an external source, most likely an OBJ file (based on the fact that you are using an ObjectLoader), and since an MAT file was not present, there is no information present about the properties of the materials.
This means that the objects you are trying to render to screen, don't have any "texture coordinates", and we need to "provide" this information in some way.
While its possible to hand craft this data, this isn't always so saleable, so we have to fallback to another alternative, generating this data based on the position of the item.
Generating UV coordinates is a complex topic, as there are many different mappings for this, and I recommend you find and use one of the solutions at "THREE.js generate UV coordinate - StackOverflow" for this.

How to set vertical angle of spherebuffergeometry? Why Orbit controls don't work properly when mesh is rotated?

I am having issues trying to set the default view of a panoramic image inside spherebuffergeometry. I have a mesh that makes use of this geometry and the material is an equirectangle panorama image. During runtime, the default view of the image is somewhere towards the right. I want the initial angle to be at the bottom of the camera. I tried changing the Phi angle and theta angle parameters of spherebuffergeometry. While I am able to move the default horizontal angle by changing Phi angle, the panoramic image looks weird when I change the theta angle.
I took the first two snapshots when phi angle was 0 and 100 respectively and theta angle was 0. I took the last snapshot when phi angle was 100 and theta angle was 1.
Any help would be appreciated.
Thanks
sphereBufferGeometry = new THREE.SphereGeometry(100,100,100,0,Math.PI*2,0,Math.PI); sphereBufferGeometry.scale( 1, 1, -1 );
var material = new THREE.MeshBasicMaterial( {
map: new THREE.TextureLoader().load([image] )
});
mesh = new THREE.Mesh( sphereBufferGeometry, material );
I have tried this as well.
init();
var scene,renderer,camera;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
var light = new THREE.PointLight(0xffffff, 0.8);
var light2 = new THREE.DirectionalLight( 0xffddcc, 1 );
light.position.set( 1, 0.75, 0.5 );
scene.add( light2 );
var sphereBufferGeometry = new THREE.SphereGeometry(100, 100, 100, 0, Math.PI * 2, 0, Math.PI);
sphereBufferGeometry.scale(1, 1, -1);
var material = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('https://threejs.org/examples/textures/2294472375_24a3b8ef46_o.jpg')
});
var mesh = new THREE.Mesh(sphereBufferGeometry, material);
mesh.rotation.x=1.6;
scene.add(camera);
scene.add(light);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
var orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
camera.position.set(0, 0, 0.001);
orbitControls.update();
var container = document.getElementById('container');
var canvas = renderer.domElement;
canvas.setAttribute("class", "frame");
container.appendChild(canvas);
document.body.appendChild(container);
animate();
}
function animate()
{
requestAnimationFrame(animate);
renderer.render( scene, camera );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>
. As you can see, I've rotated the mesh so that the default view is looking at the floor.. if we click and drag the mouse upwards such that the window in the view is horizontal and then click and drag left or right, orbit controls don't seem to work properly in that it doesn't give a proper panoramic experience.. I would like to know how can this be fixed

Make a renderer's background transparent but not shapes three.js

I'm using three.js where I'm showing a static background image and having a renderer animate shapes on top of it. The reason for doing this is to freely rotate the shapes' renderer without affecting the image. The problem is that the background sits behind the renderer so my question is, is it possible to make the renderer's "background" transparent (allowing the shapes to still show) in order to fully see the background image?
Things I've tried:
Setting opacity on renderer's domElement
Setting transparency for rgba with renderer's setClearColor method
Here's a simplified fiddle and you'll see that the square (thus renderer) is covering the background.
var square = new THREE.Shape();
square.moveTo(0, 0);
square.lineTo(0, length);
square.lineTo(length, length);
square.lineTo(length, 0);
square.lineTo(0, 0);
var geometry = new THREE.ShapeGeometry(square);
var material = new THREE.MeshBasicMaterial({
color: 'rgb(59, 89, 152)',
opacity: 1,
transparent: true
});
var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 50;
mesh.position.y = 50;
scene.add(mesh);
// and the camera
scene.add(camera);
renderer.render( scene, camera );
You need to set the webGLRenderer alpha parameter.
var renderer = new THREE.WebGLRenderer( { alpha: true } );
You can leave the clear color at the default value.
renderer.setClearColor( 0x000000, 0 ); // the default
Updated fiddles: http://jsfiddle.net/7McS2/3/ or http://jsfiddle.net/7McS2/4/
three.jr r.63

Categories

Resources