ThreeJs制作模型图片

        这个标题名字可能有歧义,只是不知道如何更好的表达,总之就是将图片的像素转换成3D场景的模型,并设置这个模型的颜色,放到像素点对应的位置从而拼接成一个图片,起因是上文中用js分解了音乐,实现了模型跳动效果,既然音频可以分解,那图片应该也可以,所以就有个这篇博客。

        大概得实现逻辑是这样的,先找一个图片,像素要小,越小越好,要有花纹,然后用canvas将图片的每个像素拆解出来,拆解后可以获得这个图片每个像素的位置,颜色,用集合保存每个像素的信息,在3D场景中循环,有了位置和颜色后,在循环中创建一个个正方体,将正方体的位置设置为像素的位置,y轴方向为1,创建贴图,并将贴图的颜色改为像素点的颜色,全部循环后就得到一副用正方体拼接出来的图片了。但是如果你的图片分辨率高,那么拆解出来的像素点很多,就需要筛选掉一些,否则浏览器会卡死,所以强调用分辨率低的图片。

这里先找一副图片:

        下面开始代码:

首先创建场景,相机,灯光,渲染器等:

 initScene(){scene = new THREE.Scene();},initCamera(){this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);this.camera.position.set(200,400,200);},initLight(){//添加两个平行光const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);directionalLight1.position.set(300,300,600)scene.add(directionalLight1);const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.5);directionalLight2.position.set(600,200,600)scene.add(directionalLight2);},
initRenderer(){this.renderer = new THREE.WebGLRenderer({ antialias: true });this.container = document.getElementById("container")this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);this.renderer.setClearColor('#AAAAAA', 1.0);this.container.appendChild(this.renderer.domElement);},initControl(){this.controls = new OrbitControls(this.camera, this.renderer.domElement);this.controls.enableDamping = true;this.controls.maxPolarAngle = Math.PI / 2.2;      // // 最大角度},initAnimate() {requestAnimationFrame(this.initAnimate);this.renderer.render(scene, this.camera);},

然后封装一个用canvas分解图片的方法

getImagePixels(image) {return new Promise((resolve) => {const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');canvas.width = image.width;canvas.height = image.height;ctx.drawImage(image, 0, 0);const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);const data = imageData.data;const pixels = [];for (let i = 0; i < data.length; i += 4) {const x = (i / 4) % canvas.width;const y = Math.floor((i / 4) / canvas.width);const r = data[i];const g = data[i + 1];const b = data[i + 2];const a = data[i + 3];pixels.push({ x, y, r, g, b, a });}resolve(pixels); // 返回所有像素的数据数组});},

然后调用这个方法获取到像素点集合信息,再循环这个集合,这里为了不卡顿,选择每40个像素点才生成一个模型,也就是下面的i%40===0的判断,

initBox(){const img = new Image();img.src = '/static/images/image.jpg';let geometry = new THREE.BoxGeometry(1, 1, 1);img.onload = async () => {let boxModel = []try {const allPixels = await this.getImagePixels(img);for (let i = 0; i < allPixels.length; i++) {if(i%40 === 0) {let r = allPixels[i].r;let g = allPixels[i].g;let b = allPixels[i].b;let x = allPixels[i].x;let y = allPixels[i].y;let cubeMaterial = new THREE.MeshPhysicalMaterial({color: 'rgb(' + r + ', ' + g + ', ' + b + ')'});this.boxMaterial.push(cubeMaterial)let mesh = new THREE.Mesh(geometry.clone(), cubeMaterial);mesh.position.set(x, 1, y);mesh.updateMatrix() // 更新投影矩阵,不更新各mesh位置会不正确boxModel.push(mesh.geometry.applyMatrix4(mesh.matrix));}}const boxGeometry = mergeGeometries(boxModel,true)let result = new THREE.Mesh(boxGeometry, this.boxMaterial)scene.add(result);console.log("執行完畢")} catch (error) {console.error('Error getting image pixels:', error);}};},

最终得到一副比较虚幻的图片

因为每个模型之间距离比较远,所以图片比较阴暗和虚幻,为了提高图片效果,可以将模型的宽和高改为5,

let geometry = new THREE.BoxGeometry(5, 5, 5);

这样就真实点了,可以根据电脑性能来调整去选取的像素点个数,如果电脑足够好,也可以根据上一篇音乐的效果,给这个图片添加音乐效果的跳动。

完整代码如下:

<template><div style="width:100px;height:100px;"><div id="container"></div></div>
</template><script>
import * as THREE from 'three'
import {OrbitControls} from "three/addons/controls/OrbitControls";
import {mergeGeometries} from "three/addons/utils/BufferGeometryUtils";let scene;
export default {name: "agv-single",data() {return{camera:null,cameraCurve:null,renderer:null,container:null,controls:null,imageData:[],boxMaterial:[],}},methods:{initScene(){scene = new THREE.Scene();},initCamera(){this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);this.camera.position.set(200,400,200);},initLight(){//添加两个平行光const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);directionalLight1.position.set(300,300,600)scene.add(directionalLight1);const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.5);directionalLight2.position.set(600,200,600)scene.add(directionalLight2);},initBox(){const img = new Image();img.src = '/static/images/image.jpg';let geometry = new THREE.BoxGeometry(5, 5, 5);img.onload = async () => {let boxModel = []try {const allPixels = await this.getImagePixels(img);for (let i = 0; i < allPixels.length; i++) {if(i%40 === 0) {let r = allPixels[i].r;let g = allPixels[i].g;let b = allPixels[i].b;let x = allPixels[i].x;let y = allPixels[i].y;let cubeMaterial = new THREE.MeshPhysicalMaterial({color: 'rgb(' + r + ', ' + g + ', ' + b + ')'});this.boxMaterial.push(cubeMaterial)let mesh = new THREE.Mesh(geometry.clone(), cubeMaterial);mesh.position.set(x, 1, y);mesh.updateMatrix() // 更新投影矩阵,不更新各mesh位置会不正确boxModel.push(mesh.geometry.applyMatrix4(mesh.matrix));}}const boxGeometry = mergeGeometries(boxModel,true)let result = new THREE.Mesh(boxGeometry, this.boxMaterial)scene.add(result);console.log("執行完畢")} catch (error) {console.error('Error getting image pixels:', error);}};},getImagePixels(image) {return new Promise((resolve) => {const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');canvas.width = image.width;canvas.height = image.height;ctx.drawImage(image, 0, 0);const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);const data = imageData.data;const pixels = [];for (let i = 0; i < data.length; i += 4) {const x = (i / 4) % canvas.width;const y = Math.floor((i / 4) / canvas.width);const r = data[i];const g = data[i + 1];const b = data[i + 2];const a = data[i + 3];pixels.push({ x, y, r, g, b, a });}resolve(pixels); // 返回所有像素的数据数组});},initRenderer(){this.renderer = new THREE.WebGLRenderer({ antialias: true });this.container = document.getElementById("container")this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);this.renderer.setClearColor('#AAAAAA', 1.0);this.container.appendChild(this.renderer.domElement);},initControl(){this.controls = new OrbitControls(this.camera, this.renderer.domElement);this.controls.enableDamping = true;this.controls.maxPolarAngle = Math.PI / 2.2;      // // 最大角度},initAnimate() {requestAnimationFrame(this.initAnimate);this.renderer.render(scene, this.camera);},initPage(){this.initScene();this.initCamera();this.initLight();this.initBox();this.initRenderer();this.initControl();this.initAnimate();}},mounted() {this.initPage()}
}
</script><style scoped>
#container{position: absolute;width:100%;height:100%;overflow: hidden;
}</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/741859.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

互联网行业的应届大学生,如何制作高水平简历?

雇主通常只会花大约25秒的时间浏览一份简历,因此,拥有一份出色的简历对于找到理想工作至关重要。如果您的简历没有令人印象深刻的特点,那么如何才能在竞争激烈的求职市场中脱颖而出呢? 如果您不知道如何在简历上有效地展示自己,或者觉得简历无论怎么修改都不够突出,那么请…

安装、配置MySQL

安装相关软件 MySQL Server、MySQL Workbench MySQL Server&#xff1a;专门用来提供数据存储和服务的软件 MySQL Workbench&#xff1a;可视化的 MySQL 管理工具 官网安装 https://www.mysql.com/ 官网 MySQL :: Download MySQL Installer 安装包路径 在这里选择版本和和对应…

多图如何导入多个二维码?图片批量导出二维码的技巧

多个图片分别生成对应的二维码怎么做&#xff1f;现在扫码看图片是一种很常用的内容预览方式&#xff0c;有些产品包装也会采用这种方式来展示对应的信息&#xff0c;怎么简单快速的将图片生成二维码&#xff0c;相信有很多的小伙伴都非常的感兴趣。那么小编通过下面这篇文章来…

探索C++中的动态数组:实现自己的Vector容器

&#x1f389;个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名乐于分享在学习道路上收获的大二在校生 &#x1f648;个人主页&#x1f389;&#xff1a;GOTXX &#x1f43c;个人WeChat&#xff1a;ILXOXVJE &#x1f43c;本文由GOTXX原创&#xff0c;首发CSDN&…

echarts line绘制机组开关状态折线图

2024.3.12今天我学习了如何用echarts line实现设备开关状态的效果。 代码如下&#xff1a; option {xAxis: {type: time,},yAxis: {type: value,splitNumber:2,// axisLine: {// show: true,// lineStyle: {// color:#808080// }// },axisLabel:{formatt…

管控员工上网行为(让老板管理更省心更高效)

很多老板都有这样一种顾虑&#xff1a; 员工到底有没有认真工作&#xff0c;工作是不是做到了全身心投入。 为什么会有担心员工工作状态的问题发生&#xff1f; 无疑是员工在上班时间浏览与工作无关的网站、下载私人文件、甚至是泄露公司机密等行为&#xff0c;让老板不放心了…

Elasticsearch:在本地使用 Gemma LLM 对私人数据进行问答

在本笔记本中&#xff0c;我们的目标是利用 Google 的 Gemma 模型开发 RAG 系统。 我们将使用 Elastic 的 ELSER 模型生成向量并将其存储在 Elasticsearch 中。 此外&#xff0c;我们将探索语义检索技术&#xff0c;并将最热门的搜索结果作为 Gemma 模型的上下文窗口呈现。 此外…

岭回归:优化预测的利器

在数据科学和机器学习的领域&#xff0c;构建准确、稳定的预测模型是一项至关重要的任务。岭回归作为一种强大的工具&#xff0c;被设计用来应对数据集中存在多重共线性的问题&#xff0c;并通过引入正则化来缩小预测误差。 1. 岭回归的原理&#xff1a; 岭回归是线性回归的一…

【国产】API接口管理平台的产品设计与搭建讲解

【国产接口管理平台】PhalApi Pro (π框架专业版) PhalApi Pro (发音&#xff1a;π框架专业版)&#xff0c;是一款国产企业级API接口管理平台&#xff0c;可以零代码、快速搭建API接口开发平台、接口开放平台、接口管理平台。基于PhalApi开源接口开发框架&#xff0c;通过低代…

【gpt实践】李某的AI课程值199吗

先说个人的答案&#xff1a;不值。但也不是说毫无价值&#xff0c;只是他的价值没那么高。 文末分享该课程&#xff0c;大家有兴趣可以看看&#xff0c;该课程是否有价值。 “清华博士”推出的199元的AI课程销售额竟然突破了5000万。这一数字让人惊叹&#xff0c;也引发了人们…

亚马逊、速卖通卖家如何做好测评让店铺销量稳定增长?

近期有刚入驻跨境电商的新买家咨询珑哥&#xff0c;店铺上的产品有人浏览&#xff0c;就是没有人下单&#xff0c;新店铺很正常因为很多人去购买东西&#xff0c;首先看的是评价&#xff0c;没有评价一般人不敢直接去下单。就像我们去淘宝买东西&#xff0c;都要看下评价&#…

测试用例的设计(2)

目录 1.前言 2.正交排列(正交表) 2.1什么是正交表 2.2正交表的例子 2.3正交表的两个重要性质 3.如何构造一个正交表 3.1下载工具 3.1构造前提 4.场景设计法 5.错误猜测法 1.前言 我们在前面的文章里讲了测试用例的几种设计方法,分别是等价类发,把测试例子划分成不同的类…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Web)上篇

提供具有网页显示能力的Web组件&#xff0c;ohos.web.webview提供web控制能力。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。示例效果请以真机运行为准&#xff0c;当前IDE预览器不支持。 需要权…

Java项目:基于springboot实现的OA协同办公系统(源码+数据库+毕业论文)

一、项目简介 本项目是一套基于Springbootvue实现的付费自习室系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、…

从零开始利用MATLAB进行FPGA设计(一):建立脉冲检测模型的Simulink模型2

目录 1.模块的总体结构 1.1从工作空间导入输入信号 1.2FIR滤波器 2.Subsystem 3.MATLAB Function 文章灵感来源于MATLAB官方免费教程&#xff1a;HDL Coder Self-Guided Tutorial 考虑到MATLAB官网的英文看着慢&#xff0c;再加上视频讲解老印浓浓的咖喱味&#xff0c;我…

【CSP】2021-12-2 序列查询新解 分段处理 用乘法代替加法减少时间复杂度(思想是离散化)

2021-12-2 序列查询新解 分段处理 用乘法代替加法减少时间复杂度&#xff08;思想是离散化&#xff09;2021-12-2 序列查询新解 分段处理 用乘法代替加法减少时间复杂度&#xff08;思想是离散化&#xff09; 2021-12-2 序列查询新解 分段处理 用乘法代替加法减少时间复杂度&am…

echarts绘制柱状图

<template><div><div>【大区数据信息】</div><div ref"target" class"w-full h-full" ></div></div> </template><script setup> import { ref, onMounted, watch} from "vue"; import *…

RC522刷卡电路设计及程序

一、RC522刷卡电路组成 基于RC522的刷卡电路如上图所示。该电路组成主要分为三部分&#xff1a; Receiving Circuit&#xff1a;接收电路&#xff0c;接收卡发送的数据。 Filtering Impedence-Transtorm circuit:滤波和阻抗变换电路&#xff0c;抑制高次谐波并优化到读卡器天线…

手把手写深度学习(23):视频扩散模型之Video DataLoader

手把手写深度学习(0)&#xff1a;专栏文章导航 前言&#xff1a;训练自己的视频扩散模型的第一步就是准备数据集&#xff0c;而且这个数据集是text-video或者image-video的多模态数据集&#xff0c;这篇博客手把手教读者如何写一个这样扩散模型的的Video DataLoader。 目录 准…

【Vue3】深入理解Vue3路由器的工作原理to的两种写法

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…