数百行代码,打造一个 Low 您所在的位置:网站首页 3dmax添加点 数百行代码,打造一个 Low

数百行代码,打造一个 Low

2023-05-09 21:09| 来源: 网络整理| 查看: 265

👁️什么是 Low-Poly

Low-Poly 是3D建模中的术语,意思是用相对较少的点线面来制作的低精度模型。Low-Poly 中的形状由三角形网格构成的,图形彼此交替连接成折线,然后构成新的几何图形。Low-Poly 设计风格的特点是低细节,抽象又具象。它延续了扁平化的特点,又与拟物化相结合。多边形组成的面数量多,面积小,配合柔光效果,营造出一种尖锐又硬朗的感觉。[1] Alex Pixel Cutter

一个精细渲染的球体转变为 Low-Poly 的过程[2] 😶‍🌫️如何让 Low-Poly 好看?

从个人的喜好出发,我认为好看的 Low-Poly 效果,满足以下几点要求:

合适的面数量 低饱和度 明亮的颜色 恰当的灯光设置

合适的面数量

面的数量不够少,将看不出 Low-Poly 风格。如上文中精细渲染的球体,面的数量太多了,形状非常光滑,导致丢失了 Low-Poly 风格尖锐硬朗的感觉,也没有抽象的感觉。

而面的数量过少,将使物体的表达丢失最低限度的具象要求。如上文中精细渲染的球体,将面减少至4个,形状会变为三棱锥,无法表达球体。

低饱和度

过高的饱和度,使得整个画面过于尖锐,丢失关注点,同时不同颜色的搭配冲击力过于“土味”。

image.png

明亮的颜色

因为在饱和度的选择上,我倾向于选择低饱和度的颜色,因此在色彩的选择上,建议尽量选明亮的颜色。否则后期配合灯光设置,暗色的阴面和阳面区分将不会很清楚,使得丢失画面细节。

恰当的灯光设置

灯光设置是最重要的一步,因为物体最终是否可见,效果如何,都取决于对场景中灯光的反射。不正确的灯光设置,将导致整个画面不协调、过度曝光、没有明暗等问题。

🕶️从零开始创建 Low-Poly Universe

接下来,我将从零开始,演示如何创建一个 Low-Poly Universe.

🍟创建一个空的场景 import * as THREE from '[email protected]' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' let renderer, camera, scene, controls; const container = document.getElementById('app') const init = () => { renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); container.appendChild(renderer.domElement) scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 10000 ); camera.position.set(0, 200, 300); camera.lookAt(0, 0, 0); controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.1; renderer.render(scene, camera); } init() 复制代码

以上代码将创建一个背景为纯黑色,抗锯齿的空场景。同时将摄像机位置放置在 (0,200,300)处。

设置灯光 const ambientLight = new THREE.AmbientLight(0xffffff, 0.3); const pointLightLeft = new THREE.PointLight(0xffffff, 1.0); pointLightLeft.position.set(200, 200, 0); const pointLightRight = new THREE.PointLight(0xffffff, 0.5, 0); pointLightRight.position.set(-200, 200, 0); scene.add(ambientLight, pointLightLeft, pointLightRight); 复制代码

AmbientLight 是环境光,环境光是一种低强度的光,在现实中,环境光是由光线经过周围环境表面多次反射后形成的。在三维可视化开发中,我们可以通过设置环境光来模拟这种效果,环境光将从各个方向将物体照亮,同时不产生投影。在 Three.js 中,AmbientLight 接收 color,intensity 两个参数,一个控制环境光的颜色,一个控制环境光的强度。

PointLight 是点光源,点光源是有位置的光源,而且光线均匀的射向空间中的所有方向。点光源的强度可以随着距离的增加而发生衰减。在 Three.js 中,Pointlight接收color,intensity,distance,decay 四个参数,分别控制点光源颜色、强度、距离和衰减系数。其中 distance 设置为 0 代表没有距离限制。

