更新问题的原因已找到 - 请参阅问题的更新部分。
我有一个使用 THREE.js (r60) 的复杂应用程序,它向主场景添加了一个特殊的平面对象。平面几何由内部提供的 base64 uri 图像(大小 16x16、32x32 或 64x64 像素)的高度映射确定。该场景有两个静态光(环境光和定向光)和一个可打开和关闭的可移动点光源。
在复杂的应用程序 中,点光源不会被平面对象反射。(点光源通过按“R”键或按钮来切换)。
我使用 THREE.js 最新版本 (r70) 制作了第一个 JSFiddle 示例,其中灯光工作正常。
[更新] 我现在使用较旧的 THREE.js 库 (r60)制作了第二个 JSFiddle 示例,它也可以正常工作。
我怀疑复杂应用程序(r60)中的问题可能与系统容量和/或时序/排序有关。容量绝对是一个问题,因为其他更简单的场景对象(盒子和圆柱体)显示对点光源的单独响应或不响应,从应用程序的一次运行到下一次,这似乎取决于系统活动的整体水平(cpu、内存使用情况)。这些更简单的对象可能会在一次运行中反映,但不会在下一次运行中反映。但是高度映射的平面对象始终不反射点光。这些行为在 (i) Win7 笔记本电脑和 (ii) Android Kitkat 平板电脑上观察到。
高度映射过程可能是部分原因。我这样说是因为当我注释掉高度映射平面并激活一个简单的类似平面对象(具有随机分配的 z 级别)时,后一个平面的行为与预期一样(即它反射点光)。
我想现在通常的方法是将我的复杂应用程序升级到 r70(这不是一个简单的步骤),然后开始禁用应用程序块以缩小原因。然而,实现高度映射的方式(例如使用回调)可能是解释高度映射平面无法反射点光的一个因素。
[重新编写] 因此,如果有人可以查看正确工作、先前引用的 (r70) JSFiddle 示例中的代码并指出任何明显的设计错误,我将不胜感激(如果应用在更复杂、更严重加载的应用程序)可能会导致高度映射平面无法反射点光。
(r70) JSFiddle 的完整代码(javascript,不是 html 或 css):-
//... Heightmap from Image file
//... see http://danni-three.blogspot.co.uk/2013/09/threejs-heightmaps.html
var camera, scene, renderer;
var lpos_x = -60,lpos_y = 20,lpos_z = 100;
var mz = 1;
var time = 0, dt = 0;
var MyPlane, HPlane;
base64_imgData = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAeAB4AAD/4QBoRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAASAAAATgAAAAAAAAB4AAAAAQAAAHgAAAABUGFpbnQuTkVUIHYzLjUuMTAA/9sAQwANCQoLCggNCwsLDw4NEBQhFRQSEhQoHR4YITAqMjEvKi4tNDtLQDQ4RzktLkJZQkdOUFRVVDM/XWNcUmJLU1RR/9sAQwEODw8UERQnFRUnUTYuNlFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFR/8AAEQgAIAAgAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A19Z8SXdu5KOMKxBAFOi1uTUdNJguxFcAchv6Vz2so/mzKc8sc8VX8MyQjUVWYNweCO9AEsOuX8s+xrqWQh8DJ4rsJCphSN3Czsm7ArG1bT7fSFe7EZJzuX3J6VQsdRnvryJ2+/wooA6O501JY7yRh0U8Vyg1WzsghhsAkqnBO4nd713t8NsEqIhJfqRXEahotxPJlISDnOaANzWvL1rR4JiTG6ryorG0C2aDUI02lhu6kVZ02wvLVSpYtu65yRXQaZYvDL5rhRx2oA//2Q==";
init();
animate();
//==================================================================
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 10);
camera.position.x = 1300;
camera.position.y = 400;
camera.position.z = 0;
camera.lookAt(0, 0, 0);
scene.add(camera);
scene.add(new THREE.AmbientLight(0x001900));
SunLight = new THREE.DirectionalLight(0xff0000,.3,20000);//...color, intensity, range.
SunLight.position.set(0, 3000, -8000);
scene.add(SunLight);
//POINT LIGHT
PL_color = 0x0000ff;
PL_intensity = 10;
PL_range_to_zero_intensity = 1200;
PL = new THREE.PointLight(PL_color, PL_intensity, PL_range_to_zero_intensity);
scene.add(PL);
PL_pos_x = -100;
PL_pos_y = -100;
PL_pos_z = 120;
PL.position.set(PL_pos_x, PL_pos_y, PL_pos_z);
//INDICATOR SPHERE
var s_Geometry = new THREE.SphereGeometry(5, 20, 20);
var s_Material = new THREE.MeshBasicMaterial({
color: 0xaaaaff
});
i_Sphere = new THREE.Mesh(s_Geometry, s_Material);
i_Sphere.position.set(PL_pos_x, PL_pos_y, PL_pos_z);
scene.add(i_Sphere);
//Plane02
var Plane02Geo = new THREE.PlaneGeometry(50, 50); //...
var Plane02Material = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide
}, {
color: 0xaaaaaa
});
Plane02 = new THREE.Mesh(Plane02Geo, Plane02Material);
Plane02.position.set(0, 0, -120);
scene.add(Plane02);
//PEAS
xxx = SOW_F_Make_peas();
//RENDERER
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = false;
document.body.appendChild(renderer.domElement);
// controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
xxx = SOW_F_Make_Heightmap_Object_from_Image_File(scene, camera);
} //...EOFunction Init
//==================================================================
function animate() {
dt = 0.1;
time += dt;
if (time < 10000) {
requestAnimationFrame(animate);
//move point light & indicator sphere
speed = 16;
if (Math.abs(PL_pos_z) > 400) mz = (-1)* mz;
PL_pos_x += 0.01 * speed * mz;
PL_pos_y += 0.05 * speed * mz;
PL_pos_z -= 0.2 * speed * mz;
PL.position.set(PL_pos_x, PL_pos_y, PL_pos_z);
i_Sphere.position.set(PL_pos_x, PL_pos_y, PL_pos_z);
renderer.render(scene, camera);
} else alert("Time=" + time + "Finished");
}
//==================================================================
function SOW_F_Make_Heightmap_Object_from_Image_File(givenScene, givenCamera) {
//... Read a Heightmap from a coloured image file
//... into a (pre-defined global) plane object called HPlane
MyImage = new Image();
MyImage.onload = function () {
var MyPlane_width = 1000;//6000; //...MyPlane width or height are in scene units and do not have to match image width or height
var MyPlane_height = 1000;//6000;
var MyPlane_w_segs = MyImage.naturalWidth - 1; //... important that this mapping is correct for texture 1 pixel :: 1 segment.
var MyPlane_h_segs = MyImage.naturalHeight - 1; //... important that this mapping is correct for texture 1 pixel :: 1 segment.
var Hgeometry = new THREE.PlaneGeometry(MyPlane_width, MyPlane_height, MyPlane_w_segs, MyPlane_h_segs);
//var texture = THREE.ImageUtils.loadTexture( '/images/Tri_VP_Texturemap.jpg' );
var texture = THREE.ImageUtils.loadTexture( base64_imgData );
//... Choose texture or color
//var Hmaterial = new THREE.MeshLambertMaterial( { map: texture, side: THREE.DoubleSide} );//....fails
var Hmaterial = new THREE.MeshPhongMaterial( {
color: 0x111111 , side: THREE.DoubleSide } ); //... works OK
HPlane = new THREE.Mesh(Hgeometry, Hmaterial);
//...get Height Data from Image
var scale = 0.6;//1//6; //0.25;
var Height_data = DA_getHeightData(MyImage, scale);
//... set height of vertices
X_offset = 0;
Y_offset = 0;
Z_offset = -100; //...this will (after rotation) add to the vertical height dimension (+ => up).
for (var iii = 0; iii < HPlane.geometry.vertices.length; iii++) {
//HPlane.geometry.vertices[iii].x = X_offset;
//HPlane.geometry.vertices[iii].y = Y_offset;
HPlane.geometry.vertices[iii].z = Z_offset + Height_data[iii];
}
//----------------------------------------------------------------------
//... Must do it in this order...Faces before Vertices
//... see WestLangley's response in http://stackoverflow.com/questions/13943907/my-object-isnt-reflects-the-light-in-three-js
HPlane.rotation.x = (-(Math.PI) / 2); //... rotate MyPlane -90 degrees on X
//alert("Rotated");
HPlane.geometry.computeFaceNormals(); //... for Lambert & Phong materials
HPlane.geometry.computeVertexNormals(); //... for Lambert & Phong materials
/*
HPlane.updateMatrixWorld();
HPlane.matrixAutoUpdate = false;
HPlane.geometry.verticesNeedUpdate = true;
*/
givenScene.add(HPlane);
HPlane.position.set(0, -150, 0);//... cosmetic
//return HPlane; //... not necessary, given that HPlane is global.
} ; //... End of MyImage.onload = function ()
//===============================================================
//... *** IMPORTANT ***
//... Only NOW do we command the script to actually load the image source
//... This .src statement will load the image from file into MyImage object
//... and invoke the pre-associated MyImage.OnLoad function
//... cause cross-origin problem: MyImage.src = '/images/Tri_VP_Heightmap_64x64.jpg'; //...if image file is local to this html file.
MyImage.src = base64_imgData;//... uses image data provided in the script to avoid Cross-origin file source restrictions.
} //... End of function SOW_F_Make_Heightmap_Object_from_Image_File
//===========================================================================
function DA_getHeightData(d_img, scale) {
//... This is used by function SOW_F_Make_Heightmap_Object_from_Image_File.
//if (scale == undefined) scale=1;
var canvas = document.createElement('canvas');
canvas.width = d_img.width; //OK
canvas.height = d_img.height;
var context = canvas.getContext('2d');
var size = d_img.width * d_img.height;
var data = new Float32Array(size);
context.drawImage(d_img, 0, 0);
for (var ii = 0; ii < size; ii++) {
data[ii] = 0;
}
var imgData = context.getImageData(0, 0, d_img.width, d_img.height);
var pix = imgData.data; //... Uint(8) UnClamped Array[1024] for a 16x16 = 256 pixel image = 4 slots per pixel.
var jjj = 0;
//... presumably each pix cell can have value 0 to 255
for (var iii = 0; iii < pix.length; iii += 4) {
var all = pix[iii] + pix[iii + 1] + pix[iii + 2];
//... I guess RGBA and we don't use the fourth cell (A, = Alpha channel)
jjj++;
data[jjj] = all * scale / 3; //...original code used 12 not 3 ??? and divided by scale.
//console.log (iii, all/(3*scale), data[jjj]);
}
return data;
} //... end of function DA_getHeightData(d_img,scale)
//==================================================================================================
function SOW_F_Get_A_Plane(givenScene, givenCamera) {
//...MyPlane width or height are in scene units and do not have to match image width or height
var MyPlane_width = 1000;
var MyPlane_height = 1000;
var MyPlane_w_segs = 64; //...
var MyPlane_h_segs = 64; //...
geometry = new THREE.PlaneGeometry(MyPlane_width, MyPlane_height, MyPlane_w_segs, MyPlane_h_segs);
//var material = new THREE.MeshLambertMaterial( { color: 0xeeee00, side: THREE.DoubleSide} );
var material = new THREE.MeshPhongMaterial({
color: 0xeeee00,side: THREE.DoubleSide
}); //... OK
MyPlane = new THREE.Mesh(geometry, material);
givenScene.add(MyPlane);
MyPlane.rotation.x = (-(Math.PI) / 2); // rotate it -90 degrees on X
MyPlane.position.set(0, 100, 0);
MyPlane.geometry.computeFaceNormals(); //...for Lambert & Phong materials
MyPlane.geometry.computeVertexNormals(); //...for Lambert & Phong materials
/*
MyPlane.geometry.verticesNeedUpdate = true;
MyPlane.updateMatrixWorld();
MyPlane.matrixAutoUpdate = false;
*/
} //... EOF SOW_F_Get_A_Plane
//====================================================================
function SOW_F_Make_peas()
{
//----------------- Make an array of spheres -----------------------
Pea_geometry = new THREE.SphereGeometry(5,16,16);
//Pea_material = new THREE.MeshNormalMaterial({ shading: THREE.SmoothShading});
Pea_material = new THREE.MeshPhongMaterial({ color: 0xaa5522});
// global...
num_peas = 1200;
for (var iii = 0; iii < num_peas; iii++)
{
//...now global
ob_Pea = new THREE.Mesh(Pea_geometry, Pea_material);
ob_Pea.position.set(
400 * Math.random() - 150,
300 * Math.random() - 150,
1200 * Math.random() - 150);
scene.add(ob_Pea);//TEST
}
}
更新
看来问题是分阶段的结果。请参阅这个新的 JSFiddle(r70)。Pointlight 在函数 init() 中创建,但未添加到场景中,或者在添加后立即从场景中删除。然后创建各种图形网格对象。当点光源被添加回场景(在动画循环中)时为时已晚 - 网格对象不会被点光源照亮。
一个程序解决方案是简单地不从场景中移除点光源,如果它们稍后要使用的话。如果他们需要暂时“熄灭”,那么只需调低强度并稍后再调高:例如
myPointLight.intensity = 0.00