ThreeJS逐步实现室内概念图的效果(渲染,交互)
近段时间由于项目需求,要在页面中插入3d的室内布局概念图,开始从零学习three.js的知识,下面记录一下这两周研究的结果,本人也是小白一枚,不足之处,还望批评指正!
第一个遇到的问题是室内地图块的渲染效果问题,使用一个光源时,效果总是不尽如人意,总有光照不到的一面是有黑色阴影,后来想到使用两个光源,两侧打光,渲染效果好了很多。
var ambiColor = "#f2f2f2"; var spotLight = new THREE.SpotLight(ambiColor); spotLight.position.set( -100, 100, -100); scene.add(spotLight); var spotLight2 = new THREE.SpotLight(ambiColor); spotLight2.position.set( 100, 100, 150); scene.add(spotLight2);
第二个问题是cube的材质问题,在研究threejs之前看到一篇有关室内地图的博文,由于实现效果相似,于是引用了博主对cube材质的思路:顶面和侧面使用不同的材质,我的第一想法就是在cube上面加一个plane,覆盖cube原先顶面的MeshLambertMaterial材质,但是这个想法在后来实现旋转时被否决了,因为plane的高度和cube重合时,会使的顶面和plane的颜色冲突,形成一条条的线,如果y轴方向稍微高出0.2,在俯视图上看不出上面问题,但是当相机的视角一调节,立马便看出问题,找了官方文档的案例,发现可以用MeshFaceMaterial自定义cube的六个面的颜色和材质。
var yellow = []; // 右侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"})); // 左侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"})); // 顶侧面 yellow.push(new THREE.MeshBasicMaterial({color: "#F8D3A5",transparent:true,opacity:0.8})); // 地侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"})); // 前侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"})); // 后侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"}));
var face1Material = new THREE.MeshFaceMaterial(yellow); var cube1Geom = new THREE.BoxGeometry(3,2,12); var cube1 = new THREE.Mesh(cube1Geom, face1Material);
顶侧使用MeshBasicMaterial可以不受光照的影响,并且可以设置透明度,侧面可以形成阴影,显得真实一点。
第三步是实现相机角度的旋转,这个是直接拿的官方文档的案例里面的方法,比较生硬,还有待优化,因为是上下360度旋转,稍微一转地图的底面便翘起来。
<script src="libs/TrackballControls.js"></script>
controls = new THREE.TrackballControls( camera ); controls.rotateSpeed = 1.0; controls.zoomSpeed = 1.2; controls.panSpeed = 0.8; controls.noZoom = false; controls.noPan = false; controls.staticMoving = true; controls.dynamicDampingFactor = 0.3;
function render() {controls.update();}
只需要引入一个官方的js包,就可以实现了。
第四步是考虑了最久的一个,实现用户的交互,在点击cube之后,cube可以变色,用以提示用户,该cube被点击了,看了网上很多demo,都是使用射线原理,将鼠标点击屏幕中的点垂直射一条光线,如果触碰到多个对象,取第一个对象即为当前点击的cube。这个原理是很简单,但是实现将cube变色却把我难住了,后来又找了很多博客,找到一个可以使cube变色的案例,但是每次把代码搬到我这里就失灵了,总是报一个getHex()和set()未定义的错误,我以为是版本的问题,后来想了很久,试了好久,终于发现,原来是我定义材质的问题,我定义的材质是一个数组,六个面需要改色的话,需要分别改色,而案例中cube的六个面的材质颜色完全一样,所以只需要直接获取再修改便可以了。
function onDocumentMouseClick(event) {event.preventDefault();mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; }
raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObjects(scene.children); if (intersects.length > 0) {if (INTERSECTED !== intersects[0].object) {if (INTERSECTED && INTERSECTED instanceof THREE.Mesh && INTERSECTED.material.length === 6) {for(var i = 0; i < 6; i++){INTERSECTED.material[i].color.setHex(INTERSECTED.currentHex);}}INTERSECTED = intersects[0].object;if(INTERSECTED instanceof THREE.Mesh && INTERSECTED.material.length === 6){for(var i = 0; i < 6; i++){INTERSECTED.currentHex = INTERSECTED.material[i].color.getHex();if(INTERSECTED.currentHex !== 16777215)INTERSECTED.material[i].color.set( "#FFC965" );}}} } else {if (INTERSECTED && INTERSECTED instanceof THREE.Mesh && INTERSECTED.material.length === 6) {for(var i = 0; i < 6; i++){INTERSECTED.material[i].color.set(INTERSECTED.currentHex);}}INTERSECTED = null; }
onmouseclick的方法确定鼠标点击的位置,下面部分是render()方法中的一部分。在整体上为了效果的美观,在所有cube的顶上加了一圈直线,这样cube看起来更加明显,但是这就又引起一个小麻烦,在点击物体时要判断是不是mesh,如果点击的直线应该是没有变色效果的,还有点击的是地板时,也不应该有变色效果。
经过一系列的探索,终于在零的基础上有了一些东西,虽然距离成品还很远(还有很多需要优化的地方),但也算threejs学习上的一小步,最后附上代码。
<!DOCTYPE html> <html><head><meta charset="UTF-8"><title>Three.js</title><style>body {margin: 0;overflow: hidden;}</style> </head><body> <script type="text/javascript" src="libs/three.js"></script> <script src="libs/TrackballControls.js"></script> <script>var renderer, scene, camera;var INTERSECTED;var raycaster;var mouse;var controls ;// 边框线的高度 var lineHeight = 1.75;// 块的高度 var cubeHeight = 1.5;function init() {renderer = new THREE.WebGLRenderer({antialias: true });renderer.setClearColor(0xF1F2F7);renderer.setSize(window.innerWidth, window.innerHeight);scene = new THREE.Scene();scene.background = new THREE.Color( 0xF1F2F7 );camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);camera.lookAt(new THREE.Vector3(20, 0, 20));camera.position.set(0, 40, 50);// 光线的照射 var ambiColor = "#f2f2f2";var spotLight = new THREE.SpotLight(ambiColor);spotLight.position.set( -100, 100, -100);scene.add(spotLight);var spotLight2 = new THREE.SpotLight(ambiColor);spotLight2.position.set( 100, 100, 150);scene.add(spotLight2);controls = new THREE.TrackballControls( camera );controls.rotateSpeed = 1.0;controls.zoomSpeed = 1.2;controls.panSpeed = 0.8;controls.noZoom = false;controls.noPan = false;controls.staticMoving = true;controls.dynamicDampingFactor = 0.3;raycaster = new THREE.Raycaster();mouse = new THREE.Vector2();document.body.appendChild(renderer.domElement);document.addEventListener('click', onDocumentMouseClick, false);creatCube();render();}function onDocumentMouseClick(event) {event.preventDefault();mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;}function creatCube() {// 材质定义 var yellow = [];// 右侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"}));// 左侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"}));// 顶侧面 yellow.push(new THREE.MeshBasicMaterial({color: "#F8D3A5",transparent:true,opacity:0.8}));// 地侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"}));// 前侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"}));// 后侧面 yellow.push(new THREE.MeshLambertMaterial({color: "#F8D3A5"}));var white = [];white.push(new THREE.MeshLambertMaterial({color: "#fff"}));white.push(new THREE.MeshLambertMaterial({color: "#fff"}));white.push(new THREE.MeshBasicMaterial({color: "#fff"}));white.push(new THREE.MeshLambertMaterial({color: "#fff"}));white.push(new THREE.MeshLambertMaterial({color: "#fff"}));white.push(new THREE.MeshLambertMaterial({color: "#fff"}));var purple = [];purple.push(new THREE.MeshLambertMaterial({color: "#E3B7F7"}));purple.push(new THREE.MeshLambertMaterial({color: "#E3B7F7"}));purple.push(new THREE.MeshBasicMaterial({color: "#E3B7F7",transparent:true,opacity:0.8}));purple.push(new THREE.MeshLambertMaterial({color: "#E3B7F7"}));purple.push(new THREE.MeshLambertMaterial({color: "#E3B7F7"}));purple.push(new THREE.MeshLambertMaterial({color: "#E3B7F7"}));var orange = [];orange.push(new THREE.MeshLambertMaterial({color: "#FFC965"}));orange.push(new THREE.MeshLambertMaterial({color: "#FFC965"}));orange.push(new THREE.MeshBasicMaterial({color: "#FFC965",transparent:true,opacity:0.5}));orange.push(new THREE.MeshLambertMaterial({color: "#FFC965"}));orange.push(new THREE.MeshLambertMaterial({color: "#FFC965"}));orange.push(new THREE.MeshLambertMaterial({color: "#FFC965"}));// 底部左侧的地板 var cubeBottomLeftGeometry = new THREE.BoxGeometry(16,0.5,42);var cubeBottomLeftMaterial = new THREE.MeshFaceMaterial(white);var cubeBottomLeft = new THREE.Mesh(cubeBottomLeftGeometry,cubeBottomLeftMaterial);cubeBottomLeft.position.x = -10;cubeBottomLeft.position.y = 0;cubeBottomLeft.position.z = 0;scene.add(cubeBottomLeft);// 底部右侧的地板 var cubeBottomRightGeometry = new THREE.BoxGeometry(28,0.5,16);var cubeBottomRightMaterial = new THREE.MeshFaceMaterial(white);var cubeBottomRight = new THREE.Mesh(cubeBottomRightGeometry,cubeBottomRightMaterial);cubeBottomRight.position.x = 12;cubeBottomRight.position.y = 0;cubeBottomRight.position.z = 13;scene.add(cubeBottomRight);// 方块1的下线1 var line11Material = new THREE.LineBasicMaterial({color:"#F7A540"});var line11Geometry = new THREE.Geometry();line11Geometry.vertices.push(new THREE.Vector3(-17.5,lineHeight,20.5));line11Geometry.vertices.push(new THREE.Vector3(-14.5,lineHeight,20.5));var line11 = new THREE.Line(line11Geometry, line11Material);scene.add(line11);// 方块1的左线2 var line12Material = new THREE.LineBasicMaterial({color:"#F7A540"});var line12Geometry = new THREE.Geometry();line12Geometry.vertices.push(new THREE.Vector3(-17.5,lineHeight,20.5));line12Geometry.vertices.push(new THREE.Vector3(-17.5,lineHeight,8.5));var line12 = new THREE.Line(line12Geometry, line12Material);scene.add(line12);// 方块1的右线3 var line13Material = new THREE.LineBasicMaterial({color:"#F7A540"});var line13Geometry = new THREE.Geometry();line13Geometry.vertices.push(new THREE.Vector3(-14.5,lineHeight,8.5));line13Geometry.vertices.push(new THREE.Vector3(-14.5,lineHeight,20.5));var line13 = new THREE.Line(line13Geometry, line13Material);scene.add(line13);// 方块1的上线4 var line14Material = new THREE.LineBasicMaterial({color:"#F7A540"});var line14Geometry = new THREE.Geometry();line14Geometry.vertices.push(new THREE.Vector3(-17.5,lineHeight,8.5));line14Geometry.vertices.push(new THREE.Vector3(-14.5,lineHeight,8.5));var line14 = new THREE.Line(line14Geometry, line14Material);scene.add(line14);// 方块2的下线1 var line21Material = new THREE.LineBasicMaterial({color:"#B42EF7"});var line21Geometry = new THREE.Geometry();line21Geometry.vertices.push(new THREE.Vector3(-9.5,lineHeight,20.5));line21Geometry.vertices.push(new THREE.Vector3(-14.5,lineHeight,20.5));var line21 = new THREE.Line(line21Geometry, line21Material);scene.add(line21);// 方块2的右线3 var line22Material = new THREE.LineBasicMaterial({color:"#B42EF7"});var line22Geometry = new THREE.Geometry();line22Geometry.vertices.push(new THREE.Vector3(-9.5,lineHeight,20.5));line22Geometry.vertices.push(new THREE.Vector3(-9.5,lineHeight,13.5));var line22 = new THREE.Line(line22Geometry, line22Material);scene.add(line22);// 方块2的左线2 与方块1右线重叠 取消 /*var line23Material = new THREE.LineBasicMaterial({color:"#B42EF7"}); var line23Geometry = new THREE.Geometry(); line23Geometry.vertices.push(new THREE.Vector3(-13.7,lineHeight,15.3)); line23Geometry.vertices.push(new THREE.Vector3(-13.7,lineHeight,22)); var line23 = new THREE.Line(line23Geometry, line23Material); scene.add(line23);*/ // 方块2的上线4 var line24Material = new THREE.LineBasicMaterial({color:"#B42EF7"});var line24Geometry = new THREE.Geometry();line24Geometry.vertices.push(new THREE.Vector3(-9.5,lineHeight,13.5));line24Geometry.vertices.push(new THREE.Vector3(-14.5,lineHeight,13.5));var line24 = new THREE.Line(line24Geometry, line24Material);scene.add(line24);// 方块1 var face1Material = new THREE.MeshFaceMaterial(yellow);var cube1Geom = new THREE.BoxGeometry(3,cubeHeight,12);var cube1 = new THREE.Mesh(cube1Geom, face1Material);cube1.position.x = -16;cube1.position.y = 1;cube1.position.z = 14.5;scene.add(cube1);// 方块2 var face2Material = new THREE.MeshFaceMaterial(purple);var cube2Geom = new THREE.BoxGeometry(5,cubeHeight,7);var cube2 = new THREE.Mesh(cube2Geom, face2Material);cube2.position.x = -12;cube2.position.y = 1.;cube2.position.z = 17;scene.add(cube2);}function render() {controls.update();requestAnimationFrame(render);renderer.render(scene, camera);raycaster.setFromCamera(mouse, camera);var intersects = raycaster.intersectObjects(scene.children);if (intersects.length > 0) {if (INTERSECTED !== intersects[0].object) {// 判断对象是否是mesh 并且是有六个面材质的 if (INTERSECTED && INTERSECTED instanceof THREE.Mesh && INTERSECTED.material.length === 6) {for(var i = 0; i < 6; i++){INTERSECTED.material[i].color.setHex(INTERSECTED.currentHex);}}INTERSECTED = intersects[0].object;if(INTERSECTED instanceof THREE.Mesh && INTERSECTED.material.length === 6){for(var i = 0; i < 6; i++){INTERSECTED.currentHex = INTERSECTED.material[i].color.getHex();// 如果是底面 因为底面的颜色获取后打印结果为:16777215 if(INTERSECTED.currentHex !== 16777215)INTERSECTED.material[i].color.set( "#FFC965" );}}}} else {if (INTERSECTED && INTERSECTED instanceof THREE.Mesh && INTERSECTED.material.length === 6) {for(var i = 0; i < 6; i++){INTERSECTED.material[i].color.set(INTERSECTED.currentHex);}}INTERSECTED = null;}}init(); </script> </body></html>
原创作品,欢迎转载,备注出处。
ThreeJS逐步实现室内概念图的效果(渲染,交互)相关推荐
- 商汤提出手机端实时单目三维重建系统,实现逼真AR效果和交互
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 来源:商汤泰坦公开课 摘要 · 看点 商汤研究院和浙江大学 CAD&CG 国家重点实验室合作研 ...
- [html] 写一个类似刮刮卡效果的交互,即鼠标划过时显示号码
[html] 写一个类似刮刮卡效果的交互,即鼠标划过时显示号码 <title>Document</title> <style> *{ margin:0; paddi ...
- H5实例教学--ThreeJs 实现粒子动画飘花效果
粒子动画在ThreeJs可以用几种方式实现 本次样例使用Sprite类来构建粒子 官方对Sprite类的解释 Sprite A sprite is a plane that always faces ...
- threejs加载C4D模型及材质渲染实例
最近在学习Threejs3D引擎使用,主要是为了实现web里面去实现3D模型的加载渲染,这样会比较直观的看到类似的效果,增加用户体验. 第一步:加载C4D模型.将模型和材质导出 二.加载主要插件模块 ...
- html 页面飘花,HTML5开发实例-ThreeJs实现粒子动画飘花效果代码分享
粒子动画在ThreeJs可以用几种方式实现 本次样例使用Sprite类来构建粒子 大概意思:这个类创建的对象是一个始终面向相机的平面,可以把贴图应用在上面,Sprite对象无法添加阴影 ,所以cast ...
- threejs学习笔记:CSS2DObject 2d文字渲染
import {CSS2DRenderer,CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer.js";/ ...
- Threejs贴图为了更好的渲染(门)
渲染结果图 门模型使用标准网格材质(MeshStandardMaterial),一种基于物理的渲染.使用PBR方式渲染. 思路: (1) 建立立方体模 (2)设置基本颜色.map属性添加颜色贴图 co ...
- 小白IT:炫彩的网页是怎么做的,什么是前端???Python前端基础CSS 效果渲染
文章目录 一 认识HTML 1.web服务的本质 2.HTML是什么? 3.html文档格式 4.html标签格式 标签的语法 几个重要的属性 HTML注释 二.常用标签 1.!DOCTYPE标签 2 ...
- 高效真实的云效果渲染算法
Realistic and Fast Cloud Rendering NinianeWang MicrosoftCorporation(nowatGoogleInc.) niniane@ofb.net ...
最新文章
- 如何使用深度学习训练聊天机器人
- Linux高级文本处理之gawk语法和基础命令(一)
- Calibre-免费开源的“一站式”的电子书管理阅读格式转换软件
- ssl1203-书的复制【dp】
- 学前儿童语言教育模拟试卷c卷,学前儿童语言教育模拟试卷参考答案.doc
- 游戏筑基之游戏菜单制作(C语言)
- FirstApp,iphone开发学习总结3,UIButton简单的操作
- 判断在ios系统中打开微信浏览器
- 航拍地形图转换成地形图_无人机航测生成地形图技术流程(Pix4D+ArcGIS+CASS)...
- 海豚蓝牙ASIO驱动程序使用说明
- 关于django的prefetch_related优化查询问题
- 混淆矩阵 Confusion Matrix
- c语言--余数正负判断,printf函数占位符
- matplotlib绘制三维图
- 2023年新自采集壁纸网页源码+简约大气
- Ubuntu16.04+ROS kinetic +Basler_camera环境配置以及相机标定
- 大数据之当传统产业遭遇互联网
- php下对中国内地身份证进行验证
- java 多线程 map_多线程Map并发读后修改
- ie visio 打开_Visio viewer 不能从IE打开vsd文件(转) | 学步园
热门文章
- DIV布局——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品
- oracle11如何生成aw r,(Oracle)自定义调用AWRamp;ADDM
- 企业安全丨旧瓶新酒之ngx_lua fail2ban实现主动诱捕
- 财务报表分析:理论框架方法与案例
- 非侵入式负荷matlab程序,非侵入式负荷分解之BLUED数据集
- python英文字符串单词个数统计_Python实现统计英文单词个数及字符串分割代码
- 【好物推荐】LICEcap – 灵活好用,GIF 屏幕录制工具
- Android6.0 Sensor架构和问题分析
- python主要是干什么用的,python到底是干什么的
- 等保2.0 信息安全及等保标准体系概述