Following the manual, I made a three.js example, but the light effect doesn't appear, why the light source doesn't appear without any error about the light source?
import * as THREE from "/assets/threejs/build/three.module.js"
class App {
// 생성 초기화
constructor(){
const divContainer = document.querySelector("#webgl-container");
this._divContainer = divContainer;
const renderer = new THREE.WebGLRenderer({antialias: true})
renderer.setPixelRatio(window.devicePixelRatio);
divContainer.appendChild(renderer.domElement);
this._renderer = renderer;
const scene = new THREE.Scene();
this._scene = scene;
this._setupCamera();
this._setupLight();
this._setupModel();
window.onresize = this.resize.bind(this);
this.resize();
requestAnimationFrame(this.render.bind(this));
}
// 카메라
_setupCamera() {
const width = this._divContainer.clientWidth;
const height = this._divContainer.clientHeight;
const camera = new THREE.PerspectiveCamera(
75,
width / height,
0.1,
100,
)
camera.position.z = 2
this._camera = camera;
}
// 광원
_setupLight(){
const color = 0xffffff;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
this._scene.add(light);
}
// 모델
_setupModel(){
const geometry = new THREE.BoxGeometry(1,1,1);
const material = new THREE.MeshBasicMaterial( { color: 0x44a88 } );
const cube = new THREE.Mesh( geometry, material );
this._scene.add(cube);
this._cube = cube;
}
// 창크기 번경
resize(){
const width = this._divContainer.clientWidth;
const height = this._divContainer.clientHeight;
this._camera.aspect = width / height;
this._camera.updateProjectionMatrix();
this._renderer.setSize(width, height);
}
// 랜더링
render(time){
this._renderer.render(this._scene, this._camera);
this.update(time);
requestAnimationFrame(this.render.bind(this));
}
// 업데이트
update(time){
time *= 0.001;
this._cube.rotation.x = time;
this._cube.rotation.y = time;
}
}
window.onload = function(){
new App();
}
The screenshot below is the result of my example code.
The screenshot below is the result of the manual.
I'm sorry for posting multiple questions with a low difficulty problem, but, I'm learning three.js for the first time. In vscode, there is no three.js code hint library, and it's hard. How can I express the light effect normally in my example code? Will there be? thanks for reading the problem
Do not use MeshBasicMaterial, but MeshStandardMaterial. MeshBasicMaterial for drawing geometries in a flat or wireframe way. MeshStandardMaterial is a physically based material that makes the surface interact with the light.
Related
i am new to three js, i'm trying to create soft lighting for a gltf model using AmbientLight and DirectionalLight, but the color contrast comes out too sharp
what i'm trying to achieve
What i got
my code
const main = document.querySelector('#main');
let WIDTH = main.offsetWidth;
let HEIGHT = main.offsetHeight;
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(15, WIDTH / HEIGHT, 0.1, 1000);
camera.position.z = 5;
let renderer = new THREE.WebGLRenderer();
renderer.setClearColor('rgb(99, 115, 107)');
renderer.setSize(WIDTH, HEIGHT);
renderer.domElement.setAttribute("id", "Church3DObj");
main.insertBefore(renderer.domElement, main.firstChild);
let controls = new THREE.OrbitControls(camera, renderer.domElement);
const ambientLight = new THREE.AmbientLight('white', 1);
const mainLight = new THREE.DirectionalLight('white', .8);
mainLight.position.set(10, 10, 10);
scene.add(ambientLight)
scene.add(mainLight)
let loader = new THREE.GLTFLoader();
let obj = null;
loader.load('https://raw.githubusercontent.com/aaadraniki/projects/web-pages/assets/models/fender_br/scene.gltf', function(gltf) {
obj = gltf.scene;
scene.add(obj);
});
renderer.setAnimationLoop(_ => {
renderer.render(scene, camera);
})
You'll usually get the best lighting in three.js by using some kind of environment map. THREE.RoomEnvironment is one of the easier ways to do that, or you can load external .hdr or .exr files. Enabling some tone mapping (e.g. ACES Filmic) may also help, see renderer.toneMapping.
I'm finally getting around to building a small game idea I've had. While it might seem odd, the aesthetic of the game requires a crisp, pixelated look.
So far, I've tried to use the built-in setPixelRatio() and setSize() on the renderer. Unfortunately, the scaled-up image appears blurry.
In my index.js
import * as THREE from 'three'
import '#/misc/console'
import Game from '#/game'
const g = new Game()
let geom = new THREE.BoxGeometry(1, 1, 1)
let mat = new THREE.MeshBasicMaterial({ color: 0xffffff })
let cube = new THREE.Mesh(geom, mat)
g.scene.add(cube)
g.camera.position.z = 5
const animate = () => {
requestAnimationFrame(animate)
cube.rotation.x += 0.01
cube.rotation.y += 0.02
g.renderer.render(g.scene, g.camera)
}
animate()
In my game/index.js
import { Scene, PerspectiveCamera, WebGLRenderer } from 'three'
export default class Game {
static VIEW_RATIO_X = 16
static VIEW_RATIO_Y = 9
scene
camera
renderer
constructor () {
// Create a new base scene
this.scene = new Scene()
// Create the frame and set the size accordingly
this.camera = new PerspectiveCamera(90, Game.VIEW_RATIO_X / Game.VIEW_RATIO_Y, 0.1, 1000)
// Create a new renderer and calculate the proper size to make it
this.renderer = new WebGLRenderer({ antialias: false })
this.renderer.setPixelRatio(0.5)
this.renderer.setSize(window.innerHeight / Game.VIEW_RATIO_Y * Game.VIEW_RATIO_X, window.innerHeight)
document.body.appendChild(this.renderer.domElement)
}
}
This basic attempt to halve the resolution just makes it blurry. Are there three.js, WebGL, or canvas settings I can use to enforce clear lines on the pixels?
I want to create new mesh before each render, here's my code(a minimal demo):
let canvas,
engine,
camera,
scene;
function initEngine(){
canvas = document.getElementById("renderCanvas");
engine = new BABYLON.Engine(canvas, true);
}
function createScene(){
initEngine();
let scene = new BABYLON.Scene(engine);
camera = new BABYLON.ArcRotateCamera("camera", Math.PI / 2, Math.PI / 2, 4, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
let light1 = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0,1,0), scene);
let ground = BABYLON.MeshBuilder.CreateGround(
"ground",
{
width:30,
height:30
},
scene
);
ground.position.y -= 0.5;
let sphere1 = BABYLON.MeshBuilder.CreateSphere(
"sphere1",
{
},
scene
);
let sphere2 = sphere1.clone("sphere2");
let sphere3 = sphere1.clone("sphere3");
sphere1.position.z = -18;
sphere2.position.z = -19;
sphere3.position.z = -20;
let snake = [
sphere1,
sphere2,
sphere3
];
(function(){
let counter = 4;
scene.registerBeforeRender(function(){
let newOne = BABYLON.MeshBuilder.CreateSphere(
"sphere" + counter,
{
},
scene
);
let head = snake[0];
newOne.position = head.position;
newOne.position.x += 0.02;
snake.unshift(newOne);
++counter;
});
})();
window.addEventListener("resize", function(){
engine.resize();
});
return scene;
}
scene = createScene();
engine.runRenderLoop(function(){
// box.position.z += 0.01;
scene.render();
});
My expecting behavior is to create a series of spheres, each position.x is slightly higher than the previous one. However, there are only three meshes in the scene after rendering, like this:
result
I want to know what is wrong with my code, and how to implement it properly?
By the way, what is the difference between scene.removeMesh(mesh) and mesh.dispose()?
It's because of this statement.
newOne.position = head.position;
It just copy a reference. so now the new sphere and snake[0] share a same position instance, so all the newly created spheres share a same position instance(by holding a position reference), and located in the same position.
Today I've been experimenting with building my first ever skybox in three.js. I've read a lot of tutorials and the code I've ended up with is based on this one: http://learningthreejs.com/blog/2011/08/15/lets-do-a-sky/
I did make a few changes in order to allow for the images to load first, and to make it compatible with the version of three.js which I am using.
I've overcome a lot of small problems to get to the point I am currently at, but cannot find any answer to my current issue despite having searched quite hard. My problem is that despite using purpose-built skybox textures downloaded from the internet, it is glaringly obvious that my skybox is a cube with corners and edges. The textures appear heavily distorted and are not at all convincing.
Here is a screenshot of how my skybox looks:
And here is a link to the site from which I downloaded the images:
http://www.humus.name/index.php?page=Cubemap&item=Yokohama3
As you can see, in their preview it looks much better.
I've tried this with a few different downloaded textures and every time it is very obvious that you are looking at the inside of a cube.
Here's my code (I'm including all my code, not just the section which creates the skybox):
var scene;
var camera;
var renderer;
function createRenderer () {
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000, 1.0)
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMapEnabled = true;
//renderer.shadowCameraNear = 0.5;
//renderer.shadowCameraFar = 500;
}
function createCamera () {
camera = new THREE.PerspectiveCamera(
45,
window.innerWidth/window.innerHeight,
0.1, 1000
);
camera.position.x = 50;
camera.position.y = 30;
camera.position.z = 40;
camera.lookAt(scene.position);
}
function createPlane () {
var material = new THREE.MeshLambertMaterial({
color: 0xcccccc,
})
var geometry = new THREE.PlaneGeometry(40, 40)
var plane = new THREE.Mesh(geometry, material)
plane.receiveShadow = true;
plane.rotation.x = -Math.PI/2
plane.position.y = -6;
scene.add(plane)
}
function createLight () {
var spotLight = new THREE.DirectionalLight(0xffffff);
spotLight.position.set( 0, 50, 20 );
spotLight.shadowCameraVisible = true;
spotLight.shadowDarkness = 0.5
spotLight.shadowCameraNear = 0;
spotLight.shadowCameraFar = 100;
spotLight.shadowCameraLeft = -50;
spotLight.shadowCameraRight = 50;
spotLight.shadowCameraTop = 50;
spotLight.shadowCameraBottom = -50;
spotLight.castShadow = true;
scene.add(spotLight);
}
function createSkyboxAndSphere () {
var urlPrefix = "Yokohama3/";
var urls = [ urlPrefix + "posx.jpg", urlPrefix + "negx.jpg",
urlPrefix + "posy.jpg", urlPrefix + "negy.jpg",
urlPrefix + "posz.jpg", urlPrefix + "negz.jpg" ];
var textureCube = THREE.ImageUtils.loadTextureCube( urls , undefined, function () {;
var shader = THREE.ShaderLib["cube"];
var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
shader.uniforms['tCube'].value = textureCube; // textureCube has been init before
var material = new THREE.ShaderMaterial({
fragmentShader : shader.fragmentShader,
vertexShader : shader.vertexShader,
uniforms : shader.uniforms,
depthWrite : false,
side: THREE.BackSide,
});
var geometry = new THREE.BoxGeometry(100, 100, 100)
var skybox = new THREE.Mesh(geometry, material)
scene.add(skybox)
var material = new THREE.MeshPhongMaterial({
color: "red",
envMap: textureCube,
reflectivity: 0.3,
})
var geometry = new THREE.SphereGeometry(6, 30, 15)
var sphere = new THREE.Mesh(geometry, material)
sphere.castShadow = true;
sphere.receiveShadow = true;
scene.add(sphere)
});
}
function init () {
scene = new THREE.Scene();
createRenderer();
createCamera();
createLight();
createPlane ();
createSkyboxAndSphere ();
document.getElementById("container").appendChild(renderer.domElement)
render ()
}
function render () {
renderer.render(scene, camera)
requestAnimationFrame(render);
}
window.onload = function () {
init ();
}
I suspect I am fundamentally misunderstanding something about how cubemapping and skyboxes work - I am very new to this in particular and javascript in general and am aware of huge gaps in my knowledge.
My apologies if the answer to this is obvious and/or the question has been asked before, and a pre-emptive thanks for your help!
Your camera needs to be in the center of the skybox -- or at least near the center.
So either move your camera very close to the box center, or update the box position every frame to match the camera position.
Or make the skybox much bigger relative to the camera offset from the origin.
Or place the skybox in a separate scene and have two cameras and two render passes, as in this example.
three.js r.74
i'm new in three.js and have a problem by scaling a object. I already looked at the documentation (https://github.com/mrdoob/three.js/wiki/Updates), but i don't really understand it at all.
My Problem: By changing a HTML-select-element the CubeGeometry should be scaled in the x-direction. That is already working BUT the "old" Cube do not disappear. So i have 2 Cubes. But i want only one Cube with the current size. I hope you can understand my problem ;-)
So here is my Code from the View:
$(document).on('change',".configurator > form select",function(event){
// update 3D-object - is that a nice way???
$.getScript("/javascripts/3D-animation.js.erb", function(){
// here comes the update
OBJECT3D.updateObject();
});
})
And here is my 3D-animation.js.erb:
var drawing_area;
var renderer;
var camera;
var obj;
var scene;
var OBJECT3D = {};
$(function() {
// get drawing_area
drawing_area = document.getElementById("canvas_wrapper");
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(drawing_area.clientWidth, drawing_area.clientHeight);
renderer.setClearColor( 0xffffff, 1);
// add renderer to drawing_area
drawing_area.appendChild(renderer.domElement);
// initialize camera
camera = new THREE.PerspectiveCamera(45, drawing_area.clientWidth/drawing_area.clientHeight, 1, 100000);
camera.position.z = 1000;
camera.position.y = 100;
camera.position.x = 300;//-0.78;
// create texture
var texture = THREE.ImageUtils.loadTexture( "/images/materials/texture_1.jpg" );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 1, 1 );
// create object
var obj_form = new THREE.CubeGeometry(250,250,250);
var obj_material = new THREE.MeshLambertMaterial( { map: texture,ambient: 0x999999 } );
OBJECT3D.obj = new THREE.Mesh(obj_form, obj_material);
// so what do i need here?
OBJECT3D.obj.geometry.dynamic = true;
// OBJECT3D.obj.geometry.__dirtyVertices = true;
// OBJECT3D.obj.geometry.__dirtyNormals = true;
// OBJECT3D.obj.geometry.verticesNeedUpdate = true;
// create scene
scene = new THREE.Scene();
scene.add(camera);
scene.add(OBJECT3D.obj);
// create lights
pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 400;
pointLight.position.y = 200;
pointLight.position.z = 1300;
scene.add(pointLight);
ambientLight = new THREE.AmbientLight( 0xffffff);
scene.add( ambientLight );
requestAnimationFrame(render);
function render(){
requestAnimationFrame(render);
OBJECT3D.obj.rotation.y += 0.005;
OBJECT3D.obj.rotation.z += 0.005;
renderer.render(scene, camera);
};
// update object
OBJECT3D.updateObject = function () {
console.log("in update");
OBJECT3D.obj.scale.x = 2.5; // SCALE
OBJECT3D.obj.geometry.needsUpdate = true;
//render();
}
});
Sorry, if the Code is not the best one, but i'm really new in this stuff! :) hope you can help me!
Thanks!
I think it is because you are using $.getScript method. This will load your script each time again and create a new instance of OBJECT3D.
I propose that you make sure that your code in 3D-animation.js.erb will be included and called only once upon the load of a page (include it just like any other regular javascript file) and then call your update directly like this:
$(document).on('change',".configurator > form select", function(event) {
OBJECT3D.updateObject();
});
also, I believe you can drop following lines of code:
OBJECT3D.obj.geometry.needsUpdate = true;
and
OBJECT3D.obj.geometry.dynamic = true;