马黑黑 发表于 2025-5-31 12:29

ThreeJS入门(十一)相机动画

<style>
        .artBox { font-size: 18px; }
        .artBox > p { margin: 10px 0; line-height: 30px; }
        .artBox mark { background: lightblue; padding: 4px 8px; }
        #prevBox { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: beige; display: none; padding: 0; overflow: hidden; z-index: 1000; margin: 0; }
        #prevBox::after { position: absolute; content: '关闭预览'; bottom: 10px; left: calc(50% - 40px); padding: 0 4px; width: 80px; height: 30px; line-height: 30px; text-align: center; border: 1px solid #efe; border-radius: 6px; background: #eee; font-size: 14px; box-shadow: 2px 2px 6px rgba(0,0,0,.25); cursor: pointer; }
        iframe { position: relative; width: 100%; height: 100%; border: none; outline: none; box-sizing: border-box; margin: 0; }
</style>

<div id="prevBox"></div>
<div class="artBox">
        <h2>(一)相机位置动画</h2>
        <p>现实生活中摄影者或者摄影设备可以绕着拍摄对象绕圈进行拍摄,得到的录像好比被拍摄对象在自转。ThreeJS 可以完成此类拍摄方式而且做的更好,方法是令相机在XYZ相应轴上改变位置,例如,在 XOZ 平面上绕Y轴做圆周运动,只需引入一个角度递增机制、再简单地通过正余弦函数计算出 <mark>position.x</mark> 和 <mark>position.z</mark> 坐标值,如此便可驱动相机绕着拍摄对象旋转。以下是完整实现代码:</p>
        <div class="hEdiv"><pre class="hEpre" id="pre1">
&lt;div style="margin: 10px; position: absolute;">点击页面可暂停/继续动画&lt;/div&gt;
&lt;script type="module"&gt;
        import * as THREE from 'https://638183.freep.cn/638183/web/ku/three.module.min.js';

        var scene = new THREE.Scene;
        var clock = new THREE.Clock();
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 0, 5);
        var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 绘制拍摄对象 :一个立方体
        // 先定义颜色colors和立方体六个面faces
        var colors = ['red', 'orange', 'purple', 'green', 'cyan', 'blue'], faces = [];
        var geometry = new THREE.BoxGeometry(); // 创建立方体
        // 再依据颜色数组构建六个面并储存到faces数组中
        colors.forEach(color => faces.push( new THREE.MeshBasicMaterial({ color: color })));
        var mesh = new THREE.Mesh(geometry, faces); // 创建立方体实例
        // 立方体在XYZ轴上的初始姿势
        mesh.rotateX(Math.PI / 4); // X轴
        mesh.rotateY(Math.PI / 1.5); // Y轴
        mesh.rotateZ(Math.PI / 3); // Z轴

        scene.add(mesh); // 立方体加入到场景

        var angle = 0; // 相机初始角度

        // 创建相机动画
        var animate = () => {
                requestAnimationFrame(animate); // 动画循环
                var delta = clock.getDelta(); // 获取动画时钟上下帧时间差
                angle = (angle + delta) % 360; // 角度由时间差驱动并取360的余数
                camera.position.x = 5 * Math.sin(angle);
                camera.position.z = 5 * Math.cos(angle); // 相机在Z轴上的位置
                camera.lookAt(0,0,0); // 相机始终聚焦场景中央
                renderer.render(scene, camera); // 渲染效果
        };

        window.onresize = () => {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
        }

        document.onclick = () => clock.running ? clock.stop() : clock.start();
       
        animate();
&lt;/script&gt;
        </pre></div>
        <blockquote><button id="btnPrev1">运行代码</button></blockquote>
        <p>相机在XYZ轴上的单轴或双轴或三轴位置的动态改变都会得到不同的拍摄效果,但若创建更强大的相机动画,还得借用第三方库,例如 ThreeJS 自己封装的 <mark>OrbitControls</mark> 模块 ——</p>
        <h2>(二)相机轨道动画</h2>
        <p>我们在ThreeJS精灵章节中的一个演示实例使用过相机轨道控制器。需要导入 OrbitControls.js 模块,导入方式需要做额外的结构封装。相机轨道控制器一旦成功导入,我们就可以真正全方位地查看场景的每一个点 —— 实际上是以极大自由度的方式变换相机的位置。下例将演示这一点:</p>
        <div class="hEdiv"><pre class="hEpre" id="pre2">
