Cannot use stats.js with three.js - three.js

I want to see the fps
my code pen
(THE CODE PEN IS EDITED AND IS WORKING CORRECTLY)
I followed
this
tutorial in packtpub.com
I also followed the instructions in github repo
I also tried npm install though code pen npm installer
which gave me this line
import installStats from "https://cdn.skypack.dev/install-stats#1.0.6";
relevent code
import installStats from "https://cdn.skypack.dev/install-stats#1.0.6";
var stats = new Stats();
stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom
document.body.appendChild( stats.dom );
.....
const clock = new THREE.Clock();
const tick = () => {
// stats.begin();
const elapsedTime = clock.getElapsedTime();
// Update controls
controls.update();
// Update time
waterMaterial.uniforms.uTime.value = elapsedTime;
// Render
renderer.render(scene, camera);
// stats.end();
// Call tick again on the next frame
window.requestAnimationFrame(tick);
};
tick();

I found that changing the stats cdn version from r17 to r16 can make it work.
use
https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js
instead of
https://cdnjs.cloudflare.com/ajax/libs/stats.js/r17/Stats.min.js
and I also got rid of this line
import installStats from "https://cdn.skypack.dev/install-stats#1.0.6";

Related

How to use node-modules installed by npm from template

I am trying to use a library installed by npm in the Go template.
I installed the necessary three libraries through 'npm install three', saved in the root folder as shown in the screenshot below.
After that, I try to import and use the three.js module as shown below, but threejs is not found.
I think there is some problem with the filesystem, how can I use threejs without problems in my situation?
import * as THREE from "/three";
class App{
constructor(){
// 장면
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);
renderer.render(scene, camera);
}
}
window.onload = function(){
new App();
}
error
import * as THREE from "/three";
http://localhost:8081/three net::ERR_ABORTED 404 (Not Found)
import * as THREE from "three";
Uncaught TypeError: Failed to resolve module specifier "three". Relative references must start with either "/", "./", or "../".
e.Static("/node", "node_modules") in main.go
import * as THREE from "/node/node_modules/three/builld/three.module.js";
GET http://localhost:8081/node/three/builld/three.module.js net::ERR_ABORTED 404 (Not Found)
<script type="importmap">
{
"imports": {
"three": "/node/three/build/three.module.js"
}
}
</script>
It was resolved by correcting the path to the job.

OutlinePass is not rendered to scene in THREE.js

I followed these examples to make the outline for objects when they are selected:
https://threejs.org/examples/?q=out#webgl_postprocessing_outline
https://github.com/scqilin/three-OutlinePass
No error is found, yet outline does not appear when the object is selected. The highlightSelectedObject function is correcly triggered when an object is selected. selectedObjects is not null.
In my case, THREE.js is installed in the project file. Scene, camera and renderer are instantiated elsewhere.
import * as THREE from "../../build/three.module.js";
import {OutlinePass} from "../../examples/jsm/postprocessing/OutlinePass.js";
import {RenderPass} from "../../examples/jsm/postprocessing/RenderPass.js";
import {EffectComposer} from "../../examples/jsm/postprocessing/EffectComposer.js";
Function:
function highlightSelectedObject(selectedObjects) {
if (selectedObjects != null) {
const scene = project.currentScene.scene;
const camera = project.currentScene.camera;
const renderer = project.renderer;
var composer = new EffectComposer(renderer);
var renderPass = new RenderPass(scene, camera);
var outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera, selectedObjects);
outlinePass.renderToScreen = true;
outlinePass.selectedObjects = selectedObjects;
composer.addPass(renderPass);
composer.addPass(outlinePass);
const params = {
edgeStrength: 2,
edgeGlow: 1,
edgeThickness: 1.0,
pulsePeriod: 0,
usePatternTexture: false
};
outlinePass.edgeStrength = params.edgeStrength;
outlinePass.edgeGlow = params.edgeGlow;
outlinePass.visibleEdgeColor.set(0xffffff);
outlinePass.hiddenEdgeColor.set(0xffffff);
composer.render(scene, camera);
}
}
The path to THREE.js should be correct. Is it a problem with render?
I had a similar issue. Upon looking at another example, I found that setting outlinePass.renderToScreen = true allowed it to work. It might not be there depending what version of the the outlinePass.js you are using. I looked at the code on the deployed example and it is there.

