[英]Turning animation model's visible property on and off interferes with ThreeJS animate/render loop?
在我的ThreeJS
應用程序 (r124) 中,我有一個 GLB animation model 我附加了一個聚光燈,讓它看起來像一架帶燈的無人機,可以打開和關閉。 我有一個 function 來“打開燈”,它唯一做的就是通過將其visible
屬性設置為true
來使燈可見:
this.turnOnLight = function(bUseLaserScan=false) {
if (self.CAC.modelInstance.visible === false)
self.CAC.modelInstance.visible = true;
}
不幸的是,這樣做會干擾渲染/動畫循環。 每次我開燈(或關燈)時,都會有一段 200-300 毫秒范圍內的長時間停頓,將場景中的所有東西都凍結了那么長的時間。 換句話說,渲染循環在 200-300 毫秒內未被調用。 如果我注釋掉改變光的可見性屬性的語句,暫停/延遲就會消失。
為什么簡單地打開和關閉模型的可見性屬性會凍結場景,我該如何解決這個問題?
如果我每次打開光子對象時都加載一個大的 model 或其他東西,我可以看到這種情況發生。 但是,當我所做的只是將visible
屬性設置為 TRUE 時,這是怎么發生的呢?
以下是我構建 Spotlight 並將其附加到相機無人機主 animation object 的方法。注意, self.CAC.modelInstance
只是一個 object,我用來聚合所有 animation 模型共有的 ThreeJS 對象和屬性。
function AnimatedLight(
modelName,
modelDeclJsonObj,
threeJsLightObjectName='PointLight',
topLevelAnimationModelObj=null,
bAddLensSphere=false,
bAddLaserScan=false) {
const self = this;
this._buildTheLight = function(
threeJsLightObjectName,
modelDeclJsonObj) {
const methodName = self.constructor.name + '::' + `_buildTheLight`;
const errPrefix = '(' + methodName + ') ';
let retLightObj = null;
if (misc_shared_lib.isEmptySafeString(threeJsLightObjectName))
throw new Error(`${errPrefix}The threeJsLightObjectName parameter is empty.`);
if (!misc_shared_lib.isNonNullObjectAndNotArray(modelDeclJsonObj))
throw new Error(`${errPrefix}The modelDeclJsonObj is not a valid object.`);
// Provide default values for the properties the modelDeclJsonObj
// object doesn't have.
//
// Default color is RED.
let color = typeof modelDeclJsonObj.color !== 'undefined' ? modelDeclJsonObj.color : 0xf41321; // 0xffffff;
let intensity = typeof modelDeclJsonObj.intensity !== 'undefined' ? modelDeclJsonObj.intensity : 8.6; // 1.0;
let distance = typeof modelDeclJsonObj.distance !== 'undefined' ? modelDeclJsonObj.distance : 0.0;
let decay = typeof modelDeclJsonObj.decay !== 'undefined' ? modelDeclJsonObj.decay : 1.0;
// These properties are only for spot-lights, which have an inner angle.
// NOTE: We store angles in the JSON model initialization declaration
// object because they are easier to specify then angles expressed
// in radians.
let angle = (0.8 * MAX_ANGLE_FOR_SPOTLIGHT); // Math.PI / 3; // Default value.
if (typeof modelDeclJsonObj.inner_angle_in_degrees !== 'undefined') {
if (typeof modelDeclJsonObj.inner_angle_in_degrees !== 'number')
throw new Error(`${errPrefix} The "inner_angle_in_degrees" property in the model JSON declaration object is not a number.`);
angle = THREE.MathUtils.degToRad(modelDeclJsonObj.inner_angle_in_degrees);
}
let penumbra = 0;
if (typeof modelDeclJsonObj.penumbra_angle_in_degress !== 'undefined') {
if (typeof modelDeclJsonObj.penumbra_angle_in_degress !== 'number')
throw new Error(`${errPrefix} The "penumbra_angle_in_degress" property in the model JSON declaration object is not a number.`);
// ThreeJS uses a range of 0 to 1.0 for the penumbra angle, so
// we divide by 180 to rescale the provided value.
penumbra = Math.min(THREE.MathUtils.degToRad(modelDeclJsonObj.penumbra_angle_in_degress / 180.0), 1);
}
// Build the correct ThreeJS light object given the specified
// light object type.
if (threeJsLightObjectName === 'PointLight') {
retLightObj = new THREE.PointLight(color, intensity, distance, decay);
}
else if (threeJsLightObjectName === 'DirectionalLight') {
retLightObj = new THREE.DirectionalLight(color, intensity);
}
else if (threeJsLightObjectName === 'SpotLight') {
// Create a mini-menu for this type of light.
retLightObj = new THREE.SpotLight(color, intensity, distance, angle, penumbra, decay);
// Is a lens sphere desired?
if (bAddLensSphere) {
// Yes. Create it.
// .................... BEGIN: SUB-OBJECT - Lens Sphere ............
const radius = 3;
self.lensSphereObj =
new THREE.Mesh(
new THREE.SphereBufferGeometry(radius, 20, 20),
new THREE.MeshPhongMaterial({color: 0xFF0000}));
// Add it to the top level animation model.
// self.CAC.modelInstance.add(cameraLensObj);
// Add it to the spotlight object.
retLightObj.add(self.lensSphereObj);
// .................... END : SUB-OBJECT - Lens Sphere ............
}
}
else
throw new Error(`${errPrefix}Invalid threeJsLightObjectName value: ${threeJsLightObjectName}`);
return retLightObj;
}
/**
* Makes the light visible.
*/
this.turnOnLight = function(bUseLaserScan=false) {
if (self.CAC.modelInstance.visible === false)
self.CAC.modelInstance.visible = true;
}
/**
* Makes the light invisible.
*/
this.turnOffLight = function() {
if (self.CAC.modelInstance.visible === true)
self.CAC.modelInstance.visible = false;
}
this.setTargetForSpotLight = function(targetObj) {
self.CAC.modelInstance.target = targetObj;
}
/**
* This method must be called to initialize this
* animated light for animation.
*/
this.initializeModel = function (
parentAnimManagerObj,
initModelArgs= null,
funcCallWhenInitialized = null,
) {
const methodName = self.constructor.name + '::' + `initializeModel`;
const errPrefix = '(' + methodName + ') ';
// Store the reference to the AnimationManager() object
// that owns us in the CommonAnimationObject we
// aggregate.
self.CAC.parentAnimManagerObj = parentAnimManagerObj;
// Create a ThreeJS light object from the model (light)
// declaration in the JSON declarations block.
self.CAC.modelInstance = self._buildTheLight(threeJsLightObjectName, initModelArgs);
self.lensSphereObj.position.set(
0,
0,
-20);
// Animated lights don't use a model loader.
self.CAC.modelLoader = null;
// Set the initial position.
self.CAC.modelInstance.position.x = initModelArgs.initialPos_X;
self.CAC.modelInstance.position.y = initModelArgs.initialPos_Y;
self.CAC.modelInstance.position.z = initModelArgs.initialPos_Z;
// Add the model to the scene.
g_ThreeJsScene.add(self.CAC.modelInstance);
// Finish the initialization process..
self.CAC.initializeModelCompletely(null);
// Execute the desired callback function, if any.
if (funcCallWhenInitialized)
funcCallWhenInitialized(self);
}
答案有點復雜。 Three.js 在材質的着色器代碼中使用一個循環來渲染每個光源。 例如,如果您有 3 個聚光燈,您會看到如下內容:
for (int i = 0; i < 3; i++ ) {
// Perform spotlight calculations
}
當您移除燈光時,引擎會重新編譯着色器代碼,因此它只會循環兩次: i < 2
。 必須對場景中的每種材質都執行此操作,因此根據場景的復雜性和顯卡,新的着色器編譯可能會造成瓶頸。
為了解決這個問題,我建議您只需將光強度更改為 0即可將其關閉。 這樣您就不會修改着色器,它只會更改變量的值。
this.toggleLight = function() {
if (light.intensity === 0) {
light.intensity = 1; // ... or whatever value you need
} else {
light.intensity = 0;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.