How to project a texture to curved surface? - three.js

Screen capture
I tried to make a 3/4 cylinder surface, and I made it from extruting by a ellipe path,
but when I tried to load a texture to the surface, It does not as I exprected: uniformly painted to the surface, it's stretched
I know it's about texture projection, But I dont know how to set options.
class EllipseCurve3 extends THREE.Curve {
ellipse = null
constructor (ellipse) {
super()
this.ellipse = ellipse
}
getPoint(t, optionalTarget = new THREE.Vector3()) {
const point = this.ellipse.getPoint(t, optionalTarget)
return new THREE.Vector3(
point.x,
point.y,
0
)
}
}
// Scene
const scene = new THREE.Scene();
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.moveTo(0, 1);
shape.lineTo(50, 1);
shape.moveTo(50, 0);
shape.lineTo(0, 0);
// var curve = new THREE.CatmullRomCurve3([
// new THREE.Vector3(0, 0, 50),
// new THREE.Vector3(-50, 0, 0),
// new THREE.Vector3(0, 0, -50)
// ]);
const arc = new THREE.EllipseCurve(
0,
0, // ax, aY
100,
100, // xRadius, yRadius
0,
1.5 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
),
path = new EllipseCurve3(arc)
geometry = new THREE.ExtrudeGeometry(shape, {
bevelEnabled: false,
extrudePath: path,
steps: 50,
depth: 5,
amount: 20,
material: 0,
extrudeMaterial: 1
});
// Set up lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight.position.set(100, 200, 100); // x, y, z
scene.add(directionalLight);
// Camera
const width = 200;
const height = width * (window.innerHeight / window.innerWidth);
const camera = new THREE.OrthographicCamera(
width / -2, // left
width / 2, // right
height / 2, // top
height / -2, // bottom
0.1, // near
1000 // far
);
camera.position.set(400, 400, 400);
camera.lookAt(0, 0, 0);
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
renderer.render(scene, camera);
// Add it to HTML
document.body.appendChild(renderer.domElement);
var textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = true;
const picture = 'https://threejs.org/examples/textures/uv_grid_opengl.jpg'
textureLoader.load(picture, function(texture) {
// repeat pattern
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
// zoom in on pattern
texture.repeat.set(.01, .01);
// assign texture via MeshBasicMaterial
var material = new THREE.MeshBasicMaterial({
map: texture,
needsUpdate: true,
// transparent: true,
// premultipliedAlpha: true,
// side: THREE.DoubleSide,
// blending: THREE.AdditiveBlending
});
// var material = new THREE.MeshPhongMaterial({ color: 0x0048ff });
var mesh = new THREE.Mesh(geometry, material)
mesh.rotation.x = Math.PI / 2
// mesh.rotation.z = Math.PI / 2
scene.add(mesh)
scene.add(cube)
})
function render() {
renderer.render(scene, camera);
// Rotate out group
// svgGroup.rotation.y -= 0.005
controls.update();
requestAnimationFrame(render);
}
render();
Code here
https://codepen.io/mike-xu/pen/RwQeEXJ

"I know it's about texture projection" - It's about computing UV, based on vertices coordinates.
CylinderGeometry also may help to achieve the result you described. With less code, and more convenient and predictable way.
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls.js";
console.clear();
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 10, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
let controls = new OrbitControls(camera, renderer.domElement);
scene.add(new THREE.AxesHelper(10));
let g = new THREE.CylinderGeometry(5, 5, 5, 100, 20, true, 0, Math.PI * 1.5);
let m = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
map: new THREE.TextureLoader().load(
"https://threejs.org/examples/textures/uv_grid_opengl.jpg",
tex => {
tex.wrapS = tex.wrapT = THREE.MirroredRepeatWrapping;
tex.repeat.set(3, 1);
}
)
});
let c = new THREE.Mesh(g, m);
scene.add(c);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
</script>

Related

How to animate a threeJS object using GSAP?