🐕‍🦺生成低多边形

在 Three.js 中生成低多边形非常方便,只需要使用 Three.js 提供的 TetrahedronGeometry 即可。TetrahedronGeometry 接收 radius,detail两个参数。radius控制低多边形的大小,detail控制低多边形面的数量。detail等于 0 生成三棱锥,随着 detail 的增加,整个物体约接近光滑的球体。因此要获得比较好的 Low-Poly 效果,detail 数值可以设置为 0/1/2/3。

const geometry = new THREE.TetrahedronGeometry(radius, detail); 复制代码 🤩给形状添加材质

形状为骨,材质为皮。材质在 Three.js 中有很多种,日常使用中,主要是考虑是否产生高光。因为我们的 Low-Poly 风格需要突出棱角分明,因此我选择使用 MeshPhongMaterial,这种材质会对光线产生强反射,形成高光。

const material = new THREE.MeshPhongMaterial({ color: color, emissive: 0x072534, flatShading: true }); 复制代码

在 Low-Poly 的项目中,材质设置成 flatshading:true 非常重要,这个参数使得物体根据每个三角形的法线计算着色效果。着色计算只执行一次,整个三角形都采用计算结果的颜色。如果不设置这个参数,最终渲染的物体将在不同的面上形成斑点状的高光,使 Low-Poly 风格物体的棱角不再分明。

💧生成场景

因为要生成很多不同颜色,形状和大小的 Low-Poly 物体,因此将生成形状的部分封装成一个函数。

const makeShape = (radius, detail, color) => { const geometry = new THREE.TetrahedronGeometry(radius, detail); const material = new THREE.MeshPhongMaterial({ color: color, emissive: 0x072534, flatShading: true }); const shape = new THREE.Mesh(geometry, material); return shape } 复制代码

在这个项目中,我想制作的是类似小行星环绕星球的感觉,因此最中间的星球要做的比较大一点。先手工创建一个星球。

const mainObject = makeShape(10, 3, 0xf1939c) scene.add(mainObject); 复制代码

同时在这个星球上有一致指示器,向用户传递星球可点击的信息。

const indicatorGeometry = new THREE.ConeGeometry(4, 8, 5, 1) const indicatorMaterial = new THREE.MeshPhongMaterial({ color: 0x8cc269, emissive: 0x072534, flatShading: true, transparent: true, opacity: 1 }); 复制代码

最外围的小行星,设置了一个最大值 maxNum=1000,意味着生成1000个形状各异的小行星。

const addOtherObjectToScene = (maxObjNum) => { for (let i = 0; i < maxObjNum - 1; i++) { let detail = Math.ceil(Math.random() * 3); let radius = Math.ceil(Math.random() * 4); let color = colors[Math.floor(Math.random() * (colors.length - 1))] const node = makeShape(radius, detail, color); scene.add(node); } } 复制代码

其中 colors 来自我选择的一些低饱和度,高亮度的颜色。

const colors = [0xf0c9cf, 0xc27c88, 0xc27c88, 0x983680, 0x61649f, 0x126bae, 0x126bae, 0x126bae, 0x5dbe8a] 复制代码

在 Three.js 中,物体默认的位置是 (0,0,0),为了使外围的小行星可以随机分布在围绕这中心星球的圆环带中,我们通过极坐标对物体进行定位。

const distance = Math.random() * 50 + 50;// 小行星与星球的距离为 50~100 const theta = Math.random() * 2 * Math.PI;// 随机角度 node.position.x = Math.cos(theta) * distance; node.position.z = Math.sin(theta) * distance; 复制代码

这样小行星就在一个圆环面上随机散开了。高度上也做一下分散处理,使得整个小行星带更加立体。

node.position.y = Math.random() * 50 - 25; 复制代码

至此,一个被2000颗小行星包围的星球就制作完成了。

