I basically followed this examples to set up anti-aliasing with the help of the FXAAShader like this:
this.composer = new EffectComposer(this.renderer);
const renderPass = new RenderPass(this.scene, this.camera);
this.fxaaPass = new ShaderPass(FXAAShader);
const pixelRatio = this.renderer.getPixelRatio();
const uniforms = this.fxaaPass.material.uniforms;
uniforms['resolution'].value.x = 1 / (this.canvas.offsetWidth * pixelRatio);
uniforms['resolution'].value.y = 1 / (this.canvas.offsetHeight * pixelRatio);
this.composer.addPass(renderPass);
this.composer.addPass(this.fxaaPass);
and this is how I render the wireframe
const mat = new THREE.LineBasicMaterial({
color: 0x000000,
linewidth: 2,
});
const geo = new THREE.WireframeGeometry(this.geometry);
this.wireframeGroup = new THREE.Group();
const wireframe = new THREE.LineSegments(geo, mat);
this.wireframeGroup.add(wireframe);
const solid = new THREE.MeshBasicMaterial({color: 0xffffff});
const base = new THREE.Mesh(this.geometry, solid);
this.wireframeGroup.add(base);
this.scene.add(this.wireframeGroup);
But the result looks kind of "pixly"
However, when use the normal rendere (not the composer), anti-aliasing seems to work fine:
What am I doing wrong?
Here is the source code
Related
all.
I'm working with three.js 127, following the documentation to add a SpotLight. However, shadows won't get shown on the scene. Below, you can find my scene. I already set the plane to receive shadow as well as other elements enabled to cast shadows yet no shadows are rendered on the scene.
const canvas = document.getElementById('webgl-output')
const init = () => {
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000,
)
const renderer = new THREE.WebGLRenderer({
canvas,
})
renderer.setClearColor(new THREE.Color(0x000000))
renderer.setSize(window.innerWidth, window.innerHeight)
const planeGeometry = new THREE.PlaneGeometry(60, 20)
const planeMaterial = new THREE.MeshLambertMaterial({
color: 0xaaaaaa,
})
const plane = new THREE.Mesh(planeGeometry, planeMaterial)
plane.receiveShadow = true
plane.rotation.x = -0.5 * Math.PI
plane.position.x = 15
plane.position.y = 0
plane.position.z = 0
scene.add(plane)
// Creating a cube
const cubeGeometry = new THREE.BoxGeometry(4, 4, 4)
const cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000,
})
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
cube.castShadow = true
cube.position.x = -4
cube.position.y = 3
cube.position.z = 0
scene.add(cube)
// Creating a sphere
const sphereGeometry = new THREE.SphereGeometry(4, 20, 20)
const sphereMaterial = new THREE.MeshLambertMaterial({
color: 0x7777ff,
})
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
sphere.castShadow = true
sphere.position.x = 20
sphere.position.y = 4
sphere.position.z = 2
scene.add(sphere)
// Position and point the camera to the center of the scene
camera.position.x = -30
camera.position.y = 12
camera.position.z = 30
camera.lookAt(scene.position)
// Add spotlight for the shadows
const spotLight = new THREE.SpotLight(0xffffff)
spotLight.position.set(8, 40, 80)
spotLight.castShadow = true
spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024
spotLight.shadow.camera.near = 8
spotLight.shadow.camera.far = 80
spotLight.shadow.camera.fov = 16
scene.add(spotLight)
renderer.render(scene, camera)
}
init()
Am I missing something to get the shadow rendered?
I noticed a few things from your code that may have caused this issue:
The biggest thing is that you didn't enable the shadowMap on your renderer, you can do this like so :
renderer.shadowMap.enabled = true;
This part doesn't seem to be appearing on the documentation of SpotLight which you linked, more information about this can be found on the documentation of SpotLightShadow.
After I tried this on a JS sandbox, I still didn't see the shadow, but it seems like the spotLight was simply too far away, it was set to 80 at it's 'z' position. I made the value slightly smaller (40) and it seemed to do the trick. What helped me figure it out is by using a helper and OrbitControls which makes it a little easier to debug:
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three#0.127/examples/jsm/controls/OrbitControls.js';
...
spotLight.position.set(8, 40, 40); // Third parameter (z) with value of 80 was too far away
controls = new OrbitControls(camera, renderer.domElement);
helper = new THREE.PointLightHelper(pointLight);
scene.add(helper);
Here is a JSFiddle of the fixed version: https://jsfiddle.net/tombugolya/hx61L5vp/17/
Maybe a newbie question but i'd like to use a random variable several times in my code, but of course it reminds the same for all parameters using it. How could I fix it ?
Example :
var randomColor = [yellow, blue, green][Math.floor(Math.random() * 3)]
const cube1 = new THREE.Mesh(geometry, randomColor);
scene.add(cube1);
const cube2 = new THREE.Mesh(geometry, randomColor);
scene.add(cube2);
Those two cubes will have to same material (named randomColor) even if it is random.
Thank you in advance !
Instead of using the same random color every time, you generate a new one by calling getRandomColor() as soon as you need it.
const colors = ['yellow', 'blue', 'green']
function getRandomColor() {
const index = Math.floor(Math.random() * colors.length)
return colors[index]
}
const cube1 = new THREE.Mesh(geometry, getRandomColor());
scene.add(cube1);
const cube2 = new THREE.Mesh(geometry, getRandomColor());
scene.add(cube2);
Here is the solution, the previous answers are not working with three.js :/ :
let colors = ['0xff0000','0xf7c252','0xa0bbd0']
const colour = new THREE.Color();
colour.setHex(colors[Math.floor(Math.random() * colors.length)]);
fond = new THREE.MeshToonMaterial({ color: colour });
const cube1 = new THREE.Mesh(geometry, fond);
scene.add(cube1);
const cube2 = new THREE.Mesh(geometry, fond);
scene.add(cube2);
I'm trying to keep a texture centered when it's shown in a different sized box.
I've seen this answer
Three.js: Make image texture fit object without distorting or repeating
But it's not quite doing it for me.
this.texture = new THREE.Texture(this.image)
const vec = new THREE.Vector3()
new THREE.Box3().setFromObject( this.rounded ).getSize(vec)
const imageAspect = this.image.width/this.image.height
const boxAspect = vec.x/vec.y
this.texture.wrapT = THREE.RepeatWrapping;
this.texture.offset.y = 0.5 * ( 1 - boxAspect/imageAspect )
//texture.wrapT = THREE.RepeatWrapping; texture.repeat.x = geometryAspectRatio / imageAspectRatio; texture.offset.x = 0.5 * ( 1 - texture.repeat.x )
this.texture.needsUpdate = true
this.rounded.material = new THREE.MeshPhongMaterial( { map: this.texture, side: THREE.DoubleSide } )
In this aspect the values are
Image: {width:399 height:275}
Texture: {width:1, height: 0.75}
In this aspect the values are
Image: {width:399 height:275}
Texture: {width:2, height: 1}
How do I fix it so the graphic is always central, maintains the aspect and is not distorted?
I hope I got you correctly, here is an option of how you can center it:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var planeGeom = new THREE.PlaneBufferGeometry(16, 9);
var planeMat = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/758px-Canestra_di_frutta_(Caravaggio).jpg", tex => {
//console.log(tex);
//console.log(tex.image.width, tex.image.height);
let imgRatio = tex.image.width / tex.image.height;
let planeRatio = planeGeom.parameters.width / planeGeom.parameters.height;
//console.log(imgRatio, planeRatio);
tex.wrapS = THREE.RepeatWrapping; // THREE.ClampToEdgeWrapping;
tex.repeat.x = planeRatio / imgRatio;
tex.offset.x = -0.5 * ((planeRatio / imgRatio) - 1);
})
});
var plane = new THREE.Mesh(planeGeom, planeMat);
scene.add(plane);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
})
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
Addition: how to re-compute UVs for ShapeBufferGeometry
var box = new THREE.Box3().setFromObject(mesh); // mesh with ShapeBufferGeometry
var size = new THREE.Vector3();
box.getSize(size);
var vec3 = new THREE.Vector3(); // temp vector
var attPos = mesh.geometry.attributes.position;
var attUv = mesh.geometry.attributes.uv;
for (let i = 0; i < attPos.count; i++){
vec3.fromBufferAttribute(attPos, i);
attUv.setXY(i,
(vec3.x - box.min.x) / size.x,
(vec3.y - box.min.y) / size.y
);
}
attUv.needsUpdate = true; // just in case
I'm trying to read depth values from framebuffer/WebGLRenderTarget into an array. I can find information to render depth to depth Texture, but could not read the depth to a buffer. With readpixels i could only get rbga values. Ideally i am trying to get worldposition from depth and ray.
I have tried reading pixels, but dont know how to read depth in threejs.
Off the top of my head it's not possible to read depth values directly.
You can set a DepthTexture to your WebGLRenderTarget's depthTexture property. You can then render all or part of that depth texture to another render target and read the result.
'use strict';
/* global THREE */
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.01;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 0.7;
const scene = new THREE.Scene();
const geometry = new THREE.BoxBufferGeometry();
const material = new THREE.MeshBasicMaterial({color: 'red'});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.x = 0.25;
cube.rotation.y = Math.PI * 0.25;
const depthTexture = new THREE.DepthTexture(canvas.width, canvas.height);
const rt = new THREE.WebGLRenderTarget(canvas.width, canvas.height, {
depthTexture,
});
renderer.setRenderTarget(rt);
renderer.render(scene, camera);
const planeScene = new THREE.Scene();
const planeGeo = new THREE.PlaneBufferGeometry(2, 2);
const planeMat = new THREE.MeshBasicMaterial({map: depthTexture});
const plane = new THREE.Mesh(planeGeo, planeMat);
planeScene.add(plane);
const ortho = new THREE.OrthographicCamera(-1, 1, 1, -1, -1, 1)
const planeRT = new THREE.WebGLRenderTarget(canvas.width, canvas.height, {type: THREE.FloatType});
renderer.setRenderTarget(planeRT);
renderer.render(planeScene, ortho);
const depths = new Float32Array(canvas.width * canvas.height * 4);
renderer.readRenderTargetPixels(planeRT, 0, 0, canvas.width, canvas.height, depths);
console.log(depths);
<canvas id="c" width="4" height="4"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
I'm trying to apply an environment map to a sphere, and followed both of the following tutorials to set up a basic scene with correct (as far as I can tell) lighting and geometry:
http://www.aerotwist.com/tutorials/getting-started-with-three-js/
http://www.aerotwist.com/tutorials/create-your-own-environment-maps/demo/
For some reason, however, the sphere renders as a simple flat black disk when I add the environment map. Without the envMap setting it renders the colored sphere correctly.
The full code is below, which is called from the .html file with Sphere.init() after the DOM loads:
Sphere = {
init: function(){
this.WIDTH = 800;
this.HEIGHT = 600;
this.VIEW_ANGLE = 45;
this.ASPECT = this.WIDTH / this.HEIGHT;
this.NEAR = 0.1;
this.FAR = 10000;
var container = $('#sphere');
this.renderer = new THREE.WebGLRenderer();
this.camera = new THREE.PerspectiveCamera(
this.VIEW_ANGLE,
this.ASPECT,
this.NEAR,
this.FAR
);
this.scene = new THREE.Scene();
this.scene.add(this.camera);
this.camera.position.z = 300;
this.renderer.setSize(this.WIDTH, this.HEIGHT);
container.append(this.renderer.domElement);
var urls = [
'pos-x.png',
'pos-x.png',
'pos-x.png',
'pos-x.png',
'pos-x.png',
'pos-x.png'
],
cubemap = THREE.ImageUtils.loadTextureCube(urls);
cubemap.format = THREE.RGBFormat;
var reflectionMaterial = new THREE.MeshPhongMaterial({
color: 0x0000ff,
envMap: cubemap
});
var radius = 50, segments = 16, rings = 16;
var sphereGeometry = new THREE.SphereGeometry(radius, segments, rings);
var sphere = new THREE.Mesh(sphereGeometry, reflectionMaterial);
this.scene.add(sphere);
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
this.scene.add(pointLight);
this.renderer.render(this.scene, this.camera);
}
};
I believe the only issue is that your declaring both: urls and cubemap at the same time. Therefore cubemap has no idea what urls is yet. Seperate the var declarations, and I bet you get it working.
var urls = [
'pos-x.png',
'pos-x.png',
'pos-x.png',
'pos-x.png',
'pos-x.png',
'pos-x.png'
];
var cubemap = THREE.ImageUtils.loadTextureCube(urls);