拜托,使用Three.js让二维图片具有3D效果超酷的好吗 💥

声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。
背景
逛 sketchfab 网站的时候我看到有很多二维平面转 3D 的模型例子,于是仿照他们的例子,使用 Three.js + React 技术栈,将二维漫画图片转化为三维视觉效果。本文包含的内容主要包括:THREE.Group 层级模型、MeshPhongMaterial 高光网格材质、正弦余弦函数 创建模型移动轨迹等。
效果
实现效果如 下图所示:页面主要有背景图、漫画图片主体以及 Boom 爆炸背景图片构成,按住鼠标左键移动模型可以获得不同视图,让图片在视觉上有 3D 景深效果。

已适配:
PC端移动端
实现
本文实现比较简单,和我前面几篇文章实现基本上是相同的,流程也比较简单,主要是素材准备流程比较复杂。下面看看具体实现方法。
素材制作
准备一张自己喜欢的图片作为素材原图,图片内容最好可以分成多个层级,以实现 3D 景深效果,本实例中使用的是一张漫画图片,刚好可以切分成多个层级。

在 Photoshop 中打开图片,根据自己需要的分层数量,创建若干图层,并将地图复制到每个图层上,然后根据对图层景深层级的划分,编辑每个图层,结合使用魔棒工具和套索工具删除多余的部分,然后将每个图层单独导出作为素材。我分为 如上 7 个图层,外加一个边框,一共有 8 个图层。

资源引入
其中 OrbitControls 用于镜头轨道控制、TWEEN 用于镜头补间动画。
import React from 'react';
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
场景初始化
初始化渲染容器、场景、摄像机、光源。摄像机初始位置设置为位于偏左方的 (-12, 0, 0),以便于后面使用 TWEEN 实现翻转动画效果。
// 场景
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
// 添加背景图片
scene.background = new THREE.TextureLoader().load(background);
// 相机
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(-12, 0, 0);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// 直射光
light = new THREE.DirectionalLight(0xffffff, 1);
light.intensity = .2;
light.position.set(10, 10, 30);
light.castShadow = true;
light.shadow.mapSize.width = 512 * 12;
light.shadow.mapSize.height = 512 * 12;
light.shadow.camera.top = 100;
light.shadow.camera.bottom = - 50;
light.shadow.camera.left = - 50;
light.shadow.camera.right = 100;
scene.add(light);
// 环境光
ambientLight = new THREE.AmbientLight(0xdddddd);
scene.add(ambientLight);
创建漫画主体
首先创建一个 Group,用于添加图层网格,然后遍历图层背景图片数组,在循环体中创建每个面的网格,该网格使用平面立方体 PlaneGeometry,材质使用物理材质 MeshPhysicalMaterial,对每个网格位置设置相同的x轴和y轴值和不同的z轴值以创建景深效果。最后将 Group 添加到场景 Scene 中。
var layerGroup = new THREE.Group();
let aspect = 18;
for (let i=0; i<layers.length; i++) {
let mesh = new THREE.Mesh(new THREE.PlaneGeometry(10.41, 16), new THREE.MeshPhysicalMaterial({
map: new THREE.TextureLoader().load(layers[i]),
transparent: true,
side: THREE.DoubleSide
}));
mesh.position.set(0, 0, i);
mesh.scale.set(1 - (i / aspect), 1 - (i / aspect), 1 - (i / aspect));
layerGroup.add(mesh);
// 文字
if (i === 5) {
mesh.material.metalness = .6;
mesh.material.emissive = new THREE.Color(0x55cfff);
mesh.material.emissiveIntensity = 1.6;
mesh.material.opacity = .9;
}
// 会话框
if (i === 6) {
mesh.scale.set(1.5, 1.5, 1.5);
animateLayer = mesh;
}
}
layerGroup.scale.set(1.2, 1.2, 1.2);
到这一步,实现效果如下图所示:

THREE.Group 层级模型
将具有相同主体的网格可以通过 Group 合并在一起,以便于提高运行效率。Three.js 层级模型 Group 的基类是 Object3D,它是 Three.js 中大部分对象的基类,提供了一系列的属性和方法来对三维空间中的物体进行操纵。如可以通过 .add(object) 方法来将对象进行组合,该方法将对象添加为子对象。
但最好使用 Group 来作为父对象,因为 Group 相比较Object3D 更语义化,可以使用 Group 作为点、线、网格等模型的父对象,用来构建一个层级模型。
创建Boom背景
为了加强视觉效果,我添加了一个 Boom 爆炸图形平面作为背景,用鼠标移动的时候随着光线的变化,可以看到该图案有金属渐变效果,这种效果主要是通过高光材质 MeshPhongMaterial 的 specular 和 shininess 属性实现的。