Applying two different fragment shaders to two different materials (of the same type) using onBeforeCompile?

I've imported a GLTF file with two different meshes. My goal is to give each mesh a material with a unique custom fragment shader using onBeforeCompile. Each mesh has the same type of material (MeshNormalMaterial).
When I try to apply one fragment shader to one material and the other fragment shader to the other material, both materials wind up with the same fragment shader. The fragment shader each material has depends on which material I setup first.
Here's a few pictures showing what I'm talking about:
Below is all the relevant code.
Main code: This is the general structure of my code. I've enclosed the important part between "PRIMARY AREA OF INTEREST" comments. For simplicity, I've replaced my shader code with "..." or a comment describing what it does. They do work as shown in the pictures above.
// Three.JS Canvas
const threeDisplay = document.getElementById("threeDisplay");
// Globals
var displayDimensions = getElemDimensions(threeDisplay); // Uniform
var currentTime = 0; // Uniform
var helix = null; // Mesh
var innerHelix = null; // Mesh
var horseshoe = null; // Mesh
// Set the scene and camera up
const scene = new THREE.Scene();
const camera = initCamera();
// Setup a directional light
const light = new THREE.DirectionalLight( 0xffffff, 1.0 );
light.position.set(-0.2, 1, -0.6);
scene.add(light);
// Setup WebGL renderer
const renderer = initRenderer();
threeDisplay.appendChild( renderer.domElement );
// Load the gltf model
new GLTFLoader().load( "./spiral_pillar_hq_horseshoe.glb", function (object) {
const helixFragmentShaderReplacements = [
{
from: ' ... ',
to: ' // rainbow '
}
];
const horseshoeFragmentShaderReplacements = [
{
from: ' ... ',
to: ' // white '
}
];
//////////////////////////////////////
// PRIMARY AREA OF INTEREST - START //
//////////////////////////////////////
// Turn the horseshoe into a shader.
horseshoe = object.scene.children[1];
var horseshoeGeometry = horseshoe.geometry;
var horseshoeMaterial = shaderMeshMaterial(new THREE.MeshNormalMaterial(), horseshoeGeometry, horseshoeFragmentShaderReplacements);
var horseshoeMesh = new THREE.Mesh(horseshoeGeometry, horseshoeMaterial);
horseshoe = horseshoeMesh;
horseshoe.rotation.z = deg2rad(180); // Re-orient the horseshoe to the correct position and rotation.
horseshoe.position.y = 13;
scene.add(horseshoe);
// Turn the inner helix into a colorful, wiggly shader.
helix = object.scene.children[0];
var helixGeometry = helix.geometry;
var helixMaterial = shaderMeshMaterial(new THREE.MeshNormalMaterial(), helixGeometry, helixFragmentShaderReplacements);
var helixMesh = new THREE.Mesh(helixGeometry, helixMaterial);
helix = helixMesh;
scene.add(innerHelix);
animate();
////////////////////////////////////
// PRIMARY AREA OF INTEREST - END //
////////////////////////////////////
}, undefined, function (error) {
console.error(error);
});
Below are functions which are relevant.
shaderMeshMaterial: Constructs a new material based on the supplied materialType that supports editing the default shader. If it's not initProcessing, then the problem may stem from this function.
// Globals used: displayDimensions
function shaderMeshMaterial(materialType, geometry, fragmentShaderReplacements) {
var material = materialType;
material.onBeforeCompile = function ( shader ) {
// Uniforms
shader.uniforms.time = { value: 0 };
shader.uniforms.resolution = { value: new THREE.Vector2(displayDimensions.width, displayDimensions.height) };
shader.uniforms.bboxMin = { value: geometry.boundingBox.min };
shader.uniforms.bboxMax = { value: geometry.boundingBox.max };
fragmentShaderReplacements.forEach((rep) => {
shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);
});
console.log(shader);
material.userData.shader = shader;
}
return material;
}
initRenderer: Sets up the renderer. Just showing you guys the renderer setup I have in case that's important.
// Globals used: displayDimensions
function initRenderer() {
var renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true,
precision: "mediump"
});
renderer.setClearColor( 0x000000, 0);
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( displayDimensions.width, displayDimensions.height );
renderer.shadowMap.enabled = true;
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.25;
return renderer;
}
animate: Handles the animation frames.
// Globals used: renderer, currentTime, postprocessing
function animate (timestamp = 0) {
requestAnimationFrame(animate);
resizeRendererToDisplaySize(renderer);
currentTime = timestamp/1000; // Current time in seconds.
scene.traverse( function ( child ) {
if ( child.isMesh ) {
const shader = child.material.userData.shader;
if ( shader ) {
shader.uniforms.time.value = currentTime;
}
}
} );
renderer.render( scene, camera );
postprocessing.composer.render( 0.1 );
};
One last thing to note is that when I inspected the console log of shader from the shaderMeshMaterial function, I can see that the fragment shaders are indeed different as they should be for each material. Also not sure why there are 4 console logs when there should only be 2.
Sorry for all the code, but I did condense it to where all irrelevant code was stripped out. I'm fairly new to Three.JS, so any possible explanations as to why this is happening are much appreciated!
EDIT: Removed vertex shader parameter from shaderMeshMaterial function to keep this question focused on just the fragment shaders. Though this problem does apply to both the vertex and fragment shaders, I figure if you fix one then you'll fix the other.
EDIT 2: Added language identifiers to code snippets. Also I removed the postprocessing function and the problem still persists, so I know the problem isn't caused by that. I've updated the code above to reflect this change. As a happy side effect of removing the postprocessing function, the console.log of the shader variable from shaderMeshMaterial new appears twice in the log (as it should).
EDIT 3: (Implementing WestLangley's suggestion) I tweaked the shaderMeshMaterial function by adding the customProgramCacheKey function. I had to condense the four parameters of shaderMeshMaterial into one for the sake of the customProgramCacheKey function. I believe I implemented the function correctly, but I'm still getting the same result as before where both materials display the same fragment shader.
New "PRIMARY AREA OF INTEREST" code:
horseshoe = object.scene.children[1];
var horseshoeGeometry = horseshoe.geometry;
var meshData = {
materialType: new THREE.MeshNormalMaterial(),
geometry: horseshoeGeometry,
fragmentShaderReplacements: horseshoeFragmentShaderReplacements
}
var horseshoeMaterial = shaderMeshMaterial(meshData);
var horseshoeMesh = new THREE.Mesh(horseshoeGeometry, horseshoeMaterial);
horseshoe = horseshoeMesh;
horseshoe.rotation.z = deg2rad(180); // Re-orient the horseshoe to the correct position and rotation.
horseshoe.position.y = 13;
scene.add(horseshoe);
// Turn the inner helix into a colorful, wiggly shader.
helix = object.scene.children[0];
var helixGeometry = helix.geometry;
var meshData2 = {
materialType: new THREE.MeshNormalMaterial(),
geometry: helixGeometry,
fragmentShaderReplacements: helixFragmentShaderReplacements
}
var helixMaterial = shaderMeshMaterial(meshData2);
var helixMesh = new THREE.Mesh(helixGeometry, helixMaterial);
helix = helixMesh;
scene.add(innerHelix);
animate();
New shaderMeshMaterial code:
// Globals used: displayDimensions
function shaderMeshMaterial(meshData) {
var material = meshData.materialType;
material.onBeforeCompile = function ( shader ) {
// Uniforms
shader.uniforms.time = { value: 0 };
shader.uniforms.resolution = { value: new THREE.Vector2(displayDimensions.width, displayDimensions.height) };
shader.uniforms.bboxMin = { value: meshData.geometry.boundingBox.min };
shader.uniforms.bboxMax = { value: meshData.geometry.boundingBox.max };
meshData.fragmentShaderReplacements.forEach((rep) => {
shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);
});
material.customProgramCacheKey = function () {
return meshData;
};
console.log(shader);
material.userData.shader = shader;
}
return material;
}
WestLangley suggestion worked for me!
material.onBeforeCompile = ...
// Make sure WebGLRenderer doesnt reuse a single program
material.customProgramCacheKey = function () {
return UNIQUE_PER_MATERIAL_ID;
};
I believe your mistake is returning meshData from customProgramCacheKey.
I think customProgramCacheKey need concrete identifier like a number or string.
It would be nice to understand what exactly happening and why do we need to specify customProgramCacheKey.
EDIT: I discover that default value for customProgramCacheKey calculated as follow in Threejs source.
customProgramCacheKey() {
return this.onBeforeCompile.toString();
}
Perhaps this is explains this default caching behavior because calling toString on function returns that function body literally as string.
For example consider function const myFunc = () => { return 1 }. Calling myFunc.toString() returns "() => { return 1 }"
So if your calling onBeforeCompile in a for loop you function body as string never change.

