【初体验 threejs】【学习】【笔记】hello,正方体 3!
前言
为了满足工作需求,我已着手学习 Three.js,并决定详细记录这一学习过程。在此旅程中,如果出现理解偏差或有其他更佳的学习方法,请大家不吝赐教,在评论区给予指正或分享您的宝贵建议,我将不胜感激。
项目基础
请参考hello,正方体 2!。
1. 抗锯齿
除非直线完全水平或垂直,否则使用方形像素绘制直线是很困难的。我们将使用一种称为抗锯齿(AA) 的技术来解决这个问题。
1.1 多重采样抗锯齿 (MSAA)
抗锯齿是使用内置的 WebGL 方法执行的,即 多重采样抗锯齿 (MSAA)。
1.2 启用抗锯齿
renderer.js: 添加如下
import { WebGLRenderer } from "three";/*** @description - 创建渲染器* @returns {WebGLRenderer} - 渲染器实例*/
export const createRenderder = () => {// 创建WebGLRenderer类的一个实例// 打开抗锯齿antialiasconst renderer = new WebGLRenderer({ antialias: true });// 启用物理上正确的光照renderer.physicallyCorrectLights = true;return renderer;
};
注意
一旦创建了渲染器,就无法更改此设置。
2. 浏览器窗口大小变化
目的是为了用户调整预览窗口的大小,场景适应新的大小。
2.1 扩展 Resizer 类
- 因为初始化时要设置初始大小,页面变化时也要重新设置大小,所有要把这部分设置代码移入到一个函数里。
- 将使用 ResizeObserver 来监听容器的大小变化。
- 因为我们只调用了.render 一次,它在画布中绘制了一个帧。当画布被调整大小时,这个框架被拉伸以适应新的大小。所以要创建一个 onResize 钩子,每次调整大小事件触发时生成一个新帧。
- 在 World 中使用 onResize 函数。
Resizer.js:添加如下
import { PerspectiveCamera, WebGLRenderer } from "three";
class Resizer {#container; // 容器#camera; // 相机#renderer; // 渲染器onResize = () => {}; // 页面大小变化钩子函数/*** @param {Element} container - 容器* @param {PerspectiveCamera} camera - 相机* @param {WebGLRenderer} renderer - 渲染器*/constructor(container, camera, renderer) {this.#container = container;this.#camera = camera;this.#renderer = renderer;// 初始化sizethis.#setSize();// 监听容器大小变化const resizeObserver = new ResizeObserver(() => {this.#setSize();this.onResize();});resizeObserver.observe(container);}#setSize() {this.#camera.aspect =this.#container.clientWidth / this.#container.clientHeight;this.#camera.updateProjectionMatrix();this.#renderer.setSize(this.#container.clientWidth,this.#container.clientHeight);this.#renderer.setPixelRatio(window.devicePixelRatio);}
}
export { Resizer };
World.js:添加如下
import { createScene } from "./components/scene";
import { createCamera } from "./components/camera";
import { createCude } from "./components/cube";
import { createLights } from "./components/lights";
import { createRenderder } from "./systems/renderer";
import { Resizer } from "./systems/Resizer";
class World {#scene; // 场景#camera; // 相机#renderer; // 渲染/*** @param {Element} container - 容器*/constructor(container) {this.#scene = createScene();this.#camera = createCamera();this.#renderer = createRenderder();container.append(this.#renderer.domElement);const cube = createCude();const light = createLights();this.#scene.add(cube, light);const resizer = new Resizer(container, this.#camera, this.#renderer);resizer.onResize = () => {this.render();};}/*** @description - 渲染函数*/render() {this.#renderer.render(this.#scene, this.#camera);}
}export { World };
3. 动画循环(运动)
为立方体添加一个简单的旋转动画。
- 创建一个动画循环
创建 systems/Loop.js 模块并在其中创建一个新 Loop 类
import { PerspectiveCamera, Scene, WebGLRenderer } from "three";
class Loop {#camera; // 相机#scene; // 场景#renderer; // 渲染/*** @param {PerspectiveCamera} camera - 相机* @param {Scene} scene - 场景* @param {WebGLRenderer} renderer - 渲染*/constructor(camera, scene, renderer) {this.#camera = camera;this.#scene = scene;this.#renderer = renderer;}/*** @description - 动画循环开始*/start() {}/*** @description - 动画循环结束*/stop() {}
}export { Loop };
- 在 World 中,将这个新类添加到导入列表中:
World.js: 添加如下
import { createScene } from "./components/scene";
import { createCamera } from "./components/camera";
import { createCude } from "./components/cube";
import { createLights } from "./components/lights";
import { createRenderder } from "./systems/renderer";
import { Resizer } from "./systems/Resizer";
import { Loop } from "./systems/Loop";
class World {#scene; // 场景#camera; // 相机#renderer; // 渲染#loop; // 动画循环/*** @param {Element} container - 容器*/constructor(container) {this.#scene = createScene();this.#camera = createCamera();this.#renderer = createRenderder();this.#loop = new Loop(this.#camera, this.#scene, this.#renderer);container.append(this.#renderer.domElement);const cube = createCude();const light = createLights();this.#scene.add(cube, light);const resizer = new Resizer(container, this.#camera, this.#renderer);resizer.onResize = () => {this.render();};}/*** @description - 渲染函数*/render() {this.#renderer.render(this.#scene, this.#camera);}/*** @description - 动画循环开始*/start() {this.#loop.start();}/*** @description - 动画循环结束*/stop() {this.#loop.stop();}
}export { World };
- 启动动画循环
main.js:添加如下
import { World } from "../World/World";function main() {// 获取容器const container = document.querySelector("#scene-container");// 创建一个world类实例const world = new World(container);// 开始动画循环world.start();
}main();
- 使用.setAnimationLoop 创建循环
在内部,循环是使用 .requestAnimationFrame。这种内置的浏览器方法可以智能地安排帧与显示器的刷新率同步,如果您的硬件跟不上,它会平滑地降低帧率。然而,.setAnimationLoop 还有一点额外的魔力可以确保循环在虚拟现实和增强现实环境中工作。
Loop.js:添加如下
import { PerspectiveCamera, Scene, WebGLRenderer } from "three";
class Loop {#camera; // 相机#scene; // 场景#renderer; // 渲染/*** @param {PerspectiveCamera} camera - 相机* @param {Scene} scene - 场景* @param {WebGLRenderer} renderer - 渲染*/constructor(camera, scene, renderer) {this.#camera = camera;this.#scene = scene;this.#renderer = renderer;}/*** @description - 动画循环开始*/start() {this.#renderer.setAnimationLoop(() => {this.#renderer.render(this.#scene, this.#camera);});}/*** @description - 动画循环结束*/stop() {this.#renderer.setAnimationLoop(null);}
}export { Loop };
- 移除 onResize 钩子
现在循环正在运行,每当我们调整窗口大小时,都会在循环的下一次迭代中生成一个新帧。这足够快,不会注意到任何延迟,因此我们不再需要在调整大小时手动重绘场景。从 World 中移除 resizer.onResize 钩子
World.js:添加如下
import { createScene } from "./components/scene";
import { createCamera } from "./components/camera";
import { createCude } from "./components/cube";
import { createLights } from "./components/lights";
import { createRenderder } from "./systems/renderer";
import { Resizer } from "./systems/Resizer";
import { Loop } from "./systems/Loop";
class World {#scene; // 场景#camera; // 相机#renderer; // 渲染#loop; // 动画循环/*** @param {Element} container - 容器*/constructor(container) {this.#scene = createScene();this.#camera = createCamera();this.#renderer = createRenderder();this.#loop = new Loop(this.#camera, this.#scene, this.#renderer);container.append(this.#renderer.domElement);const cube = createCude();const light = createLights();this.#scene.add(cube, light);new Resizer(container, this.#camera, this.#renderer);}/*** @description - 渲染函数*/render() {this.#renderer.render(this.#scene, this.#camera);}/*** @description - 动画循环开始*/start() {this.#loop.start();}/*** @description - 动画循环结束*/stop() {this.#loop.stop();}
}export { World };
4. 动画系统
每次循环运行时,希望通过将它们向前移动一帧来更新所有这些动画。就在我们渲染每一帧之前,我们会让立方体旋转一点点, 几乎是肉眼无法看到的微小量,但随着时间的推移会产生流畅的动画效果。
- Loop.tick 方法
为了处理所有这些,我们需要一个更新所有动画的函数,并且这个函数应该在每一帧开始时运行一次。
Loop.js:添加如下
import { PerspectiveCamera, Scene, WebGLRenderer } from "three";
class Loop {#camera; // 相机#scene; // 场景#renderer; // 渲染/*** @param {PerspectiveCamera} camera - 相机* @param {Scene} scene - 场景* @param {WebGLRenderer} renderer - 渲染*/constructor(camera, scene, renderer) {this.#camera = camera;this.#scene = scene;this.#renderer = renderer;}/*** @description - 动画循环开始*/start() {this.#renderer.setAnimationLoop(() => {this.tick();this.#renderer.render(this.#scene, this.#camera);});}/*** @description - 动画循环结束*/stop() {this.#renderer.setAnimationLoop(null);}/*** @description - 更新动画*/tick() {}
}export { Loop };
- cube.tick 方法
为动画对象创建一个.tick 方法,更新自身。
cube.js: 添加如下
import { BoxGeometry, Mesh, MeshStandardMaterial, MathUtils } from "three";
/*** @description - 创建立方体* @returns {Mesh} - 网格实例*/export const createCude = () => {// 创建边长为2的几何体(就是边长2米)const geometry = new BoxGeometry(2, 2, 2);// 创建一个高质量、通用、物理精确的材料 设置颜色为紫色const material = new MeshStandardMaterial({ color: "purple" });// 创建一个网格添加几何体和材质const cube = new Mesh(geometry, material);// 旋转立方体cube.rotation.set(-0.5, -0.1, 0.8);// 添加更新动画方法tickcube.tick = () => {cube.rotation.z += 0.01;cube.rotation.x += 0.01;cube.rotation.y += 0.01;};return cube;
};
注意
像这样在运行时向现有类添加属性称为 猴子补丁。
- Loop.#updatables
循环类中的动画对象列表。
Loop.js:添加如下
import { PerspectiveCamera, Scene, WebGLRenderer, Object3D } from "three";
class Loop {#camera; // 相机#scene; // 场景#renderer; // 渲染#updatables = []; // 循环里的动画对象列表/*** @param {PerspectiveCamera} camera - 相机* @param {Scene} scene - 场景* @param {WebGLRenderer} renderer - 渲染* @param {Object3D[]} updatables - 循环里的动画对象列表*/constructor(camera, scene, renderer, updatables = []) {this.#camera = camera;this.#scene = scene;this.#renderer = renderer;this.#updatables = updatables;}/*** @description - 动画循环开始*/start() {this.#renderer.setAnimationLoop(() => {this.tick();this.#renderer.render(this.#scene, this.#camera);});}/*** @description - 动画循环结束*/stop() {this.#renderer.setAnimationLoop(null);}/*** @description - 更新动画*/tick() {for (const object of this.#updatables) {object.tick();}}/*** @description - 添加动画对象* @param {...Object3D} object*/addObjectToUpdatables(...object) {this.#updatables.push(...object);}/*** @description - 删除动画对象* @param {...Object3D} object*/removeObjectFromUpdatables(...object) {this.#updatables = this.#updatables.filter((obj) => !object.includes(obj));}
}export { Loop };
- 添加 cube 到 Loop.#updatables
World.js:添加如下
import { createScene } from "./components/scene";
import { createCamera } from "./components/camera";
import { createCude } from "./components/cube";
import { createLights } from "./components/lights";
import { createRenderder } from "./systems/renderer";
import { Resizer } from "./systems/Resizer";
import { Loop } from "./systems/Loop";
class World {#scene; // 场景#camera; // 相机#renderer; // 渲染#loop; // 动画循环/*** @param {Element} container - 容器*/constructor(container) {this.#scene = createScene();this.#camera = createCamera();this.#renderer = createRenderder();this.#loop = new Loop(this.#camera, this.#scene, this.#renderer);container.append(this.#renderer.domElement);const cube = createCude();this.#loop.addObjectToUpdatables(cube);const light = createLights();this.#scene.add(cube, light);new Resizer(container, this.#camera, this.#renderer);}/*** @description - 渲染函数*/render() {this.#renderer.render(this.#scene, this.#camera);}/*** @description - 动画循环开始*/start() {this.#loop.start();}/*** @description - 动画循环结束*/stop() {this.#loop.stop();}
}export { World };
小结
立方体应该立即开始旋转。
5. 将动画速度与帧速率解耦
我们动画的速度取决于观看它的设备。我们的动画循环不会以固定速率生成帧。这意味着,在 60Hz 屏幕上,目标帧率为 60FPS,在 90Hz 屏幕上,目标帧率为 90FPS,以此类推。
在每一种情况下,动画循环都会以较低的速率生成帧,并且这个速率可能会因为许多因素从一个时刻到下一个时刻波动。这称为可变帧速率。
为了防止这种情况,我们需要将动画速度与帧速率解耦。我们将这样做:当我们告诉一个对象.tick 前进一帧时,我们将根据前一帧花费的时间来缩放移动的大小。
- 使用 Clock 类
用 Clock.getDelta 来衡量前一帧花了多长时间。.getDelta 告诉我们自上次调用.getDelta 以来已经过去了多少时间。
将结果保存在一个名为 delta 的变量中,然后我们将其传递给每个动画对象的.tick 方法。
Loop.js:添加如下
import {PerspectiveCamera,Scene,WebGLRenderer,Object3D,Clock,
} from "three";
class Loop {#camera; // 相机#scene; // 场景#renderer; // 渲染#updatables = []; // 循环里的动画对象列表#clock; // 秒表/*** @param {PerspectiveCamera} camera - 相机* @param {Scene} scene - 场景* @param {WebGLRenderer} renderer - 渲染* @param {Object3D[]} updatables - 循环里的动画对象列表*/constructor(camera, scene, renderer, updatables = []) {this.#camera = camera;this.#scene = scene;this.#renderer = renderer;this.#updatables = updatables;this.#clock = new Clock();}/*** @description - 动画循环开始*/start() {this.#renderer.setAnimationLoop(() => {this.tick();this.#renderer.render(this.#scene, this.#camera);});}/*** @description - 动画循环结束*/stop() {this.#renderer.setAnimationLoop(null);}/*** @description - 更新动画*/tick() {// .getDelta告诉我们自上次调用.getDelta以来已经过去了多少时间。const delta = this.#clock.getDelta();for (const object of this.#updatables) {object.tick(delta);}}/*** @description - 添加动画对象* @param {...Object3D} object*/addObjectToUpdatables(...object) {this.#updatables.push(...object);}/*** @description - 删除动画对象* @param {...Object3D} object*/removeObjectFromUpdatables(...object) {this.#updatables = this.#updatables.filter((obj) => !object.includes(obj));}
}export { Loop };
cube.js:添加如下
import { BoxGeometry, Mesh, MeshStandardMaterial, MathUtils } from "three";
/*** @description - 创建立方体* @returns {Mesh} - 网格实例*/export const createCude = () => {// 创建边长为2的几何体(就是边长2米)const geometry = new BoxGeometry(2, 2, 2);// 创建一个高质量、通用、物理精确的材料 设置颜色为紫色const material = new MeshStandardMaterial({ color: "purple" });// 创建一个网格添加几何体和材质const cube = new Mesh(geometry, material);// 旋转立方体cube.rotation.set(-0.5, -0.1, 0.8);// 度数转弧度const radiansPerSecond = MathUtils.degToRad(30);// 添加更新动画方法tickcube.tick = (delta) => {const radian = radiansPerSecond * delta;cube.rotation.z += radian;cube.rotation.x += radian;cube.rotation.y += radian;};return cube;
};
6. 总结
至此已经全部完成。你好,正方体 3!如果出现理解偏差或有其他更佳的学习方法,请大家不吝赐教,在评论区给予指正或分享您的宝贵建议,我将不胜感激。
主要文献
three.js 官网
《discoverthreejs》
相关文章:
【初体验 threejs】【学习】【笔记】hello,正方体 3!
前言 为了满足工作需求,我已着手学习 Three.js,并决定详细记录这一学习过程。在此旅程中,如果出现理解偏差或有其他更佳的学习方法,请大家不吝赐教,在评论区给予指正或分享您的宝贵建议,我将不胜感激。 项…...