🥁 让小行星环绕星球做公转和自转

所有的动画效果都通过修改物体的某些属性,并调用 requestAnimateFrame()实现。

const animate = () => { requestAnimationFrame(animate); renderer.render(scene, camera); //修改属性的代码 } animate() 复制代码 🥣小行星的自转

小行星的自转,实际上是不停的改变小行星的旋转角度实现的。在 Three.js 中,物体有一个 rotation 属性,修改这个属性,就可以修改小行星的旋转角度。

const animate = () => { requestAnimationFrame(animate); renderer.render(scene, camera); //修改属性的代码 for (let i in clickAbleObjects) { let obj = clickAbleObjects[i]; obj.rotation.y = obj.rotation.y + Math.random() * 0.02; } } animate() 复制代码

clickAbleObject 是自定义的一个数组,生成的所有物体,都先 push 到这个数组中再添加到场景中。

🎪小行星的公转

通过不停的更改小行星在圆周上的位置,实现小行星的公转。在生成小行星的代码中,给小行星增加几个属性。

const addOtherObjectToScene = (maxObjNum) => { for (let i = 0; i < maxObjNum - 1; i++) { let detail = Math.ceil(Math.random() * 3); let radius = Math.ceil(Math.random() * 4); let color = colors[Math.floor(Math.random() * (colors.length - 1))] const node = makeShape(radius, detail, color); node.distance = distance; node.theta = theta; clickAbleObjects.push(node); scene.add(node); } } 复制代码

在 animate 函数中,修改位置。

const animate = () => { requestAnimationFrame(animate); renderer.render(scene, camera); for (let i in clickAbleObjects) { let obj = clickAbleObjects[i]; obj.theta = obj.theta + 1 / 180 / 10 * Math.PI obj.rotation.y = obj.rotation.y + obj.rotationSpeed obj.position.x = Math.cos(obj.theta) * obj.distance; obj.position.z = Math.sin(obj.theta) * obj.distance; } } 复制代码

这样,小行星就产生了自转以及公转的效果。

🪂点击散开小行星带

小行星带的散开,通过修改小行星 distance 属性实现,将该属性改大一点就可以了。因此当监测到点击事件,修改 distance 即可。但在我的这个设计中,我并不想点击任何地方都可以展开小行星带,同时我也希望展开是逐渐向四周散开,有一个移动的过程,而不是一下子跳到了更大的圆环上。因此如何判断点击的物体是哪个,以及如何实现 distance 逐渐增大,是我们需要解决的问题。

这两个问题的解决方案,我已经写在了 Make your 3D project interactive 一文中,欢迎查阅。

👹最终效果及完整代码

😀请点击运行按钮,待渲染完成后,点击中间星球查看小行星散开动画。

🥰商业应用

本项目并不是无根之水,我们有一个知识图谱可视化的需求,该图谱共四个层级,其中最顶级一个节点,顶级节点有不固定数量的二级节点,二级节点有不固定数量的三级节点,三级节点有不固定数量的四级节点。我们期望通过三维可视化的形式对图谱进行展示,一来节点与节点中间的关系比较清晰,二来效果看起来也比较新颖,更符合用户喜好。在本次的演示中,点击星球之后,你可以看到小行星带展开成了三个圆环。这便是为了传递不同层级的信息。

在实际的项目中,我们还增加了点击之后,处于同一图谱树中的节点上浮,其余节点下层并透明。以及动态飞线、文字渲染等效果。后续将逐一增加到演示中。

😍其它

(^_^)如果你喜欢这个可视化项目,或者你想了解更多 AI 可视化相关信息,请打开🔜链接

🔗code.juejin.cn/pen/7226160…

点一个小小的赞👍,如果能收藏就更好了~栓Q🙇‍

💧参考

[1] 详解Low Poly设计风格,渣特效翻身成为流行?

[2]Low Poly Art制作简述及基础讲解



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有