I have been learning threeJS just recently and can't get passed a problem. I tried to animate a extruded triangle using the GSAP library. It is just a simple animation to have the triangle move to the right but it seems I did something wrong. Any help is much appreciated.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create Triangles
var material = new THREE.MeshPhongMaterial({
color: 0xf6c12a,
shininess: 70
});
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(2, 3);
shape.lineTo(4, 0);
shape.lineTo(0, 0);
var extrudeSettings = {
steps: 5,
depth: 1,
bevelEnabled: true,
bevelThickness: 0.3,
bevelSize: 0.5,
bevelOffset: 0,
bevelSegments: 1
};
var geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
// Sets the origin to the center of geometry for rotation
geometry.center();
var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = -5;
mesh.scale.set(1.5, 1.5, 1.5);
scene.add(mesh);
gsap.to(mesh, { duration: 2, x: 300 });
camera.position.z = 5;
// Background
var geometry = new THREE.PlaneGeometry(1000, 1000, 1);
var material = new THREE.MeshPhysicalMaterial({ color: 0x444444 });
var plane = new THREE.Mesh(geometry, material);
plane.position.z = -50;
scene.add(plane);
// Lighting
var ambientLight = new THREE.AmbientLight(0xffffff, 0.55);
scene.add(ambientLight);
var pointLight1 = new THREE.PointLight(0xf9eac8, 1, 100);
pointLight1.position.set(5, 10, 0);
pointLight1.castShadow = true;
pointLight1.shadow.camera.top = 20;
scene.add(pointLight1);
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
Here is the link to Codepen
Tried to put the gsap code into different position but maybe that's not the problem.
gsap.to(mesh.position, { duration: 2, x: 300 });
the value that you want to change is mesh.position.x not mesh.x
just add .position it will work

I need to use the data from first picture to draw cylinder,put two cylinders point B is not coincide(like second picture)