const boom = new THREE.Mesh(new THREE.PlaneGeometry(36.76, 27.05), new THREE.MeshPhongMaterial({
map: new THREE.TextureLoader().load(boomImage),
transparent: true,
shininess: 160,
specular: new THREE.Color(0xff6d00),
opacity: .7
}));
boom.scale.set(.8, .8, .8);
boom.position.set(0, 0, -3);
layerGroup.add(boom)
scene.add(layerGroup);
添加后效果:

MeshPhongMaterial 高光网格材质
MeshPhongMaterial 是一种用于具有镜面高光的光泽表面的材质。该材质使用非物理的 Blinn-Phong 模型来计算反射率。 与 MeshLambertMaterial 中使用的 Lambertian 模型不同,该材质可以模拟具有镜面高光的光泽表面,如涂漆木材。
构造函数:
MeshPhongMaterial(parameters: Object)
parameters:可选,用于定义材质外观的对象,具有一个或多个属性。 材质的任何属性都可以从此处传入(包括从Material继承的任何属性)。
特殊属性:
.emissive[Color]:材质的放射光颜色,基本上是不受其他光照影响的固有颜色。默认为黑色。.emissiveMap[Texture]:设置发光贴图。默认值为null。放射贴图颜色由放射颜色和强度所调节。.emissiveIntensity[Float]:放射光强度。调节发光颜色。默认为1。.envMap[TextureCube]:环境贴图。默认值为null。.isMeshPhongMaterial[Boolean]:用于检查此类或派生类是否为Phong网格材质。默认值为true。.lightMap[Texture]:光照贴图。默认值为null。.lightMapIntensity[Float]:烘焙光的强度。默认值为1。.reflectivity[Float]:环境贴图对表面的影响程度。默认值为1,有效范围介于0(无反射)和1(完全反射)之间。.refractionRatio[Float]:空气的折射率除以材质的折射率。折射率不应超过1。默认值为0.98。.shininess[Float]:.specular高亮的程度,越高的值越闪亮。默认值为30。.skinning[Boolean]:材质是否使用蒙皮。默认值为false。.specular[Color]:材质的高光颜色。默认值为0x111111的颜色Color。这定义了材质的光泽度和光泽的颜色。.specularMap[Texture]:镜面反射贴图值会影响镜面高光以及环境贴图对表面的影响程度。默认值为null。
使用Phong着色模型计算着色时,会计算每个像素的阴影,与MeshLambertMaterial使用的Gouraud模型相比,该模型的结果更准确,但代价是牺牲一些性能。
镜头控制、缩放适配、动画
镜头补间动画,镜头切换到正确位置。
Animations.animateCamera(camera, controls, { x: 0, y: 0, z: 20 }, { x: 0, y: 0, z: 0 }, 3600, () => { });
镜头控制,本示例中显示了模型平移以及水平垂直旋转的角度,以达到最好的预览效果。
controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.enableDamping = true;
controls.enablePan = false;
// 垂直旋转角度限制
controls.minPolarAngle = 1.2;
controls.maxPolarAngle = 1.8;
// 水平旋转角度限制
controls.minAzimuthAngle = -.6;
controls.maxAzimuthAngle = .6;
屏幕缩放适配。
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}, false);
对于会话框图层网格,我给它添加了在一条光滑曲线上左右移动的动画效果,主要是通过修改它在 x 轴和 y 轴上的 position 来实现的 。
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls && controls.update();
TWEEN && TWEEN.update();
// 会话框摆动动画
step += 0.01;
animateLayer.position.x = 2.4 + Math.cos(step);
animateLayer.position.y = .4 + Math.abs(Math.sin(step));
}
正弦余弦函数创建模型移动轨迹
使用 step 变量并在函数 Math.cos() 和 Math.sin() 的帮助下 ,创建出一条光滑的轨迹。step+= 0.01 定义的是球的弹跳速度。
到此,本示例的完整实现都描述完毕了,大家感兴趣的话,可以动手试着把自己喜欢的图片改造成 3D 视图。拜托,使用 Three.js 这样展示图片超酷的好吗!
完整代码:https://github.com/dragonir/3d/tree/master/src/containers/Comic
总结
本文知识点主要包含的的新知识:
THREE.Group层级模型MeshPhongMaterial高光网格材质正弦余弦函数创建模型移动轨迹
想了解场景初始化、光照、阴影、基础几何体、网格、材质及其他
Three.js的相关知识,可阅读我往期文章。转载请注明原文地址和作者。如果觉得文章对你有帮助,不要忘了一键三连哦 。
附录
- [1]. Three.js 实现2022冬奥主题3D趣味页面
- [2]. 1000粉!使用Three.js制作一个专属3D奖牌
- [3]. Three.js 实现虎年春节3D创意页面
- [4]. Three.js 实现脸书元宇宙3D动态Logo
- [5]. Three.js 实现3D全景侦探小游戏
- [6]. Three.js实现炫酷的酸性风格3D页面
- [7]. 3dx模型转换为blender支持格式
本文作者:dragonir 本文地址:https://www.cnblogs.com/dragonir/p/15921635.html
拜托,使用Three.js让二维图片具有3D效果超酷的好吗 💥的更多相关文章
- js 生成二维码图片
1.用纯JavaScript实现的微信二维码图片生成器 QRCode.js是javascript实现二维码(QRCode)制作生成库. QRCode.js有着良好的跨浏览器兼容性(高版本使用HTML5 ...
- js生成二维码以及插入图片
先根据qrcode官网demo,不同属性值的变化,二维码的变化效果:https://larsjung.de/jquery-qrcode/latest/demo/ 进入demo中,审查元素查看里面引用的 ...
- 网站开发进阶(十四)JS实现二维码生成
JS实现二维码生成 绪 项目开发原语:已然花费半天的时间,仍旧未能将二维码显示在订单中.但是可以在单个页面中显示二维码,结合到angularjs的控制器中就失效了,自己是真的找不到其中的原因了.费解! ...
- 调用 js 生成图片二维码
一.js 生成二维码: 首先,需要引用 jQuery 和 jquery.qrcode.js.然后: //生产二维码 $("#qrcodeCanvas").qrcode({ rend ...
- js生成二维码以及点击下载二维码
js生成二维码 jquery.qrcode.js可以快速使用页面生成二维码.但改项目有两个小问题:1.不支持中文:2.不支持二维码中间生成图片. 支持中文的jquery-qrcode jquery.q ...
- Java与JS生成二维码
1.二维码概念 二维码/二维条码是用某种特定的集合图形按一定规律在平面上(二维方向上)分布的黑白相间的图形记录数据符号信息的图片. 黑线是二进制的1,空白的地方是二进制的0,通过1.0这种数据组合用于 ...
- 使用jquery.qrcode.js生成二维码
通常生成二维码的方式有两种:第一种是java代码的形式,第二种是通过Js方式. 在这里我做个记录,用js生成二维码,可以在官网下载源码:http://jeromeetienne.github.io/j ...
- js生成二维码 qrcode
js生成二维码 QRcode npm 地址 1.安装qrcode //在项目文件夹中执行: npm install --save qrcode //或者,将其全局安装以使用qrcode命令行来保存qr ...
- QRCode.js生成二维码
QRCode的GitHub地址: https://github.com/KeeeX/qrcodejs 该版本解决了主版本(https://github.com/davidshimjs/qrcodejs ...
随机推荐
- vue特效网站集锦
1.17素材网 http://www.17sucai.com/pins/tag/7012.html
- Protobuf使用--go和C#
一.Go安装及使用protobuf工具 以下都是基于Linux系统: 1.安装 A) protobuf 编译工具安装 1.下载 protoBuf: cd $GOPATH/src/ git clone ...
- 知乎上一个关于Android面试的问题答案
由于链接出错,这里附上原文链接:Touch Me 前段时间面试,自己以及小伙伴们简要的汇总的一些面试问题,可以对照的参考一下吧- 建议就是在面一家公司之前了解好这个公司的app是以什么为驱动的,例如电 ...
- MATLAB中插值算法实现
%%%1.M文件%(1).以往少的程序可以在命令行窗口进行编码,但大量的程序编排到命令行窗口,%会有造成乱码的危险.(2).如果将命令编成程序存储在一个文件中(M文件),依次运行文件中的命令,则可以重 ...
- 《剑指offer》面试题44. 数字序列中某一位的数字
问题描述 数字以0123456789101112131415-的格式序列化到一个字符序列中.在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等. 请写一个函数,求任意第n位 ...
- JuiceFS 在理想汽车的使用和展望
理想汽车是中国新能源汽车制造商,设计.研发.制造和销售豪华智能电动汽车,于 2015 年 7 月创立,总部位于北京,已投产的自有生产基地位于江苏常州,通过产品创新及技术研发,为家庭用户提供安全及便捷的 ...
- 阿里Java规范:【强制】所有的 POJO 类属性必须使用包装数据类型
在 Java 开发手册中有这一条: 我们知道基本类型和包装类型有很多不同点: 封装类型可以调用各种方法,而基本类型没有 封装类型声明字段之后可以不设置默认值,而基本类型需要初始化默认值.比如 int ...
- Kubeadm部署K8S(kubernetes)集群(测试、学习环境)-单主双从
1. kubernetes介绍 1.1 kubernetes简介 kubernetes的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理.目的是实现资源管理的自动 ...
- Python 序列类型小结
序列是python中最基本的数据结构. 序列中每一个元素都有其对应的索引,索引是从0开始,0,1,2......依次类推 python中的序列类型有:字符串str.列表list.元组tuple.Uni ...
- 10分钟了解代码命名规范(Java、Python)
前言 关于代码命名,我相信是经常困扰很多小伙伴的一个问题,尤其是对于强迫症晚期患者.怎么说呢,每次小编在写代码之前,总会在想啊想啊,用什么命名法好呢?对于经常在C++.Java.Python等主流语言 ...