第04章:IDEA的安装与使用
第04章:随堂复习与企业真题(IDEA安装与使用) 一、随堂复习 1. IDEA的认识 IDEA(集成功能强大、符合人体工程学(设置人性化))Eclipse 2. IDEA的下载、安装、卸载 卸载:使用控制面板进行卸载,…...
[原创][Delphi多线程]使用TMonitor, TEvent和TQueue配合实现TThreadQueue的经典使用案例.
[简介] 常用网名: 猪头三 出生日期: 1981.XX.XX QQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delph…...

6.12ctf练习
[西湖论剑 2022]Node Magical Login 源码在这里:GitHub - CTF-Archives/2022-xhlj-web-node_magical_login: A web challenge in 2022 西湖论剑大赛打开 打开环境是个登录框,先进行了扫描和抓包都没有看见什么有价值的东西,看源码 大致连接…...

海豚调度异常处理: 使用 arthas 在内存中删除启动失败的工作流
💡 本系列文章是 DolphinScheduler 由浅入深的教程,涵盖搭建、二开迭代、核心原理解读、运维和管理等一系列内容。适用于想对 DolphinScheduler了解或想要加深理解的读者。祝开卷有益。大数据学习指南 大家好,我是小陶,DolphinSch…...
在Qt中,QSerialPort::write(data) 和 readAll() 有什么关联和联系
在Qt中,QSerialPort::write(data) 和 readAll() 是与串行通信相关的两个不同的函数,它们属于 QSerialPort 类。这两个函数在串行通信中扮演不同的角色,但它们之间存在一定的联系: QSerialPort::write(data) 这个函数用于将数据发…...