&lt;div style="margin: 10px; position: absolute;"&gt;可以手动翻转场景&lt;/div&gt;

&lt;!-- 标准化模块系统 :创建可靠的模块导入结构 --&gt;
&lt;script type="importmap"&gt;
{
"imports": {
    "three": "https://unpkg.ihwx.cn/three@0.176.0/build/three.module.js",
    "three/examples/jsm/": "https://unpkg.ihwx.cn/three@0.176.0/examples/jsm/"
}
}
&lt;/script&gt;

&lt;script type="module"&gt;
        import * as THREE from 'three'; // 导入ThreeJS核心库
        import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; // 导入相机轨道控制库

        const scene = new THREE.Scene;
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 0, 5);
        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const controls = new OrbitControls(camera, renderer.domElement); // 轨道控制器
        controls.autoRotate = true; // 启用轨道自转

        // 圆球 :半径0.5,法线网格材质
        const ball = new THREE.Mesh(
                new THREE.SphereGeometry(0.5),
                new THREE.MeshNormalMaterial()
        );

        // 圆盘 :半径2、16边,法线网格材质(线框化、双面渲染)
        const pan = new THREE.Mesh(
                new THREE.CircleGeometry(2, 16),
                new THREE.MeshNormalMaterial({ wireframe: true, side: THREE.DoubleSide })
        );

        scene.add(ball, pan); // 图像都加入到场景中

        const animate = () =&gt; {
                requestAnimationFrame(animate);
                controls.update(); // 更新轨道控制器令其自转
                renderer.render(scene, camera);
        };

        window.onresize = () =&gt; {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
        }

        animate();
&lt;/script&gt;
        </pre></div>
        <blockquote><button id="btnPrev2">运行代码</button></blockquote>
        <p>相机轨道控制器还可以有更多的操作,相关内容可参阅 <a href="http://www.webgl3d.cn/pages/837374/" target="_blank">相机控件OrbitControls</a> 并可在其上查找 OrbitControls 更多的资料。而相机位置动画,可点击 <a href="http://www.webgl3d.cn/pages/ca57bd/" target="_blank">ThreeJS中文网·相机动画(Sprite)</a> 查看。另外,相机动画还有更多的实现手段,有余力的朋友可以自行扩展。</p>
</div>

<script type="module">
        import hlight from 'https://638183.freep.cn/638183/web/helight/helight1.js';
        const pres = document.querySelectorAll('.hEpre');
        const divs = document.querySelectorAll('.hEdiv');
        divs.forEach( (div, key) => hlight.hl(div, pres));
       
        const preView = (htmlCode, targetBox) => {
                if (targetBox.innerHTML) return;
                const iframe = document.createElement('iframe');
                htmlCode = htmlCode + '<style>body {margin: 0; }</style>';
                iframe.srcdoc = htmlCode;
                targetBox.appendChild(iframe);
                targetBox.style.display = 'block';
                targetBox.onclick = () => {
                        targetBox.innerHTML = '';
                        targetBox.style.display = 'none';
                }
        };

        const value1 = pre1.textContent;
        const value2 = pre2.textContent;
        btnPrev1.onclick = () => preView(value1, prevBox);
        btnPrev2.onclick = () => preView(value2, prevBox);
</script>

梦江南 发表于 2025-5-31 16:00

谢谢老师辛苦,学生不才,只能欣赏支持了。{:4_187:}

梦江南 发表于 2025-5-31 16:00

祝老师端午节阖家幸福安康!{:4_187:}

花飞飞 发表于 2025-5-31 16:43

看到了相机动画的两种运动形态说明~~其实之前都有出现过的
昨天的《Hakim》是第一种,通过改变相机的轴位实现的动态
之前《古域传说》是第二种,通过轨道控制实现的动态。。{:4_173:}

