web 3d场景构建+three.js+室内围墙,仓库,楼梯,货架模型等,第一人称进入场景案例


翻到了之前的一个案例,基于three.js做的仓库布局模拟,地图元素除了大模型外,其他都是通过JSON数据解析动态生成的,例如墙体,柱子门口,地标等,集成了第一人称的插件可以第一人称进入场景有需要的可以下载看看,对想入门的朋友应该有一些参考价值。
/**
*创建自定义几何体
*输入参数几何体底面逆时针坐标组、几何体高度
* 目前只支持凸多边形 逆时针则连线,顺时针不连线
*/
function createCustomBufferGeometry(planeArr, height, color) {
let planes = planeArr;
let planes2 = [];
//组装顶面坐标
for (let i = 0; i < planes.length; i++) {
planes2.push(new THREE.Vector3(planes[i].x, planes[i].y + height, planes[i].z));
}
planes = planes.concat(planes2);
let arr = [];
//循环组成三角面
for (let i = 0; i < planes.length; i++) {
let j = i + 1, k2 = j + planes2.length;
let xLength = planes2.length;
if (j >= planes2.length && i < planes2.length) {
j = 0; k2 = j + planes2.length;
}
if (i >= planes2.length) {
if (j >= planes.length) {
j = planes2.length;
}
k2 = i - planes2.length;
xLength = planes.length;
}
for (let x = i + 2; x < xLength; x++) {
arr = arr.concat([planes[i].x, planes[i].y, planes[i].z]);
arr = arr.concat([planes[j].x, planes[j].y, planes[j].z]);
arr = arr.concat([planes[x].x, planes[x].y, planes[x].z]);
// if (((planes[j].x - planes[i].x) * (planes[x].z - planes[i].z) - (planes[x].x - planes[i].x) * ( planes[j].z - planes[i].z)) < 0) {
// arr = arr.concat([planes[i].x, planes[i].y, planes[i].z]);
// arr = arr.concat([planes[j].x, planes[j].y, planes[j].z]);
// arr = arr.concat([planes[x].x, planes[x].y, planes[x].z]);
// }
}
arr = arr.concat([planes[i].x, planes[i].y, planes[i].z]);
if (i < planes2.length) {
arr = arr.concat([planes[j].x, planes[j].y, planes[j].z]);
arr = arr.concat([planes[k2].x, planes[k2].y, planes[k2].z]);
} else {
arr = arr.concat([planes[k2].x, planes[k2].y, planes[k2].z]);
arr = arr.concat([planes[j].x, planes[j].y, planes[j].z]);
}
}
let bufferGeometry = new THREE.BufferGeometry();
let vertices = new Float32Array(arr);
// itemSize = 3 因为每个顶点都是一个三元组。
bufferGeometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
bufferGeometry.computeFaceNormals();//计算法向量,会对光照产生影响
bufferGeometry.computeVertexNormals();//自动设置法向量
let material = new THREE.MeshLambertMaterial({ color: color });
let mesh = new THREE.Mesh(bufferGeometry, material);
_bufferGeometry = bufferGeometry;
//worldScene.add(mesh);
return mesh;
}
var _bufferGeometry;
/**
*地图数据坐标是左上角为原点开始的二维坐标系x,y,绘制以左上角开始
* web 3d坐标原点是屏幕中心点,绘制的时候也是以中心为相对位置
* MapXLength:地图最长距离,MapZLength 地图最宽距离
* 转换规则 3d.position.x = 2d.width/2 - maxWidth/2 +2d.position.x
* 3d.position.z = 2d.height/2 - maxHeight/2 +2d.position.z;
* 3d.position.y y轴高度 2D地图无需设置,默认为0,如果有高度, 3d.position.y = 2d.高度.y/2+2d.高度位置+MapYLength/2
*/
function handCoordinate(data) {
data.x = data.x / 10;
data.y = data.y / 10;
data.z = data.z / 10;
if (data.positionY)
data.positionY = data.positionY / 10;
else
data.positionY = 0;
data.width = data.width / 10;
data.height = data.height / 10;
data.x = data.width / 2 - MapXLength / 2 + data.x;
data.z = data.height / 2 - MapZLength / 2 + data.z;
if (data.groupOption) {
data.groupOption.offSetX = data.groupOption.offSetX / 10;
data.groupOption.offSetY = data.groupOption.offSetY / 10;
data.groupOption.offSetZ = data.groupOption.offSetZ / 10;
}
}
var _baseBox;
var _floorType;
//最底层的box
function createBaseBox(data,floorType) {
data.width = data.width / 10;
data.height = data.height / 10;
MapXLength = data.width;
MapZLength = data.height;
sizeRatio = MapXLength / MapXLength;
if(data.floorType){
_floorType = data.floorType;
if(!floorModels[floorType])
{
floorModels[floorType] = [];
}
}
var geometry = new THREE.BoxBufferGeometry(data.width, 1, data.height);
var material = new THREE.MeshLambertMaterial({ color: data.color });
var cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 0, 0);
//cube.castShadow = true;//开启投影
cube.receiveShadow = true;//接收阴影
cube.geometry.computeBoundingBox();
_baseBox = cube.geometry.boundingBox;
clickObjects.push(cube);//加入点击对象组
worldScene.add(cube);
floorModels[floorType].push(cube);
//console.log(cube);
//地图标注
// worldScene.add(createTextTextureBySprite(data))
}
//创建几何体
function createBox(data,_floorType) {
handCoordinate(data);
var geometry = new THREE.BoxGeometry(data.width, data.y, data.height);
var material = new THREE.MeshLambertMaterial({ color: data.color, vertexColors: THREE.FaceColors });
var cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;//开启投影
//cube.receiveShadow = true;//接收阴影
cube.position.set(data.x, 0.55 + data.positionY / 2 + data.y / 2, data.z);
var newMesh;
//几何体组合处理
if (data.bspMesh) {
newMesh = cube;
data.bspMesh.forEach(x => {
handCoordinate(x);
let tempMesh;
if (x.geometryType == 'box') {
tempGeometry = new THREE.BoxGeometry(x.width, x.y, x.height);
tempMesh = new THREE.Mesh(tempGeometry, new THREE.MeshLambertMaterial({ color: x.color }));
}
tempMesh.position.set(x.x, 0.55 + x.positionY / 2 + x.y / 2, x.z);
newMesh = bspMesh(x.type, newMesh, tempMesh);
})
} else {
}
let finalMesh;
if (newMesh) {
newMesh.castShadow = true;//开启投影
// worldScene.add(newMesh);
finalMesh = newMesh;
} else {
finalMesh = cube;
// worldScene.add(cube);
}
//多个相同模型组合
if (data.type && data.type == 'group') {
for (let i = 0; i < data.groupOption.total; i++) {
let tempMesh = finalMesh.clone();
if (data.groupOption.offSetX != 0) {
tempMesh.position.x = finalMesh.position.x + (data.width + data.groupOption.offSetX) * i;
}
if (data.groupOption.offSetY != 0) {
tempMesh.position.y = finalMesh.position.y + (data.y + data.groupOption.offSety) * i;
}
if (data.groupOption.offSetZ != 0) {
tempMesh.position.z = finalMesh.position.z + (data.height + data.groupOption.offSetZ) * i;
}
// tempMesh.position.set(
// ( data.width+tempMesh.position.x+data.groupOption.offSetX)*i,
// ( data.y+tempMesh.position.y+data.groupOption.offSetY)*i,
// (data.height+tempMesh.position.z+data.groupOption.offSetZ)*i);
worldScene.add(tempMesh);
floorModels[_floorType].push(tempMesh);
}
} else {
worldScene.add(finalMesh);
floorModels[_floorType].push(finalMesh);
}
//地图标注
let sprite = createTextureBySprite(data);
if (sprite != null)
worldScene.add(sprite);
}
//创建圆柱体
function createCylinder(data) {
handCoordinate(data);
var geometry = new THREE.CylinderGeometry(data.width / 2, data.width / 2, data.y, 32);
var material = new THREE.MeshLambertMaterial({ color: data.color, vertexColors: THREE.FaceColors });
var cylinder = new THREE.Mesh(geometry, material);
cylinder.position.set(data.x, 0.55 + data.positionY / 2 + data.y / 2, data.z);
for (let i = 0; i < 64; i++) {
geometry.faces[i].color = new THREE.Color('#004892');
}
worldScene.add(cylinder);
}
/**
* 创建网格
* @param {几何体对象} geometry
*/
function createMesh(geometry, color) {
if (!color) {
color = '#4685C6';
}
return new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: color }));
}
/**
* A,B模型type:'intersect' 交集 'union' 并集 subtract 差集
* @param {A模型} geometryA
* @param {B模型} geometryB
*/
function bspGeometry(type, geometryA, geometryB) {
//生成ThreeBSP对象
var a = new ThreeBSP(geometryA);
var b = new ThreeBSP(geometryB);
//进行算
var resultBSP;
if (type == 'intersect')
resultBSP = a.intersect(b);
else if (type == 'union')
resultBSP = a.union(b);
else
resultBSP = a.subtract(b);
//从BSP对象内获取到处理完后的mesh模型数据
var result = resultBSP.toGeometry();
//更新模型的面和顶点的数据
result.computeFaceNormals();
result.computeVertexNormals();
return cresult;
}
/**
* A,B模型type:'intersect' 交集 'union' 并集 subtract 差集
* @param {A模型} meshA
* @param {B模型} meshB
*/
function bspMesh(type, meshA, meshB) {
//生成ThreeBSP对象
var a = new ThreeBSP(meshA);
var b = new ThreeBSP(meshB);
//进行算
var resultBSP;
if (type == 'intersect')
resultBSP = a.intersect(b);
else if (type == 'union')
resultBSP = a.union(b);
else
resultBSP = a.subtract(b);
//从BSP对象内获取到处理完后的mesh模型数据
var result = resultBSP.toMesh();
result.material = meshA.material;
//更新模型的面和顶点的数据
result.geometry.computeFaceNormals();
result.geometry.computeVertexNormals();
testResult = result
return result;
}
var testResult;
/**
*创建地图标注
*canvas地图标注的内容很小需要放大,放大会失真,后期调整其缩放大小,或者不采用canvas渲染
*/
function createTextureBySprite(data) {
if ((data.title == '' && data.imageurl == '') || (!data.title && !data.imageurl)) {
return null;
}
let canvas = document.createElement('canvas');
canvas.width=3000;
canvas.height=2000;
let ctx = canvas.getContext('2d');
ctx.lineWidth = 1;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.textAlign = 'center';
if (data.font) {
ctx.font = data.font;
} else {
ctx.font = "Normal 180px Arial"
}
if (data.textcolor) {
ctx.fillStyle = data.textcolor;
}
//ctx.lineWidth = 4;
if (data.imageurl) {
let img = new Image();
img.src = data.imageurl;
img.onload = function () {
ctx.drawImage(img, 30, 90);
texture.needsUpdate = true;
}
}
/*
把整个 canvas 作为纹理,所以字尽量大一些,撑满整个 canvas 画布。
但也要小心文字溢出画布。
*/
ctx.fillText(data.title, 400, 200);
let texture = new THREE.CanvasTexture(canvas);
let material = new THREE.SpriteMaterial({
map: texture,
transparent: true, // 避免遮挡其他图形
// sizeAttenuation:false
});
let textMesh = new THREE.Sprite(material);
/*
精灵很小,要放大
*/
textMesh.scale.set(10, 10, 10);
/*
WebGL 3D 世界中的位置
*/
textMesh.position.set(data.x,data.y + 1.5, data.z);//data.y + 3
return textMesh;
}
相关文章:
web 3d场景构建+three.js+室内围墙,仓库,楼梯,货架模型等,第一人称进入场景案例
翻到了之前的一个案例,基于three.js做的仓库布局模拟,地图元素除了大模型外,其他都是通过JSON数据解析动态生成的,例如墙体,柱子门口,地标等,集成了第一人称的插件可以第一人称进入场景有需要的…...
EditPlus取消自动.bak备份
Tools->Preferences->File 将√取消...
LLM - Transformer LLaMA2 结构分析与 LoRA 详解
目录 一.引言 二.图说 LLM 1.Transformer 结构 ◆ Input、Output Embedding ◆ PositionEmbedding ◆ Multi-Head-Attention ◆ ADD & Norm ◆ Feed Forward ◆ Linear & Softmax 2.不同 LLM 结构 ◆ Encoder-Only ◆ Encoder-Decoder ◆ Decoder-Only …...
前端技术搭建五子棋游戏(内含源码)
The sand accumulates to form a pagoda ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分 ✨ 写在前面 上周我们实通过前端基础实现了拼图游戏,今天还是继续按照我们原定的节奏来带领大家完成一个五子棋游戏,功能也比较简单简单,也…...
AST入门与实战(三):if节点转switch节点(瑞数5)
原文地址:https://zhuoyue360.com/jsnx/110.html 1. 期望 这是一个瑞数5代解混淆的案例,我们本章节需要做的是把if节点的内容转换成switch-case内容.以此来熟悉AST对JS混淆的对抗. 原始代码: function whileState() {while (1) {aV cA[wU];if (aV < 4) {if (…...
小白到运维工程师自学之路 第七十一集 (kubernetes网络设置)
一、概述 Master 节点NotReady 的原因就是因为没有使用任何的网络插件,此时Node 和Master的连接还不正常。目前最流行的Kubernetes 网络插件有Flannel、Calico、Canal、Weave 这里选择使用flannel。 二、安装flannel 1、master下载kube-flannel.yml,所…...
day17 enum abstract interface 枚举 抽象 接口
一、枚举 enum 枚举本来的面目 创建Season类, 所有类都默认继承Object,写不写都一样 声明属性 :季节的名字、 季节的描述, 因为枚举的对象是看的见的客观事物, 想让它的属性不可修改 使用 final修饰表示最终的 &am…...
c刷题(二)
目录 加减混合运算 计算n的k次方 计算非负整数各位之和 字符串逆序 双指针 递归 矩阵计算 矩阵转置 加减混合运算 题目:计算1 / 1 - 1 / 2 1 / 3 - 1 / 4 1 / 5 …… 1 / 99 - 1 / 100 的值,打印出结果。 一般情况我们可以写个循环然后在用条…...
【leetcode】15. 三数之和(medium)
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。 这题真…...
【css】属性选择器
有些场景中需要在相同元素中获取具有特定属性的元素,比如同为input,type属性有text、button,可以通过属性选择器设置text和button的不同样式。 代码: <style> input[typetext] {width: 150px;display: block;margin-bottom…...
Redis_概述
1.redis概述 1.1 简介 截止到2021年12月 数据库排名https://db-engines.com/en/ranking redis(Remote Dictionary Server) 一个开源的key-value存储系统它支持存储的Value类型:包括String(字符串),list(链表),set(集合),zset(sorted set 有序集合),hash(哈希类型…...
【从零学习python 】16. Python字符串的format方法(一)
文章目录 字符串的format方法1. 概念:2. 字段名2.1 简单字段名2.1.1 省略字段名2.1.2 数字字段名2.1.3 变量字段名2.1.4 简单字段名的混合使用2.1.5 使用元组和字典传参 进阶案例 字符串的format方法 1. 概念: str.format() 方法通过字符串中的大括号{}来识别替换字段 replac…...
python re 模块 正则表达式
一、正则表达式基本符号 ^ 表示匹配字符串的开始位置 (例外 用在中括号中[ ] 时,可以理解为取反,表示不匹配括号中字符串)$ 表示匹配字符串的结束位置* 表示匹配 零次到多次(记忆方法:符号是星星,天上的星星可以是无数个也可以看不到&#x…...
c#设计模式-创建型模式 之 单例模式
目录 前言: 优点: 缺点: 饿汉式(静态变量方式) 懒汉式(线程不安全) 懒汉式(双重检查锁定) 推荐方式Lazy 总结: 前言: 这种模式涉及到一个单一的类&a…...
K-01BFS(2023河南萌新联赛第(五)场:郑州轻工业大学)
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 思路: 直接枚举这个图中的拐点 这个拐点是经过左右平移到上下平移或者上下平移到左右平移 假设这个点事左到右后然后再从下到上 左到右就相当于走了个最长上升子序列࿰…...
CSP复习每日一题(四)
树的重心 给定一颗树,树中包含 n n n 个结点(编号 1 ∼ n 1∼n 1∼n)和 n − 1 n−1 n−1条无向边。请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。 重心定义: 重心是指树中的一…...
dubbo之整合SpringBoot
目录 zookeeper安装 1.拉取ZooKeeper镜像 2.新建文件夹 3.挂载本地文件夹并启动服务 4.查看容器 5.进入容器(zookeeper) Dubbo Admin安装 1.下载dubbo-admin 2.zip包解压 3.修改配置文件 4.打包项目 5.启动jar 6.访问 构建项目 api模块 1.创建…...
UE 5 GAS 在项目中处理AttributeSet相关
这一篇文章是个人的实战经验记录,如果对基础性的内容不了解的,可以看我前面一篇文章对基础的概念以及内容的讲解。 设置AttributeSet 使用GAS之前,首先需要设置参数集AS,这个是用于同步的一些参数,至于如何设置GAS&a…...
JDBC数据库连接
目录 引言 一,基本概念 二,常用操作步骤 三,连接操作 引言 JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种 关系数据库提供统一访问,它由一组用Java语言编写的类和接口…...
gitee分支合并
合并dev分支到master(合并到主分支) git checkout master git merge dev //这里的dev表示你的分支名称 git push //推送到远程仓库 效果如下图 不报错就表示推送成功了,希望能帮助各位小伙伴...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