第 2 章:Spring Framework 中的 IoC 容器
控制反转(Inversion of Control,IoC)与 面向切面编程(Aspect Oriented Programming,AOP)是 Spring Framework 中最重要的两个概念,本章会着重介绍前者,内容包括 IoC 容器以及容器中 …...
构造函数、实例、原型对象三者之间的关系
在 JavaScript 中,构造函数、实例和原型对象之间有着密切的关系。下面是对它们之间关系的详细解析和代码示例: 构造函数:构造函数是一个特殊的函数,用于创建对象的模板。它定义了对象的属性和方法。构造函数通常以大写字母开头&a…...

人工智能抢走了他们的工作。现在他们得到报酬,让它听起来像人类
人工智能抢走了他们的工作。现在他们得到报酬,让它听起来像人类 如果你担心人工智能会如何影响你的工作,那么广告文案的世界或许能让你窥见未来。 作家本杰明米勒(化名)在2023年初非常红火。他领导了一个由60多名作家和编辑组成的团队,发表博…...

大模型微调出错的解决方案(持续更新)
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…...

企业多云策略的优势与实施指南
企业在选择云服务提供商时,常见的选项包括亚马逊AWS、微软Azure、谷歌云GCP、阿里云、腾讯云和华为云。为了避免过度依赖单一供应商,许多企业选择采用多云策略,这样可以充分利用不同云服务的优势,同时避免重复工作和其他额外的工作…...

