I am just getting into babylon.js, but I can't seem to figure out, how would you change color of a material ?
My code currently is:
/* eslint-disable */
import * as BABYLON from 'babylonjs';
// Get the canvas element from our HTML above
const canvas = document.getElementById("root");
// Load the BABYLON 3D engine
const engine = new BABYLON.Engine(canvas, true);
let fn;
let mainColor = new BABYLON.Color3(1.0, 0.2, 0.7);
setTimeout(() => {
mainColor = new BABYLON.Color3(0.3, 0.2, 0.2);
fn();
}, 2000);
// This begins the creation of a function that we will 'call' just after it's built
function createScene () {
// Now create a basic Babylon Scene object
const scene = new BABYLON.Scene(engine);
// Change the scene background color to green.
scene.clearColor = new BABYLON.Color4(0.5, 0.8, 0.6, 0.8);
// This creates and positions a free camera
const camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 1, 0.8, 30, new BABYLON.Vector3(0, 0, 0), scene);
// This targets the camera to scene origin
camera.setTarget(BABYLON.Vector3.Zero());
// This attaches the camera to the canvas
camera.attachControl(canvas, false);
// This creates a light, aiming 0,1,0 - to the sky.
const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(1, 1, 1), scene);
// Dim the light a small amount
light.intensity = .5;
// Let's try our built-in 'sphere' shape. Params: name, subdivisions, size, scene
const sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
const materialSphere1 = new BABYLON.StandardMaterial("texture1", scene);
materialSphere1.alpha = 1;
materialSphere1.diffuseColor = mainColor;
sphere.material = materialSphere1;
sphere.position.y = 1;
// Move the sphere upward 1/2 its height
// Let's try our built-in 'ground' shape. Params: name, width, depth, subdivisions, scene
const ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
fn = () => {
materialSphere1.diffuseColor = mainColor;
}
// Leave this function
return scene;
}; // End of createScene function
const scene = createScene();
engine.runRenderLoop(function () {
scene.render();
});
window.addEventListener("resize", function () {
engine.resize();
});
It looks aweful to me. As you can see i am defining fn inside the createScene which then allows me to modify it that way, however I believe that there should be a better way of doing it. I tried creating a function outside the createScene(), that would fetch the color, and then use that for materialSphere1.diffuseColor, however that did not work. So my question is: Is there any other (nicer) way to change a color of a material in babylon.js
Why not declaring your material outside of the callback?
You can also use scene.materials to browse materials
Related
I just started to learn more about Babylon.js (I don't know if this is a good choice between p5.js and three.js; throw some suggestions for me).
I came along with this question "which function is used more often between BABYLON.Animation and scene.registerBeforeRender(). I guess I am more used to use render() method, but I guess Animation function is good when I change the frameRates.
Which is better? which is used more often ?
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const createScene = () => {
const scene = new BABYLON.Scene(engine);
/**** Set camera and light *****/
const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 10, new BABYLON.Vector3(0, 0, 0));
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
const box = BABYLON.MeshBuilder.CreateBox("box", {});
box.position.y = 0.5;
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width:10, height:10});
// Animations
var alpha = 0;
scene.registerBeforeRender(function () {
box.rotation.y += 0.05;
});
const frameRate = 60;
const xSlide = new BABYLON.Animation("xSlide", "position.x", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
const keyFrames = [];
keyFrames.push({
frame: 0,
value: 2
});
keyFrames.push({
frame: frameRate,
value: -2
});
keyFrames.push({
frame: 2 * frameRate,
value: 2
});
xSlide.setKeys(keyFrames);
box.animations.push(xSlide);
scene.beginAnimation(box, 0, 2 * frameRate, true);
return scene;
}
const scene = createScene();
engine.runRenderLoop(() => {
// call render method for our scene
scene.render();
});
scene.registerBeforeRender() is more flexible in changing the values. In your example, you are changing rotation by 0.05 constant value. In some cases, this may be variable, so you can assign a variable instead of constant value.
It is tricky to change this in Animation class methods, because in KeyFrame, your key and values are fixed once you set them. The only way I could change this is remove animations and add new ones. On a positive side, in Animation class methods, you can change frameRate and change what you want to do in a particular frame.
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?
Trying to add 3D text in my react application by three JavaScript. Following code is giving a blank page in output. This is my 1st attempt with Three.js. Any help will be appreciated,
Same code is working while I am adding any other geometry with same format.
class ThreeScene extends Component {
componentDidMount() {
const width = this.mount.clientWidth
const height = this.mount.clientHeight
//ADD SCENE
this.scene = new THREE.Scene()
//ADD CAMERA
this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
this
.camera
.position
.set(-15, 0, 25);
this
.camera
.lookAt(this.scene.position);
// Add Font Loader
this.loader = new THREE.FontLoader();
const self = this;
this
.loader
.load('fonts/helvetiker_regular.typeface.json', function (font) {
var material = new THREE.MeshPhongMaterial({color: 0x0033ff, specular: 0x555555, shininess: 30});
var geometry = new THREE.TextGeometry('Hello', {
font: font,
size: 80,
height: 5,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 10,
bevelSize: 8,
bevelSegments: 5
});
self.textData = new THREE.Mesh(geometry, material);
self
.scene
.add(self.textData);
self.light = new THREE.DirectionalLight(0xffffff);
self
.light
.position
.set(0, 1, 1)
.normalize();
self
.scene
.add(self.light);
});
this.renderer = new THREE.WebGLRenderer({antialias: true});
this
.renderer
.setSize(width, height)
this
.mount
.appendChild(this.renderer.domElement);
this.start()
}
renderScene = () => {
this
.renderer
.render(this.scene, this.camera)
}
}
Your font defined here is causing that issue...
.load('fonts/helvetiker_regular.typeface.json',
Try using placing the file (fonts/helvetiker_regular.typeface.json') into your public folder (e.g fonts) and then access that using
.load('/fonts/helvetiker_regular.typeface.json',
So, I ran into the same issue. You want to make sure that you are including the json code example from the THREE.js. You can find it in there github repo if you do not have it locally.
Font Code
This is the most basic code to get it up and runnin ghten you can fill out your styling params.
let loader = new THREE.FontLoader();
let font = loader.parse(fontJSON);
let geometry = new THREE.TextGeometry("Hello World",{font: font, size: 1, height:1 });
let material = new THREE.MeshBasicMaterial({color:0xffffff});
let text = new THREE.Mesh(geometry, material);
text.position.x = 1;
scene.add(text)
NOTE: You will get an error for fontJSON is undefined if you do not include let fontJSON = {code from three.js github library}
I'm setting up a website that loads 3d models in ply format, and I'm using Three.js with his PLYLOADER to do that.
My problem is that every model is loading in a different position. I've managed to make them load in a perpendicular way with the ground, but some of them are looking, some are looking far away from the camera and others look like they turn around a Y axis (not their center).
This is my HTML div:
<div id="WebGL-output"></div>
This is my JS code:
<script type="text/javascript">
var lesson7 = {
scene: null,
camera: null,
renderer: null,
container: null,
controls: null,
clock: null,
stats: null,
init: function() { // Initialization
// create main scene
this.scene = new THREE.Scene();
this.scene.fog = new THREE.FogExp2(0xcce0ff, 0.0003);
var SCREEN_WIDTH = '800',
SCREEN_HEIGHT = '600';
// prepare camera
var VIEW_ANGLE = 60, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
this.scene.add(this.camera);
this.camera.position.set(0, 0, 300);
this.camera.lookAt(new THREE.Vector3(0,0,0));
// prepare renderer
this.renderer = new THREE.WebGLRenderer({ antialias:true });
this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
this.renderer.setClearColor(this.scene.fog.color);
this.renderer.shadowMapEnabled = true;
this.renderer.shadowMapSoft = true;
// prepare container
this.container = document.getElementById('WebGL-output');
this.container.appendChild(this.renderer.domElement);
// events
THREEx.WindowResize(this.renderer, this.camera);
// prepare controls (OrbitControls)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.target = new THREE.Vector3(0, 0, 0);
this.controls.maxDistance = 20000;
// prepare clock
this.clock = new THREE.Clock();
this.scene.add( new THREE.AmbientLight(0xFFFFFF) );
// light
var dirLight = new THREE.DirectionalLight(0x606060);
dirLight.position.set(200, 200, 1000).normalize();
this.camera.add(dirLight);
this.camera.add(dirLight.target);
// load models
this.loadModels();
},
loadModels: function() {
// prepare PLY loader and load the model
var scene = new THREE.Scene();
var oPlyLoader = new THREE.PLYLoader();
oPlyLoader.load("../models/{{ $analysis->three_d_model }}", function(geometry) {
var material = new THREE.MeshPhongMaterial( { color: 0xAAAAAA, specular: 0x111111, shininess: 0 } );
var meshMaterial = material;
// if (geometry.hasColors) { // to fix when models will have colors
meshMaterial = new THREE.MeshPhongMaterial({ opacity: 1, vertexColors: THREE.VertexColors });
// }
var mesh = new THREE.Mesh(geometry, meshMaterial);
// Solution: center mesh to world origin
mesh.geometry.computeBoundingBox();
var bbox = mesh.geometry.boundingBox;
var centerVec = new THREE.Vector3();
bbox.getCenter(centerVec); // get center of bbox into centerVec
// move mesh so that center of bbox is placed to world origin
mesh.applyMatrix(new THREE.Matrix4().makeTranslation(-centerVec.x, -centerVec.y, -centerVec.z));
// proceed doing stuff
// mesh.position.set(0, -6, 0);
// mesh.scale.set(5, 5, 5);
lesson7.scene.add(mesh);
});
}
};
// Animate the scene
function animate() {
requestAnimationFrame(animate);
render();
update();
}
// Update controls and stats
function update() {
lesson7.controls.update(lesson7.clock.getDelta());
// lesson7.stats.update();
}
// Render the scene
function render() {
if (lesson7.renderer) {
lesson7.renderer.render(lesson7.scene, lesson7.camera);
}
}
// Initialize lesson on page load
function initializeLesson() {
lesson7.init();
animate();
}
if (window.addEventListener)
window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;
</script>
I need help to make them all load with the same camera position, looking centred and in the same size.
PS: If i can have better results loading in different extension, I'm up for it.
Thanks in advance
EDIT:
I updated the code with the help of #alex. Now my models are very well centered. The only thing missing now si a fixed camera position however the model was saved
Currently see two solutions here ( if i understand correctly )
You objects are not centered correctly. The solution would be to open the model files in a 3d editor and set them all to the same size and make their center points consistent.
Once you load the model add it to an Object3D and find its center.
Docs about that here: https://threejs.org/docs/#api/en/core/Object3D
the below code this.objectAll is the Object3D. Which is put into a box and we make the controls look at the center of that box. If all model are consistent then this should work.
this.boundingBox = new THREE.Box3();
this.boundingBox.setFromObject(this.objectAll);
this.boundingBox.center(this.controls.target);
Best of luck!
Im trying to get the shadows right using Babylonjs. Without any joy :p
Here is the resources on shadows I have found
Babylonjs wiki: Shadows
framework for building 3D games with HTML5 and WebGL
but I can't find anything on "element on element" shadows. :(
here is my attempt at it:
my sources loosely based on Babylonjs wiki: 17-Shadows
I have 2 lights and 3 objects
I get a shadow behind the sphere but then I also get an artefact on the front face of the sphere.
► Live code:
jsfiddle.net/codemeasandwich/z64Ba
I appreciate your help as I've been struggling with this for a while.
function createSceneTuto(engine) {
var scene = new BABYLON.Scene(engine);
//freeCamera is a FPS like camera where you control the camera with the cursors keys and the mouse
//touchCamera is a camera controlled with touch events (it requireshand.jsto work)
//arcRotateCamera is a camera that rotates around a given pivot. It can be controlled with the mouse or touch events (and it also requires hand.js to work)
// ArcRotateCamera >> Camera turning around a 3D point (here Vector zero)
// Parameters : name, alpha, beta, radius, target, scene
var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 90, BABYLON.Vector3.Zero(), scene);
camera.setPosition(new BABYLON.Vector3(30, 30, 30));
// pointLight (like the sun for instance) which emits lights in every direction from a specific position
// directionalLight which emits lights from the infinite towards a specific direction
var light = new BABYLON.DirectionalLight("Dir0", new BABYLON.Vector3( -1,0, 0), scene);
var light0 = new BABYLON.PointLight("Omni0", new BABYLON.Vector3(1, 10, 100), scene);
light0.diffuse = new BABYLON.Color3( 0,1, 0);
light0.specular = new BABYLON.Color3(1, 1, 1);
var box = BABYLON.Mesh.CreateBox("Box", 3, scene);
var torus = BABYLON.Mesh.CreateTorus("torus", 5, 1, 20, scene);
// var plan = BABYLON.Mesh.CreatePlane("Plane", 50.0, scene);
// plan.position.z = -40
var sphere = BABYLON.Mesh.CreateSphere("Sphere", 15, 20, scene);
// Shadows
var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
var shadowGenerator0 = new BABYLON.ShadowGenerator(1024, light0);
shadowGenerator.getShadowMap().renderList.push(box);
shadowGenerator.getShadowMap().renderList.push(torus);
shadowGenerator.getShadowMap().renderList.push(sphere);
shadowGenerator0.getShadowMap().renderList.push(box);
shadowGenerator0.getShadowMap().renderList.push(torus);
shadowGenerator0.getShadowMap().renderList.push(sphere);
box.receiveShadows = true;
torus.receiveShadows = true;
sphere.receiveShadows = true;
var alphaTorus = 0, alphaBox =0;
scene.registerBeforeRender(function () {
torus.rotation.x += 0.02;
torus.position = new BABYLON.Vector3(Math.cos(alphaTorus) * 15, 0, Math.sin(alphaTorus) * 15);
alphaTorus += 0.003;
box.position = new BABYLON.Vector3(Math.cos(alphaBox) * 15, 0, Math.sin(alphaBox) * 15);
alphaBox += 0.01;
});
return scene;
}
► The above lights as directional lights
var light = new BABYLON.DirectionalLight("Dir0", new BABYLON.Vector3( -1,0, 0), scene);
light.position = new BABYLON.Vector3(0, 0, 20);
light.intensity = 0.5;
var light0 = new BABYLON.DirectionalLight("Omni0", new BABYLON.Vector3(0,0,-1), scene);
light0.position = new BABYLON.Vector3(25, 0, 0);
light.intensity = 0.5;
Only directional lights can cast shadows and they also need a position to define from where the shadows come
I update the wiki to add this IMPORTANT information :)
Only directional ligths can cast shadows:
var light = new BABYLON.DirectionalLight("dir01", new BABYLON.Vector3(-1, -2, -1), scene);
You must also define a position for your light (because Babylon.js must define a point of view to > create the shadow map):
light.position = new BABYLON.Vector3(20, 40, 20);
Please note that you should have to move the position to define the area where the shadows are seen.