how to Combe three.js page into A-frame page

Here is a Three.js Example from stemkoski, now I want to use this Texture-Animation plane or box in A-frame page, how can I Combine it.
A-frame Version: 0.9.0
I couldn't find any examples.
When integrating three.js pieces into aframe, it's recommended to use custom components. Here's a simple example:
js
AFRAME.registerComponent('foo', {
// this is called upon initialization
init: function() {
// we'll need this later on for updating the animation
this.animator = null
// wait until the component is loaded
this.el.addEventListener('loaded', e => {
// copied straight from stemkoski's code:
var runnerTexture = new THREE.ImageUtils.loadTexture( 'images/run.png' );
this.animator = new TextureAnimator( runnerTexture, 10, 1, 10, 75 );
// apply the texture to our element
let mesh = this.el.getObject3D('mesh')
mesh.material.map = runnerTexture
mesh.material.needsUpdate = true
})
},
// this is called before each render loop
tick: function(time, delta) {
// update only if animator was created
if (!this.animator) return
this.animator.update(1000 * delta);
}
})
HTML:
<a-plane foo></a-plane>
glitch here. To make it work with a glitch i had to preload the image with a-assets due to cors issues.

Three.js clone FBX with animation

I can’t seem to be able to clone an FBX model (FBX downloaded from Mixamo) while retaining animation keyframes.
Have attempted a number of approaches including using the cloneFbx gist (included in the example below); all to no avail. Even placing the entire FBXLoader() function inside a loop does not work as expected since only one of the models will animate at a time.
This issue has been partially addressed here, but I cannot seem to ‘copy’ the animation sequence as answer suggests.
Can anyone point out where I’m going wrong?
Here's a rough example of one of my tests:
Load fbx model and store animation:
var loader = new THREE.FBXLoader();
loader.load( 'models/Walking.fbx', function ( fbx ) {
clip = fbx.animations[ 0 ];
// createVehicle(fbx); // Works! Creates one animated model via FBX
// cloneFbx via: https://gist.github.com/kevincharm/bf12a2c673b43a3988f0f171a05794c1
for (var i = 0; i < 2; i++) {
const model = cloneFbx(fbx);
createVehicle(model);
}
});
Add mixers and actions based on stored clip, add model to scene:
function createVehicle(model){
model.mixer = new THREE.AnimationMixer( model );
mixers.push( model.mixer );
var action = model.mixer.clipAction( clip );
action.play();
model.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
});
const x = Math.random() * groundSize - groundSize/2;
const z = Math.random() * groundSize - groundSize/2;
model.position.set(x, 0, z);
const vehicle = new Vehicle(model, x, z);
vehicles.push(vehicle);
scene.add( model );
}
Animation cycle:
if ( mixers.length > 0 ) {
for ( var i = 0; i < mixers.length; i ++ ) {
mixers[ 0 ].update( clock.getDelta() );
}
}
Couldn’t figure out an elegant solution to this. Best I could come up with is creating a loop with the loading sequence inside of it; this is very slow (since the FBX has to be parsed each time).
The key here was having an animation mixer controlling the animated objects as a group as opposed to creating a mixer per animated object.
If anyone can figure out a better solution, I would be super keen to hear it (perhaps using the cloneFbx script properly).
Create mixer, load FBX:
// Create mixer to run animations
mixer = new THREE.AnimationMixer( scene );
// Load fbx
var loader = new THREE.FBXLoader();
for (var i = 0; i < 5; i++) {
loader.load( 'models/Walking.fbx', function ( fbx ) {
mixer.clipAction( fbx.animations[ 0 ], fbx )
.startAt( - Math.random() )
.play();
createVehicle(fbx);
});
}
Create class instances, add to scene:
function createVehicle(model){
const x = Math.random() * groundSize - groundSize/2;
const z = Math.random() * groundSize - groundSize/2;
model.position.set(x, 0, z);
const vehicle = new Vehicle(model, x, z);
vehicles.push(vehicle);
scene.add( model );
}
Draw cycle:
mixer.update( clock.getDelta() );
I found out that SkeletonUtils.clone() works good for me.
https://threejs.org/docs/index.html#examples/en/utils/SkeletonUtils.clone

Resources