vue分页
先看效果 再看代码 <!-- 分页 --><div v-if"pageParams.pageCount > 1" class"flex justify-end mt-6"><n-paginationv-model:page"pageParams.page" v-model:page-size"pageParams.pageSize" :page-count"pa…...
服务器上设置pnpm环境变量
首先,确认 pnpm 是否已经安装: ls /www/server/nodejs/v20.10.0/bin/pnpm如果输出包含 pnpm,那么说明 pnpm 已经安装。 如果没有看到 pnpm,你可能需要重新安装它: npm install -g pnpm接下来,确保 PATH …...
Java中BIO、NIO、AIO详解
参考: https://blog.csdn.net/s2152637/article/details/98777686 https://blog.csdn.net/bigorsmallorlarge/article/details/137292669 1、几个基本概念 Java中IO模型简介 在Java中,主要有三种IO模型,分别是: 同步阻塞IO&…...

cloud_enum:一款针对不同平台云环境安全的OSINT工具
关于cloud_enum cloud_enum是一款功能强大的云环境安全OSINT工具,该工具支持AWS、Azure和Google Cloud三种不同的云环境,旨在帮助广大研究人员枚举目标云环境中的公共资源,并尝试寻找其中潜在的安全威胁。 功能介绍 当前版本的cloud_enum支…...