**I need to use the data from first picture to draw cylinder,put two cylinders point B is not coincide(like second picture) **
var geometry = new THREE.CylinderGeometry(10, 10,151.02648774304458, 20, 1, false);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(1,75.5,1);
scene.add(mesh);
var material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var geometry1 = new THREE.CylinderGeometry(10, 10,158.8741640418605, 20, 1, false);
var mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.position.set(-30,217,32.5);
mesh1.rotation.set(2,151,2);
scene.add(mesh1);
You have to add the red cylinder to a Group. Set the position in that way, that the bottom of the cylinder is at (0, 0, 0). Set the position of the group in that way, that it's origin is at the top of the black cylinder.
Finally you have to rotate the group:
let height = 151.02648774304458;
let height1 = 158.8741640418605;
var geometry = new THREE.CylinderGeometry(10, 10, height, 20, 1, false);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(1, 75.5, 1);
scene.add(mesh);
var material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var geometry1 = new THREE.CylinderGeometry(10, 10, height1, 20, 1, false);
var mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.position.set(0, height1/2, 0);
group = new THREE.Group();
group.position.set(mesh.position.x, mesh.position.y + height/2, mesh.position.z);
group.add(mesh1);
group.rotation.set(...);
scene.add(group);
(function onLoad() {
var container, camera, scene, renderer, orbitControls;
init();
animate();
function init() {
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({
canvas: my_canvas,
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
//container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 200, -400);
camera.lookAt( 0, 0, 0 );
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(camera);
window.onresize = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
orbitControls = new THREE.OrbitControls(camera, container);
createModel();
}
var group;
function createModel() {
var material = new THREE.MeshPhongMaterial({color:'#ff0000'});
var material1 = new THREE.MeshPhongMaterial({color:'#000000'});
let height = 151.02648774304458;
let height1 = 158.8741640418605;
var geometry = new THREE.CylinderGeometry(10, 10, height, 20, 1, false);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(1, 75.5, 1);
scene.add(mesh);
var material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var geometry1 = new THREE.CylinderGeometry(10, 10, height1, 20, 1, false);
var mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.position.set(0, height1/2, 0);
group = new THREE.Group();
group.position.set(mesh.position.x, mesh.position.y + height/2, mesh.position.z);
group.add(mesh1);
//group.rotation.set(2, 151, 2);
scene.add(group);
}
var rotate = 0.0;
function animate() {
group.rotation.set(0, 0, rotate);
rotate += 0.01;
requestAnimationFrame(animate);
orbitControls.update();
render();
}
function render() {
renderer.render(scene, camera);
}
})();
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/controls/OrbitControls.js"></script>
<div id="container"><canvas id="my_canvas"> </canvas></div>
To set a specific rotation by a specific vector, I recommend to set the rotation by a .setRotationFromQuaternion.
The Quaternion defines how to rotate from the upwards direction (0, 1, 0) to the target direction. The Target direction is the vector form the joint to the endpoint of the upper cylinder (-62-1, 283-151, 61-1):
For instance:
let upVector = new THREE.Vector3(0, 1, 0);
let targetVector = new THREE.Vector3(-62 - 1, 283 - height, 61 - 1);
let quaternion = new THREE.Quaternion().setFromUnitVectors(
upVector, targetVector.normalize());
group.setRotationFromQuaternion(quaternion)

Threejs How to Add in and Out curves in a custom shape?

I have a simple shape made in 3js with 8 points. The shape is without curves. I am not very good in absarc or curves in Threejs. I have tried to search online but I didn't get exact example.
My shape output:
While I need output like this:
Just an option, using .absarc():
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 0, 6);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var grid = new THREE.GridHelper(10, 10);
grid.rotation.x = -Math.PI * 0.5;
grid.position.set(0, 0, -0.01);
scene.add(grid);
var s = new THREE.Shape();
s.absarc(0, 0, 0.5, Math.PI * 0.5, -Math.PI * 0.5, true);
s.lineTo(0, -2);
s.absarc(-1, 0, Math.sqrt(2) * 2, -Math.PI * 0.25, Math.PI * 0.25, false);
s.lineTo(0, 2);
var sGeom = new THREE.ShapeBufferGeometry(s);
var sMat = new THREE.MeshBasicMaterial({
color: "teal"
});
var shape = new THREE.Mesh(sGeom, sMat);
scene.add(shape);
var lineGeom = new THREE.BufferGeometry().setFromPoints(s.getPoints());
var lineMat = new THREE.LineBasicMaterial({
color: "orange"
});
var outline = new THREE.LineLoop(lineGeom, lineMat);
scene.add(outline);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Moving an object using three.js

I am very much new to this three.js. I want to create two cubes. I am least interested in its animation. So, i want cube 1 to move towards cube 2 without any keyboard inputs. I am also providing image for better understanding.
Thanks in advance
On possibility to animate objects, is to use THREE.Tween
Create the 2 cubes:
var material1 = new THREE.MeshPhongMaterial({color:'#2020ff'});
var geometry1 = new THREE.BoxGeometry( 1, 1, 1 );
cube1 = new THREE.Mesh(geometry1, material1);
cube1.position.set(0.0, 0.0, 2.0);
var material2 = new THREE.MeshPhongMaterial({color:'#ff2020'});
var geometry2 = new THREE.BoxGeometry( 1, 1, 1 );
cube2 = new THREE.Mesh(geometry2, material2);
cube2.position.set(2.0, 0.0, 0.0);
Create a Tween, which rotates the cube and continuously restarts:
tweenStart = { value: 0 };
var finish = { value: Math.PI/2 };
cubeTween = new TWEEN.Tween(tweenStart);
cubeTween.to(finish, 3000)
cubeTween.onUpdate(function() {
cube1.position.set(0.0, 0.0, 0.0);
cube1.rotation.y = tweenStart.value;
cube1.translateZ( 2.0 );
});
cubeTween.onComplete( function() {
tweenStart.value = 0;
requestAnimationFrame(function() {
cubeTween.start();
});
});
cubeTween.start();
(function onLoad() {
var container, loader, camera, scene, renderer, controls, cube1, cube2, cubeTween, tweenStart;
init();
animate();
animateCube();
function init() {
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(0, 6, 0);
camera.lookAt( 0, 0, 0 );
loader = new THREE.TextureLoader();
loader.setCrossOrigin("");
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(camera);
window.onresize = resize;
var ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set(1,2,1.5);
scene.add( directionalLight );
controls = new THREE.OrbitControls(camera);
addGridHelper();
createModel();
}
function createModel() {
var material1 = new THREE.MeshPhongMaterial({color:'#2020ff'});
var geometry1 = new THREE.BoxGeometry( 1, 1, 1 );
cube1 = new THREE.Mesh(geometry1, material1);
cube1.position.set(0.0, 0.0, 2.0);
var material2 = new THREE.MeshPhongMaterial({color:'#ff2020'});
var geometry2 = new THREE.BoxGeometry( 1, 1, 1 );
cube2 = new THREE.Mesh(geometry2, material2);
cube2.position.set(2.0, 0.0, 0.0);
scene.add(cube1);
scene.add(cube2);
tweenStart = { value: 0 };
var finish = { value: Math.PI/2 };
cubeTween = new TWEEN.Tween(tweenStart);
cubeTween.to(finish, 3000)
cubeTween.onUpdate(function() {
cube1.position.set(0.0, 0.0, 0.0);
cube1.rotation.y = tweenStart.value;
cube1.translateZ( 2.0 );
});
cubeTween.onComplete( function() {
tweenStart.value = 0;
requestAnimationFrame(function() {
cubeTween.start();
});
});
cubeTween.start();
}
function addGridHelper() {
var helper = new THREE.GridHelper(100, 100);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
var axis = new THREE.AxesHelper(1000);
scene.add(axis);
}
function resize() {
var aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
render();
}
function render() {
renderer.render(scene, camera);
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.js"></script>
<div id="container"></div>
Alternatively you can slightly change the position of the cube by changing the position property of the cube in the animate function:
function animate() {
requestAnimationFrame(animate);
if ( cube1.position.z > 0.0 ) {
cube1.position.set(0.0, 0.0, cube1.position.z-0.01);
} else if ( cube1.position.x < 2.0 ) {
cube1.position.set(cube1.position.x+0.01, 0.0, 0.0);
} else {
cube1.position.set(0.0, 0.0, 2.0);
}
render();
}
(function onLoad() {
var container, loader, camera, scene, renderer, controls, cube1, cube2, cubeTween, tweenStart;
init();
animate();
animateCube();
function init() {
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(0, 6, 0);
camera.lookAt( 0, 0, 0 );
loader = new THREE.TextureLoader();
loader.setCrossOrigin("");
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(camera);
window.onresize = resize;
var ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set(1,2,1.5);
scene.add( directionalLight );
controls = new THREE.OrbitControls(camera);
addGridHelper();
createModel();
}
function createModel() {
var material1 = new THREE.MeshPhongMaterial({color:'#2020ff'});
var geometry1 = new THREE.BoxGeometry( 1, 1, 1 );
cube1 = new THREE.Mesh(geometry1, material1);
cube1.position.set(0.0, 0.0, 2.0);
var material2 = new THREE.MeshPhongMaterial({color:'#ff2020'});
var geometry2 = new THREE.BoxGeometry( 1, 1, 1 );
cube2 = new THREE.Mesh(geometry2, material2);
cube2.position.set(2.0, 0.0, 0.0);
scene.add(cube1);
scene.add(cube2);
}
function addGridHelper() {
var helper = new THREE.GridHelper(100, 100);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
var axis = new THREE.AxesHelper(1000);
scene.add(axis);
}
function resize() {
var aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame(animate);
if ( cube1.position.z > 0.0 ) {
cube1.position.set(0.0, 0.0, cube1.position.z-0.01);
} else if ( cube1.position.x < 2.0 ) {
cube1.position.set(cube1.position.x+0.01, 0.0, 0.0);
} else {
cube1.position.set(0.0, 0.0, 2.0);
}
render();
}
function render() {
renderer.render(scene, camera);
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>
There are a few different ways you could do this.
It will also be easier to answer this by assuming axes and both cube's starting position.
Let's say cube1 position is:
x: 0
y: 0
z: 0
Cube2 position is:
x: 10
y: 20
z: 0
In the render loop, move cube1 along the y-axis until it reaches the same value as cube2 (20), then move cube1 along the x-axis until it reaches cube2 x position. So cube1 would then be sitting on top of cube2. You would need the code to move cube1 to be present, but only run when certain conditions are met.
E.g, only move in the y-axis if cube1.position.y is less than or equal to cube2.position.y
Movement speed can be increased or decreased by changing the increment value. So an update on the y-axis could look like this:
cube1.position.y += 0.1;
This way could be harder to manage though, as you will need to constantly check the cube1 position in relation to cube2, and because the cube needs to move in two directions this could get messy in the long-run.
You can see an example of this update in the loop concept here - https://jsfiddle.net/f2Lommf5/
I would look at using an animation library such as Tween.js or Greensock which in my opinion would be much better suited. You can specify an axis to move along, the start and end position, animation duration and easing. When the animation on the y-axis is complete, you can start the animation on the x-axis.
Some useful links:
Tween.js - http://learningthreejs.com/blog/2011/08/17/tweenjs-for-smooth-animation/
Greensock - http://www.kadrmasconcepts.com/blog/2012/05/29/greensock-three-js/

Custom plane does not cast/ receive shadows in THREE js

I am trying to create a box (a wall) using six planes. I have created planes but shadows are not there.
This is how I create custom planes.
function createWall(vertices) {
var geometry = new THREE.Geometry(), i;
for (i = 0; i < vertices.length; i = i + 1) {
geometry.vertices.push(vertices[i]);
}
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.faces.push(new THREE.Face3(0, 2, 3));
geometry.computeVertexNormals();
geometry.computeFaceNormals();
var material = new THREE.MeshStandardMaterial({
emissive: 0x708090,
emissiveIntensity: 1,
side: THREE.DoubleSide,
color: 0xD3D3D3
});
var mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
Here is the complete code.
var camera, scene, renderer;
function addFloor() {
var material = new THREE.MeshStandardMaterial({
roughness: 0.8,
color: 0x696969,
metalness: 0.2,
bumpScale: 0.0005
});
var geometry = new THREE.PlaneBufferGeometry(2000, 2000);
var mesh = new THREE.Mesh(geometry, material);
mesh.receiveShadow = true;
mesh.rotation.x = -Math.PI / 2.0;
scene.add(mesh);
}
function createWall(vertices) {
var geometry = new THREE.Geometry(), i;
for (i = 0; i < vertices.length; i = i + 1) {
geometry.vertices.push(vertices[i]);
}
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.faces.push(new THREE.Face3(0, 2, 3));
geometry.computeVertexNormals();
geometry.computeFaceNormals();
var material = new THREE.MeshStandardMaterial({
emissive: 0x708090,
emissiveIntensity: 1,
side: THREE.DoubleSide,
color: 0xD3D3D3
});
var mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
function addBulb(location) {
var geometry = new THREE.SphereGeometry(2, 20, 20);
var light = new THREE.PointLight(0xffffff, 1, 100, 2);
var material = new THREE.MeshStandardMaterial({
emissive: 0xffffee,
emissiveIntensity: 1,
color: 0x000000
});
light.add(new THREE.Mesh(geometry, material));
light.position.set(location.x, location.y, location.z);
light.shadow.camera.near = 0.0001;
light.castShadow = true;
//light.shadow.darkness = 0.5;
//light.shadow.camera.vsible = true;
return light;
}
function addWalls() {
var wall1 = createWall([
new THREE.Vector3(0, 0, 0), //vertex0
new THREE.Vector3(200, 0, 0), //1
new THREE.Vector3(200, 100, 0), //2
new THREE.Vector3(0, 100, 0) //3
]);
var wall2 = createWall([
new THREE.Vector3(0, 0, 5), //vertex0
new THREE.Vector3(200, 0, 5), //1
new THREE.Vector3(200, 100, 5), //2
new THREE.Vector3(0, 100, 5) //3
]);
scene.add(wall1);
scene.add(wall2);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(50, 100, 300);
scene.add(camera);
}
function init() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.renderSingleSided = false;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
addCamera();
addFloor();
addWalls();
scene.add(addBulb({x: 100, y: 50, z: 25}));
var ambientLight = new THREE.AmbientLight(0x999999, 0.6);
scene.add(ambientLight);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
init();
animate();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
I may have done something wrong while creating custom plane. I do not understand what is wrong here.
UPDATED:
renderer.shadowMap.renderSingleSided = false;
It seems shadows are working but lighting has strange square effect.
Here I have tried BoxGeometry which has little depth as suggested in three-js-plane-doesnt-cast-shadow ,
As I noted this occurs only for small values for depth. (depth < 1)
var camera, scene, renderer;
function addFloor() {
var material = new THREE.MeshStandardMaterial({
roughness: 0.9,
color: 0xffffff,
metalness: 0.1,
bumpScale: 0.0005
});
var geometry = new THREE.PlaneBufferGeometry(2000, 2000);
var mesh = new THREE.Mesh(geometry, material);
mesh.receiveShadow = true;
mesh.rotation.x = -Math.PI / 2.0;
scene.add(mesh);
}
function createWall(location) {
var geometry = new THREE.BoxGeometry(200, 100, 0.1);
geometry.translate((location.x1 + location.x2) / 2, 150 / 2, location.z);
var material = new THREE.MeshStandardMaterial({
emissive: 0x708090,
emissiveIntensity: 1,
side: THREE.DoubleSide,
color: 0xD3D3D3,
roughness: 0.9,
metalness: 0.1
});
var mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
function addBulb(options) {
var geometry = new THREE.SphereGeometry(2, 20, 20);
var light = new THREE.PointLight(options.color, 1, 500, 2);
var material = new THREE.MeshStandardMaterial({
emissive: options.color,
emissiveIntensity: 1,
color: options.color
});
light.add(new THREE.Mesh(geometry, material));
light.position.set(options.x, options.y, options.z);
light.shadow.camera.near = 0.0001;
light.castShadow = true;
light.shadow.darkness = 0.5;
light.shadow.camera.vsible = true;
return light;
}
function addWalls() {
var wall1 = createWall({ x1: 0, x2: 200, z: 0});
var wall2 = createWall({ x1: 0, x2: 200, z: 5});
scene.add(wall1);
//scene.add(wall2);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(100, 100, 300);
scene.add(camera);
}
function init() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.renderSingleSided = false;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
addCamera();
addFloor();
addWalls();
scene.add(addBulb({x: 0, y: 100, z: 25, color: 0xff0000 }));
scene.add(addBulb({x: 100, y: 100, z: 25, color: 0x00ff00 }));
scene.add(addBulb({x: 200, y: 100, z: 25, color: 0x0000ff }));
var ambientLight = new THREE.AmbientLight(0x999999, 0.6);
scene.add(ambientLight);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
init();
animate();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
You're facing Z fighting. Since the thickness of the wall is so small, the 2 faces are nearly touching each other, so they cast shadows on each other.
You can either remove the casting/receiving of shadows on the wall or, better, increase the z value in the geometry, like so:
var geometry = new THREE.BoxGeometry(200, 100, 1);
Also, since you changed to a box from a plane, the line side: THREE.DoubleSide, in the wall material is no longer needed. In fact, it's part of the self-shadowing problem. Changing those two lines should solve your problem.
Here's a working Fiddle.

Resources