花飞飞 发表于 2025-5-31 16:49

在第一个实例中,先设了六个颜色,立体方绘成之后,六个面就能依次着色,这也很神奇呀。。
设七个也只取前六个,只不过一二,三四,五六分别是对立面。。
应该是上下,左右,前后的顺序着色的吧。。

花飞飞 发表于 2025-5-31 16:53

先给立方体一个初始姿态,在时间差的驱动下角度变换,那就会不停的重绘再重绘,无限循环就看到动画了。{:4_173:}
想到之前的画布画时钟好象也是这个道理,不停的擦画。。显示不同的时间
联系起来比较好理解

花飞飞 发表于 2025-5-31 16:57

相机轨道控制,更形象一些。
拍电影的那些人,在演员周围架个轨道,以完成更快的人工难实现的角度变换。。{:4_173:}
这个是有个专用的相机轨道JS库来辅助完成的,象是给相机装了个专用外挂似的。

花飞飞 发表于 2025-5-31 17:03

相机轨道控制动画好象和适用于组合图形,或者是多个克隆图形,

就是整个场景在转。。无场景里放了多少个元素,都一起动
当然自身转动可以同时进行。。{:4_199:}

马黑黑 发表于 2025-5-31 17:25

花飞飞 发表于 2025-5-31 17:03
相机轨道控制动画好象和适用于组合图形,或者是多个克隆图形,

就是整个场景在转。。无场景里放了多少个 ...

精灵不受它的影响

马黑黑 发表于 2025-5-31 17:25

花飞飞 发表于 2025-5-31 16:57
相机轨道控制,更形象一些。
拍电影的那些人,在演员周围架个轨道,以完成更快的人工难实现的角度变换。。 ...

是酱紫的

马黑黑 发表于 2025-5-31 17:26

花飞飞 发表于 2025-5-31 16:53
先给立方体一个初始姿态,在时间差的驱动下角度变换,那就会不停的重绘再重绘,无限循环就看到动画了。{:4_ ...

ThreeJS 就是基于 JS canvas 弄的模块,加上 WebGL/GPU

马黑黑 发表于 2025-5-31 17:27

花飞飞 发表于 2025-5-31 16:49
在第一个实例中,先设了六个颜色,立体方绘成之后,六个面就能依次着色,这也很神奇呀。。
设七个也只取前 ...

多面体有多少个面就用多少个预设的颜色

马黑黑 发表于 2025-5-31 17:28

花飞飞 发表于 2025-5-31 16:43
看到了相机动画的两种运动形态说明~~其实之前都有出现过的
昨天的《Hakim》是第一种,通过改变相机的轴位 ...

上一个讲义第一个示例有用到的

马黑黑 发表于 2025-5-31 17:28

梦江南 发表于 2025-5-31 16:00
祝老师端午节阖家幸福安康!

{:4_190:}

杨帆 发表于 2025-5-31 19:55

谢谢马老师经典讲授,祝双节愉快{:4_191:}

花飞飞 发表于 2025-5-31 20:20

马黑黑 发表于 2025-5-31 17:25
精灵不受它的影响

精灵厉害,有法术护体。。{:4_170:}

花飞飞 发表于 2025-5-31 20:21

马黑黑 发表于 2025-5-31 17:25
是酱紫的

{:4_205:}猜准了还是开心的

花飞飞 发表于 2025-5-31 20:23

马黑黑 发表于 2025-5-31 17:26
ThreeJS 就是基于 JS canvas 弄的模块,加上 WebGL/GPU

啊,前身也是画布。。{:4_173:}那就算早认识了。

花飞飞 发表于 2025-5-31 20:24

马黑黑 发表于 2025-5-31 17:27
多面体有多少个面就用多少个预设的颜色

难道8面12面都可以这样一个个设下来。。艾玛,那可好看了

花飞飞 发表于 2025-5-31 20:25

马黑黑 发表于 2025-5-31 17:28
上一个讲义第一个示例有用到的
嗯哪,第一个示例比第二个示例用得多。。只要动的就碰上它
页: [1] 2 3 4 5 6
查看完整版本: ThreeJS入门(十一)相机动画