图像的对比度和亮度
目标 访问像素值用0来初始化矩阵cv::saturate_cast像素转换提高一张图像的亮度 原理 图像处理 图像变换可以被视作两个步骤: 点操纵(像素转换)相邻区域转换(以面积为基础) 像素转换 在这种图像处理的转换过程中…...

手撕设计模式——计划生育之单例模式
1.业务需求 大家好,我是菠菜啊。80、90后还记得计划生育这个国策吗?估计同龄的小伙伴们,小时候常常被”只生一个好“”少生、优生“等宣传标语洗脑,如今国家已经放开并鼓励生育了。话说回来,现实生活中有计划生育&…...

Mac M3 Pro 部署Flink-1.16.3
目录 1、下载安装包 2、解压及配置 3、启动&测试 4、测试FlinkSQL读取hive数据 以上是mac硬件配置 1、下载安装包 官网:Downloads | Apache Flink 网盘: Flink 安装包 https://pan.baidu.com/s/1IN62_T5JUrnYUycYMwsQqQ?pwdgk4e Flink 已…...

Mysql 的分布式策略
1. 前言 MySQL 作为最最常用的数据库,了解 Mysql 的分布式策略对于掌握 MySQL 的高性能使用方法和更安全的储存方式有非常重要的作用。 它同时也是面试中最最常问的考点,我们这里就简单总结下 Mysq 的常用分布式策略。 2. 复制 复制主要有主主复制和…...

记录一个利用winhex进行图片隐写分离的
前提 是一次大比武里面的题目,属实给我开了眼,跟我之前掌握的关于隐写合并的操作都不一样。 它不是直接在文件里面进行输入文件隐写,叫你输入密码,或者更改颜色,或者偏移位置; 它不是单纯几个文件合并&a…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...