Three.js 从零到精通:掌握 3D 网络开发的艺术 (three.js要学多久)_热门资讯_【聚客圈】_激活码商城_营销软件商城_微商软件

【同步智客】

同步智客

【代理加盟】

代理加盟

【综合商城】

软件商城

【微商必备】

微商必备

【营销必备】

营销必备

【知识付费】

知识付费

【代理项目落地训练营】

项目落地训练营

Three.js 从零到精通:掌握 3D 网络开发的艺术 (three.js要学多久)

suetone 2024-04-07 5浏览 0评论

简介

Three.js 是一个流行的 JavaScript 库,用于在网络浏览器中创建和操作 3D 图形。它是一种功能强大的工具,可用于构建各种 3D 应用程序,从交互式场景到逼真的可视化效果。

在本文中,我们将探讨 Three.js 的基础知识,并指导您使用它从头开始构建 3D 场景。我们将涵盖从创建基本几何形状到应用纹理和实现交互式动画的各个方面。

入门

设置 Three.js

您需要在您的项目中包含 Three.js 库。您可以从官方网站下载最新版本,或使用 CDN:

html

创建场景

Three.js 场景包含所有 3D 对象、相机和灯光。要创建一个场景,请创建一个新的 Scene 对象:

javascript const scene = new THREE.Scene();

创建相机

相机定义了观察者在场景中的视角。要创建一个透视相机,请使用 PerspectiveCamera 构造函数:

javascript const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  • 75 是视野(以度为单位)
  • window.innerWidth / window.innerHeight 是纵横比
  • 0.1 和 1000 是近平面和远平面的距离

创建渲染器

渲染器负责将场景转换为屏幕上的图像。要创建一个渲染器,请使用 WebGLRenderer 构造函数:

javascript const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight);

您可以将渲染器附加到 DOM 元素,以便在网页中显示输出:

javascript document.body.appendChild(renderer.domElement);

创建几何体

几何体是 3D 对象的基础。Three.js 提供了各种内置几何体,包括立方体、球体和圆柱体。

要创建立方体,请使用 BoxGeometry 构造函数:

javascript const geometry = new THREE.BoxGeometry(1, 1, 1);

创建材质

材质定义了对象的视觉外观。Three.js 提供了多种内置材质,包括基础颜色材质、纹理材质和光泽材质。

要创建基础颜色材质,请使用 MeshBasicMaterial 构造函数:

javascript const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });

创建网格对象

网格对象是几何体与材质的组合。要创建网格对象,请使用 Mesh 构造函数:

javascript const mesh = new THREE.Mesh(geometry, material);

现在您可以将网格对象添加到场景中:

javascript scene.add(mesh);

渲染场景

要渲染场景,请使用渲染器上的 render 方法:

javascript renderer.render(scene, camera);这将把场景呈现到屏幕上。

添加灯光

灯光对于使您的 3D 场景看起来逼真非常重要。Three.js 提供了多种内置灯光,包括点光源、聚光灯和环境光。

要创建点光源,请使用 PointLight 构造函数:

javascript const light = new THREE.PointLight(0xffffff, 1, 100); scene.add(light);
  • 0xffffff 是光源的颜色
  • 1 是光源的强度
  • 100 是光源的范围

实现交互

Three.js 允许您通过各种方式实现交互性。您可以检测鼠标移动、键盘输入和用户手势。

要检测鼠标移动,请使用 addEventListener 方法:

javascript canvas.addEventListener('mousemove', (event) => {// 在这里处理鼠标移动 });

您可以使用鼠标移动事件来旋转或移动相机。

掌握 Three.js

了解 Three.js 的基础知识后,您可以将其应用于各种 3D 项目。您可以构建交互式游戏、创建可视化效果或开发增强现实应用程序。

随着您继续学习 Three.js,您会发现很多有用的资源。官方文档、教程和社区论坛都是宝贵的学习来源。

总结

Three.js 是一个强大的工具,可用于创建令人惊叹的 3D 网络应用程序。通过遵循本文中的步骤,您已经迈出了学习 Three.js 旅程的第一步。

记住,精通 Three.js 需要时间和实践。继续构建项目,尝试新技术并从社区中学习。随着时间的推移,您将成为一名熟练的 Three.js 开发人员,能够创建令人惊叹的 3D 体验。


要成为一名专业的程序员,从零开始需要怎么一步步来比较好,要把最底层的先学精通吗?(个人认为)求学长

前言你是否觉得自己从学校毕业的时候只做过小玩具一样的程序?走入职场后哪怕没有什么经验也可以把以下这些课外练习走一遍(朋友的抱怨:学校课程总是从理论出发,作业项目都看不出有什么实际作用,不如从工作中的需求出发)建议:不要乱买书,不要乱追新技术新名词,基础的东西经过很长时间积累而且还会在未来至少10年通用。 回顾一下历史,看看历史上时间线上技术的发展,你才能明白明天会是什么样。 一定要动手,例子不管多么简单,建议至少自己手敲一遍看看是否理解了里头的细枝末节。 一定要学会思考,思考为什么要这样,而不是那样。 还要举一反三地思考。 注:你也许会很奇怪为什么下面的东西很偏Unix/Linux,这是因为我觉得Windows下的编程可能会在未来很没有前途,原因如下:现在的用户界面几乎被两个东西主宰了,1)Web,2)移动设备iOS或Android。 Windows的图形界面不吃香了。 越来越多的企业在用成本低性能高的Linux和各种开源技术来构架其系统,Windows的成本太高了。 微软的东西变得太快了,很不持久,他们完全是在玩弄程序员。 详情参见《Windows编程革命史》所以,我个人认为以后的趋势是前端是Web+移动,后端是Linux+开源。 开发这边基本上没Windows什么事。 启蒙入门1、 学习一门脚本语言,例如Python/Ruby可以让你摆脱对底层语言的恐惧感,脚本语言可以让你很快开发出能用得上的小程序。 实践项目:处理文本文件,或者csv (关键词 python csv, python open, python sys) 读一个本地文件,逐行处理(例如 word count,或者处理log)遍历本地文件系统 (sys, os, path),例如写一个程序统计一个目录下所有文件大小并按各种条件排序并保存结果跟数据库打交道 (python sqlite),写一个小脚本统计数据库里条目数量学会用各种print之类简单粗暴的方式进行调试学会用Google (phrase, domain, use reader to follow tech blogs)为什么要学脚本语言,因为他们实在是太方便了,很多时候我们需要写点小工具或是脚本来帮我们解决问题,你就会发现正规的编程语言太难用了。 2、 用熟一种程序员的编辑器(不是IDE) 和一些基本工具Vim / Emacs / Notepad++,学会如何配置代码补全,外观,外部命令等。 Source Insight (或 ctag)使用这些东西不是为了Cool,而是这些编辑器在查看、修改代码/配置文章/日志会更快更有效率。 3、 熟悉Unix/Linux Shell和常见的命令行如果你用windows,至少学会用虚拟机里的linux, vmware player是免费的,装个Ubuntu吧一定要少用少用图形界面。 学会使用man来查看帮助文件系统结构和基本操作 ls/chmod/chown/rm/find/ln/cat/mount/mkdir/tar/gzip …学会使用一些文本操作命令 sed/awk/grep/tail/less/more …学会使用一些管理命令 ps/top/lsof/netstat/kill/tcpdump/iptables/dd…了解/etc目录下的各种配置文章,学会查看/var/log下的系统日志,以及/proc下的系统运行信息了解正则表达式,使用正则表达式来查找文件。 对于程序员来说Unix/Linux比Windows简单多了。 (参看我四年前CSDN的博文《其实Unix很简单》)学会使用Unix/Linux你会发现图形界面在某些时候实在是太难用了,相当地相当地降低工作效率。 4、 学习Web基础(HTML/CSS/JS) + 服务器端技术 (LAMP)未来必然是Web的世界,学习WEB基础的最佳网站是W3School。 学习HTML基本语法学习CSS如何选中HTML元素并应用一些基本样式(关键词:box model)学会用 Firefox + Firebug 或 chrome 查看你觉得很炫的网页结构,并动态修改。 学习使用Javascript操纵HTML元件。 理解DOM和动态网页(Dynamic HTML: The Definitive Reference, 3rd Edition - OReilly Media) 网上有免费的章节,足够用了。 或参看 DOM 。 学会用 Firefox + Firebug 或 chrome 调试Javascript代码(设置断点,查看变量,性能,控制台等)在一台机器上配置Apache 或 Nginx学习PHP,让后台PHP和前台HTML进行数据交互,对服务器相应浏览器请求形成初步认识。 实现一个表单提交和反显的功能。 把PHP连接本地或者远程数据库 MySQL(MySQL 和 SQL现学现用够了)跟完一个名校的网络编程课程(例如:~ouster/cgi-bin/cs142-fall10/ ) 不要觉得需要多于一学期时间,大学生是全职一学期选3-5门课,你业余时间一定可以跟上学习一个javascript库(例如jQuery 或 ExtJS)+ Ajax (异步读入一个服务器端图片或者数据库内容)+JSON数据格式。 HTTP: The Definitive Guide 读完前4章你就明白你每天上网用浏览器的时候发生的事情了(proxy, gateway, browsers)做个小网站(例如:一个小的留言板,支持用户登录,Cookie/Session,增、删、改、查,上传图片附件,分页显示)买个域名,租个空间,做个自己的网站。 进阶加深1、 C语言和操作系统调用重新学C语言,理解指针和内存模型,用C语言实现一下各种经典的算法和数据结构。 推荐《计算机程序设计艺术》、《算法导论》和《编程珠玑》。 学习(麻省理工免费课程)计算机科学和编程导论学习(麻省理工免费课程)C语言内存管理学习Unix/Linux系统调用(Unix高级环境编程),,了解系统层面的东西。 用这些系统知识操作一下文件系统,用户(实现一个可以拷贝目录树的小程序)用fork/wait/waitpid写一个多进程的程序,用pthread写一个多线程带同步或互斥的程序。 多进程多进程购票的程序。 用signal/kill/raise/alarm/pause/sigprocmask实现一个多进程间的信号量通信的程序。 学会使用gcc和gdb来编程和调试程序(参看我的《用gdb调试程序》)学会使用makefile来编译程序。 (参看我的《跟我一起写makefile》)IPC和Socket的东西可以放到高级中来实践。 学习Windows SDK编程(Windows 程序设计 ,MFC程序设计)写一个窗口,了解WinMain/WinProcedure,以及Windows的消息机制。 写一些程序来操作Windows SDK中的资源文件或是各种图形控件,以及作图的编程。 学习如何使用MSDN查看相关的SDK函数,各种WM_消息以及一些例程。 这本书中有很多例程,在实践中请不要照抄,试着自己写一个自己的例程。 不用太多于精通这些东西,因为GUI正在被Web取代,主要是了解一下Windows 图形界面的编程。 @virushuo 说:“ 我觉得GUI确实不那么热门了,但充分理解GUI工作原理是很重要的。 包括移动设备开发,如果没有基础知识仍然很吃力。 或者说移动设备开发必须理解GUI工作,或者在win那边学,或者在mac/iOS上学”。 2、学习JavaJava 的学习主要是看经典的Core Java 《Java 核心技术编程》和《Java编程思想》(有两卷,我仅链了第一卷,足够了,因为Java的图形界面了解就可以了)学习JDK,学会查阅Java API Doc Java Platform SE 6了解一下Java这种虚拟机语言和C和Python语言在编译和执行上的差别。 从C、Java、Python思考一下“跨平台”这种技术。 学会使用IDE Eclipse,使用Eclipse 编译,调试和开发Java程序。 建一个Tomcat的网站,尝试一下JSP/Servlet/JDBC/MySQL的Web开发。 把前面所说的那个PHP的小项目试着用JSP和Servlet实现一下。 3、Web的安全与架构学习HTML5,网上有很多很多教程,以前酷壳也介绍过很多,我在这里就不罗列了。 学习Web开发的安全问题(参考新浪微博被攻击的这个事,以及Ruby的这篇文章)学习HTTP Server的rewrite机制,Nginx的反向代理机制,fast-cgi(如:PHP-FPM)学习Web的静态页面缓存技术。 学习Web的异步工作流处理,数据Cache,数据分区,负载均衡,水平扩展的构架。 实践任务:使用HTML5的canvas 制作一些Web动画。 尝试在前面开发过的那个Web应用中进行SQL注入,JS注入,以及XSS攻击。 把前面开发过的那个Web应用改成构造在Nginx + PHP-FPM + 静态页面缓存的网站4、学习关系型数据库你可以安装MSSQLServer或MySQL来学习数据库。 学习教科书里数据库设计的那几个范式,1NF,2NF,3NF,……学习数据库的存过,触发器,视图,建索引,游标等。 学习SQL语句,明白表连接的各种概念(参看《SQL Join的图示》)学习如何优化数据库查询(参看《MySQL的优化》)实践任务:设计一个论坛的数据库,至少满足3NF,使用SQL语句查询本周,本月的最新文章,评论最多的文章,最活跃用户。 5、一些开发工具学会使用SVN或Git来管理程序版本。 学会使用JUnit来对Java进行单元测试。 学习C语言和Java语言的coding standard 或 coding guideline。 (我N年前写过一篇关C语言非常简单的文章——《编程修养》,这样的东西你可以上网查一下,一大堆)。 推荐阅读《代码大全》《重构》《代码整洁之道》高级深入1、C++ / Java 和面向对象我个人以为学好C++,Java也就是举手之劳。 但是C++的学习曲线相当的陡。 不过,我觉得C++是最需要学好的语言了。 参看两篇趣文“C++学习信心图” 和“21天学好C++”学习(麻省理工免费课程)C++面向对象编程读我的 “如何学好C++”中所推荐的那些书至少两遍以上(如果你对C++的理解能够深入到像我所写的《C++虚函数表解析》或是《C++对象内存存局(上)(下)》,或是《C/C++返回内部静态成员的陷阱》那就非常不错了)然后反思为什么C++要干成这样,Java则不是?你一定要学会对比C++和Java的不同。 比如,Java中的初始化,废品回收,接口,异常,虚函数,等等。 实践任务:用C++实现一个BigInt,支持128位的整形的加减乘除的操作。 用C++封装一个数据结构的容量,比如hash table。 用C++封装并实现一个智能指针(一定要使用模板)。 《设计模式》必需一读,两遍以上,思考一下,这23个模式的应用场景。 主要是两点:1)钟爱组合而不是继承,2)钟爱接口而不是实现。 (也推荐《深入浅出设计模式》)实践任务:使用工厂模式实现一个内存池。 使用策略模式制做一个类其可以把文本文件进行左对齐,右对齐和中对齐。 使用命令模式实现一个命令行计算器,并支持undo和redo。 使用修饰模式实现一个酒店的房间价格订价策略——旺季,服务,VIP、旅行团、等影响价格的因素。 学习STL的用法和其设计概念 - 容器,算法,迭代器,函数子。 如果可能,请读一下其源码。 实践任务:尝试使用面向对象、STL,设计模式、和WindowsSDK图形编程的各种技能做一个贪吃蛇或是俄罗斯方块的游戏。 支持不同的级别和难度。 做一个文件浏览器,可以浏览目录下的文件,并可以对不同的文件有不同的操作,文本文件可以打开编辑,执行文件则执行之,mp3或avi文件可以播放,图片文件可以展示图片。 学习C++的一些类库的设计,如: MFC(看看候捷老师的《深入浅出MFC》) ,Boost, ACE, CPPUnit,STL (STL可能会太难了,但是如果你能了解其中的设计模式和设计那就太好了,如果你能深入到我写的《STL string类的写时拷贝技术》那就非常不错了,ACE需要很强在的系统知识,参见后面的“加强对系统的了解”)Java是真正的面向对象的语言,Java的设计模式多得不能再多,也是用来学习面向对象的设计模式的最佳语言了(参看Java中的设计模式)。 推荐阅读《Effective Java》 and 《Java解惑》学习Java的框架,Java的框架也是多,如Spring, Hibernate,Struts 等等,主要是学习Java的设计,如IoC等。 Java的技术也是烂多,重点学习J2EE架构以及JMS, RMI, 等消息传递和远程调用的技术。 学习使用Java做Web Service (官方教程在这里)实践任务: 尝试在Spring或Hibernate框架下构建一个有网络的Web Service的远程调用程序,并可以在两个Service中通过JMS传递消息。 C++和Java都不是能在短时间内能学好的,C++玩是的深,Java玩的是广,我建议两者选一个。 我个人的学习经历是:深究C++(我深究C/C++了十来年了)学习Java的各种设计模式。 2、加强系统了解重要阅读下面的几本书:《Unix编程艺术》了解Unix系统领域中的设计和开发哲学、思想文化体系、原则与经验。 你一定会有一种醍醐灌顶的感觉。 《Unix网络编程卷1,套接字》这是一本看完你就明白网络编程的书。 重要注意TCP、UDP,以及多路复用的系统调用select/poll/epoll的差别。 《TCP/IP详解 卷1:协议》- 这是一本看完后你就可以当网络黑客的书。 了解以太网的的运作原理,了解TCP/IP的协议,运作原理以及如何TCP的调优。 实践任务:理解什么是阻塞(同步IO),非阻塞(异步IO),多路复用(select, poll, epoll)的IO技术。 写一个网络聊天程序,有聊天服务器和多个聊天客户端(服务端用UDP对部分或所有的的聊天客户端进Multicast或Broadcast)。 写一个简易的HTTP服务器。 《Unix网络编程卷2,进程间通信》信号量,管道,共享内存,消息等各种IPC…… 这些技术好像有点老掉牙了,不过还是值得了解。 实践任务:主要实践各种IPC进程序通信的方法。 尝试写一个管道程序,父子进程通过管道交换数据。 尝试写一个共享内存的程序,两个进程通过共享内存交换一个C的结构体数组。 学习《Windows核心编程》一书。 把CreateProcess,Windows线程、线程调度、线程同步(Event, 信号量,互斥量)、异步I/O,内存管理,DLL,这几大块搞精通。 实践任务:使用CreateProcess启动一个记事本或IE,并监控该程序的运行。 把前面写过的那个简易的HTTP服务用线程池实现一下。 写一个DLL的钩子程序监控指定窗口的关闭事件,或是记录某个窗口的按键。 有了多线程、多进程通信,TCP/IP,套接字,C++和设计模式的基本,你可以研究一下ACE了。 使用ACE重写上述的聊天程序和HTTP服务器(带线程池)实践任务:通过以上的所有知识,尝试写一个服务端给客户端传大文件,要求把100M的带宽用到80%以上。 (注意,磁盘I/O和网络I/O可能会很有问题,想一想怎么解决,另外,请注意网络传输最大单元MTU)了解BT下载的工作原理,用多进程的方式模拟BT下载的原理。 3、系统架构负载均衡。 HASH式的,纯动态式的。 (可以到Google学术里搜一些关于负载均衡的文章读读)多层分布式系统 – 客户端服务结点层、计算结点层、数据cache层,数据层。 J2EE是经典的多层结构。 CDN系统 – 就近访问,内容边缘化。 P2P式系统,研究一下BT和电驴的算法。 比如:DHT算法。 服务器备份,双机备份系统(Live-Standby和Live-Live系统),两台机器如何通过心跳监测对方?集群主结点备份。 虚拟化技术,使用这个技术,可以把操作系统当应用程序一下切换或重新配置和部署。 学习Thrift,二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务。 学习Hadoop。 Hadoop框架中最核心的设计就是:MapReduce和HDFS。 MapReduce的思想是由Google的一篇论文所提及而被广为流传的,简单的一句话解释MapReduce就是“任务的分解与结果的汇总”。 HDFS是Hadoop分布式文件系统(Hadoop Distributed File System)的缩写,为分布式计算存储提供了底层支持。 了解NoSQL数据库(有人说可能是一个过渡炒作的技术),不过因为超大规模以及高并发的纯动态型网站日渐成为主流,而SNS类网站在数据存取过程中有着实时性等刚性需求,这使得目前NoSQL数据库慢慢成了人们所关注的焦点,并大有成为取代关系型数据库而成为未来主流数据存储模式的趋势。 当前NoSQL数据库很多,大部分都是开源的,其中比较知名的有:MemcacheDB、Redis、Tokyo Cabinet(升级版为Kyoto Cabinet)、Flare、MongoDB、CouchDB、Cassandra、Voldemort等。

js实现1百到1万的数字,生成2-30组随机的数字相加等于输入值且每组数字不重复,个十百位不重复

一位乐于分享学生精彩笔记的大学教授对于扫描版的文件非常不满意——颜色不清晰并且文件巨大。他因此用python自己写了一个小程序来解决这个问题。

这个程序可以用来整理手写笔记的扫描件哦,输出的图片不仅很清晰,而且文件大小只有100多KB!

先来看一个例子:

左:输入扫描件(300 DPI,7.2MB PNG/790KB JPG.)右:输出图片(300 DPI,121KB PNG)。

如果你急于上手操作,可以直接查看Github repo中的代码,或跳到本文结果部分,看看炫酷的颜色簇交互式三维图。

关注大数据文摘微信公众号,后台对话框内回复“压缩”下载哦~

起因

一些我任课的班级没有指定的教材,这是因为我更喜欢每周指定一个“学生记录员”,与班里其他同学分享他们的讲义。这样可以为学生提供一些书面资源,以便他们需要时可以进行对照。笔记以PDF的格式发布在课程网站。

在学校,我们有一台能够将笔记扫描成PDF文件的“智能”复印机,但是它生成的文件不够招人喜欢。下图是手写笔记的输出示例:

复印机好像随意地决定是否将每个数学符号进行二值化,或者转换后的JPG很不理想(如上图中的平方根符号)。因此我决定对上述问题进行优化。

概述

我们从某位同学一页漂亮的笔记开始处理,笔记扫描件如下:

以300 DPI精度扫描的原始PNG图像大小约为7.2MB;转换为图像品质较高的JPG格式后,文件大小约为790KB。由于PDF扫描件通常采用PNG或JPG作为容器格式,我们当然不希望在转换为PDF时损失文件信息。

但是考虑到网页加载时间,每页笔记800KB已经相当大了,我希望获得文件大小更接近100KB/页的图像。

虽然这位学生的笔记很整洁,但笔记的扫描件看起来有点乱。原因是复印机将这页笔记的反面内容也进行了扫描,这会分散读者的注意力,而对于JPG或PNG编码器来说,这种情况比纯色背景的图片更难压缩。

下图是我写的程序的输出结果:

输出结果是一个相对较小的PNG文件,大小只有121KB。不仅图像内存变小,而且看起来更清晰!这才是我想要的!

处理过程和彩色图像基础

以下是生成小内存且清晰的图像所需的步骤:

1.识别原始扫描图像的背景色。

2.根据背景色的不同阈值分离出前景色。

3.从前景色中选择几种“代表性颜色”,作为生成PNG过程中需要的索引色。

在深入研究这些步骤之前,先来了解下彩色图像是如何以数字形式进行存储的。由于人类眼睛中有三种不同类型的感色细胞,因此我们可以通过组合不同强度的红色、绿色和蓝色来重建任何颜色。重构过程就是将每种颜色与RGB颜色空间中的三维点一一对应,如下所示:

尽管真正的向量空间允许无限数量的像素亮度连续变化,但为了将颜色以数字形式存储在计算机上,我们需要对上述像素范围进行离散处理——通常红色、绿色和蓝色分别用8位通道色表示。这种将像素类比成三维色彩空间坐标的分析方法将为我们接下来的理解与重建提供巨大的帮助。

识别背景色

由于页面的大部分地方没有墨迹或线条,也许有人会认为纸张本身的颜色将会是扫描图像中出现频率最高的一种颜色——即复印机会将白纸的每个像素表示为相同的RGB值。

如果结果真是这样,那么分离背景色将不会有任何问题。遗憾的是,情况并非总是如此,由于复印机玻璃板上的灰尘和污迹、页面本身的颜色变化、传感器噪声等不同的因素,像素的RGB值会发生随机的变化,页面的“实际颜色”其实可能涵盖数千个不同的RGB值。

扫描图像的原件大小为2081×2531,共个像素点。虽然我们可以逐一处理每个像素点,但是处理输入图像的代表性像素点会更快。

程序默认采集输入图像5%的像素点(在扫描精度为300 DPI的情况下)。接下来,我们先选择一个点的小像素集,结果如下图所示:

虽然结果与笔记扫描件的页面差异很大(没有手写墨迹)——但两幅图像的颜色分布几乎完全相同。两张图片中大多像素点呈灰白色,也有少量红色、蓝色和深灰色的像素点。然后我们对个像素点按亮度进行了排序(例如将每个像素点的R、G和B进行求和),结果如下:

从远处看,图像底部80-90%的区域看上去是同一种颜色;然而仔细观察后,你会发现很多不一致的细节。事实上,上图中主要颜色(RGB值为(240,240,242))的像素个数仅为226——占比还不到总像素数的3%。

由于上述方法中主要颜色占总像素的比例很小,能否将它作为代表性颜色来描述图像的颜色分布就值得怀疑。如果在寻找方法之前先减小图像的位深度,我们将更好地识别页面的主要颜色。

因此我们把每个色彩通道四个最低有效位置零,将原来每个8位通道色简化成4位通道色,结果如下所示:

现在主要颜色的RGB值为(224,224,224),并且其像素点数为3623,占总像素的36%。通过减少位深度,实际上我们将相似的像素分到更大的“组”,这将更容易在数据中找到一个强峰。

可靠性和精确度之间存在一个折衷方案:小像素集可以更好地区分颜色,但大像素集处理起来更可靠。最后,我决定用6位通道色表示来识别背景色,这似乎是两个极端之间的一个最佳选择。

分离前景色

一旦识别出背景色,就可以根据图像中每个像素与背景色的相似程度来进行阈值计算。通常来说,通过计算两个像素坐标的欧几里得距离,再与预设的阀值进行比较就能得到他们之间的相似性。可这个最常用的方法却无法正确区分下面的几个颜色:

下表展示了每种颜色与背景色的欧几里德距离:

从表中可以看出,笔记反面渗过来的深灰色应该被分为背景色,但它与白色背景的差值要比粉红色的差值更大,而粉红色应该是前景色。如果使用这种方法,就无法有效分离出粉红色的前景色,因为总会包含渗过来的深灰色。

为了解决这个问题,我们可以将图片从RGB空间移动到色相-饱和度-亮度(Hue-Saturation-Value,HSV)空间,HSV将RGB的立方体转变为圆柱体,其剖面图如下:

HSV圆柱体上表面边缘呈现圆形分布的彩虹色,色度(hue)是指围绕圆柱体的中心轴旋转的角度(红色为0°)。圆柱体的中心轴从底部的黑色、中间的灰色渐变到顶部的白色——整个轴的饱和度(saturation)为0,外圆周上鲜艳的颜色饱和度都为1。最后,亮度(value)是指颜色的整体亮度,其变化范围从底部的暗色调到顶部的亮色调。

现在让我们用HSV重新区分一下之前的颜色:

从表中可以看出,白色、黑色和灰色的亮度差别很大,但它们的饱和度都很接近且数值较低——远低于红色或粉红色。通过分析图像的HSV值,我们可以利用下面的标准来标记属于前景色的像素,只需要满足其中一条就可以:

该像素的亮度与背景色的差值大于0.3;

该像素的饱和度与背景色的差值大于0.2;

第一条标准可以分离出笔记中的黑色墨迹,第二条标准则可以分离出红色墨迹和粉色线条,且这两个标准在选取前景色时排除了笔记反面渗透过来的灰色。但不同的图像可能需要不同的饱和度或亮度阈值,详情请参阅结果部分。

选择一组有代表性的颜色

当我们将前景色分离后,会得到与页面上笔记的颜色相对应的一组颜色。将得到的像素点重新放进RGB空间并计算每个像素对应的坐标,可以看到新的散点图呈现簇状,每一个颜色会形成自己的色块:

由提供支持的交互式三维图

现在我们的目标是将原始的图像(24位/像素)中的所有颜色用8种“索引色”进行替换(8并非固定的数字)。这样做有两种好处:首先,它缩小了文件的大小,因为现在只需要3位就可以指定一种颜色(因为8 = 2^3);此外,它使得生成的图像在视觉上更美观,因为在最终输出的图像中,相似颜色的笔记都会只用一种颜色替代。

为了实现这个目标,我们通过数据驱动的方式,也就是利用上图中的“簇状”特性,选择每个色簇的中心坐标来表示这一组颜色。用术语说,我们将通过聚类分析来解决一个色彩量化问题(其实是向量量化)。

具体的做法是,通过k-means算法在一个颜色簇中找到一个点,这个点到其他每个点的平均距离之和最小。对上述数据集使用这个方法,得到7个不同的颜色簇:

由提供支持的交互式三维图

在这张图中,黑色轮廓彩色实心的点表示前景色像素的颜色坐标,通过彩色的线将它们连接到RGB色彩空间中最近的中心点。当图像转换为索引颜色时,每个前景色像素的颜色将被替换为距其最近的中心点的颜色。最后,包围每个颜色簇的圆表示每个中心点距相关像素的最远距离。

细节调整

除了能够设置亮度和饱和度的阈值之外,还具有几个其他值得一提的功能。默认情况下,它通过将亮度的最小和最大值重新调整为0和255来增加最终调色板的鲜艳度和对比度。如果不进行调整,上述扫描件的8色调色板将如下所示:

调整后的调色板色彩更鲜明:

在完成前景色分离后,还有一个选项可以强制将背景色变为白色。通过转换为索引颜色的图像可以进一步压缩PNG文件,还可以运行如optipng、pngcrush或pngquant等图像优化工具。

该程序最终会将多个压缩后的图像合并为一个PDF文件,就像使用ImageMagick的转换工具一样。此外,会自动对输入文件名进行数字排序(而不是像shell globbing 那样按字母顺序排列)。当复印机输出的文件名是scan 和scan 时是非常有帮助的,上述排序功能保证了压缩后的页面在PDF中也保持同样的顺序。

结果

以下是一些程序输出的例子。第一个输出的PDF使用默认的阈值设置,看起来很棒:

不同颜色簇的可视化:

由提供支持的交互式三维图

第二个PDF需要将饱和度阈值降低到0.045,因为蓝灰色的线条颜色太深不便于阅读:

对应的颜色簇:

由提供支持的交互式三维图

最后这个PDF来自于工程师的方格纸,在这个过程中我将亮度阈值设置为0.05,因为背景和线条之间的对比度非常低:

对应的颜色簇:

由提供支持的交互式三维图

综上,这四份PDF文件大小约788KB,平均每页130KB大小。

结论与展望

我很高兴能开发一个实用的工具,这个工具可以将课程网站中的手写笔记的PDF进行加工和美化。与此同时,记录下这整个过程也让我受益匪浅,我先后在维基百科上补充了关于颜色量化的更多内容,也促使我尝试并学习了。

如果再次启动这个项目,我想尝试一下其他的量化方案,就在前几天还在想用光谱簇结合最近邻图的方式去尝试一下,当时十分兴奋认为这是一个绝佳的方案,然后就发现已经有一篇2012年的论文提出了完全一样的构思,哎…

你也可以尝试使用最大期望算法来生成描述颜色分布的高斯混合模型——不确定之前是否有人做过类似的实现。当然感兴趣的同学也可以试试其他有趣的想法,如使用Lab这

Three.js游戏开发入门

就在不久前,创建和部署 游戏 的唯一方法是选择像 Unity 或 Unreal 这样的 游戏 引擎,学习语言,然后打包 游戏 并将其部署到你选择的平台上。

试图通过浏览器向用户提供 游戏 的想法似乎是一项不可能完成的任务。

幸运的是,由于浏览器技术的进步和硬件加速在所有流行的浏览器中都可用,JavaScript 性能的改进以及可用处理能力的稳步提高,为浏览器创建交互式 游戏 体验变得越来越普遍。

在本文中,我们将了解如何使用 创建 游戏 。但首先,让我们回顾一下 是什么以及为什么它是 游戏 开发的好选择。

在 GitHub 上的项目描述恰当地将 描述为“......一个易于使用、轻量级、跨浏览器的通用 3D 库”。

让我们作为开发人员可以相对简单地在屏幕上绘制 3D 对象和模型。如果没有它,我们将需要直接与 WebGL 交互,虽然这并非不可能,但即使是最小的 游戏 开发项目也会花费大量时间。

传统上,“ 游戏 引擎”由多个部分组成。例如,Unity 和 Unreal 提供了一种将对象渲染到屏幕上的方法,但也提供了大量其他功能,如网络、物理等等。

然而, 的方法更受限制,不包括物理或网络之类的东西。但是,这种更简单的方法意味着它更容易学习和更优化以做它最擅长的事情:将对象绘制到屏幕上。

它还有一组很棒的示例,我们可以使用它们来了解如何在屏幕上绘制各种对象。最后,它提供了一种简单且原生的方式将我们的模型加载到我们的场景中。

如果不希望用户需要通过应用商店下载应用或进行任何设置来玩你的 游戏 ,那么 作为 游戏 开发引擎可能是一个有吸引力的选择。如果你的 游戏 在浏览器中运行,那么进入门槛最低,这只能是一件好事。

今天,我们将通过制作一个使用着色器、模型、动画和 游戏 逻辑的 游戏 来浏览 。我们将创建的内容如下所示:

这个概念很简单。我们控制着一艘火箭飞船,穿越一个星球,我们的目标是拾取能量晶体。我们还需要通过增加护盾来管理飞船的 健康 状况,并尽量不要因为撞击场景中的岩石而严重损坏我们的船。

在我们的运行结束时,火箭飞船返回天空中的母舰,如果用户点击 NEXT LEVEL ,他们会再次尝试,这一次火箭要经过更长的路径。

随着用户玩 游戏 ,火箭飞船的速度会增加,因此他们必须更快地躲避岩石并收集能量晶体。

Three.js 从零到精通:掌握 3D 网络开发的艺术 (three.js要学多久) 第1张

要创建这样的 游戏 ,我们必须回答以下问题:

到我们制作这款 游戏时,我们将克服这些挑战。

不过,在我们开始编码之前,我们必须回顾一些简短的理论,特别是与我们将如何在 游戏 中创造运动感有关。

想象一下,你在现实生活中控制着一架直升机,并且正在跟踪地面上的一个物体。物体以逐渐增加的速度继续前进。为了跟上,你必须逐渐提高你所在直升机的速度。

如果对直升机或地面上的物体的速度没有限制,只要你想跟上地面上的物体,这种情况就会持续下去。

当创建一个跟随对象的 游戏 时,正如我们在本例中所做的那样,应用相同的逻辑可能很诱人。也就是说,在世界空间中随着物体的加速移动物体,并更新后面跟随的相机的速度。然而,这提出了一个直接的问题。

基本上,每个玩这个 游戏 的人都会在他们的手机或台式电脑上玩它。这些设备资源有限。如果我们尝试在相机移动时生成可能无限数量的对象,然后移动该相机,最终我们将耗尽所有可用资源,并且浏览器选项卡将变得无响应或崩溃。

我们还需要创建一个代表海洋的平面(一个平面 2D 对象)。当我们这样做时,我们必须给出海洋的尺寸。

然而,我们不能创建一个无限大的平面,我们也不能创建一个巨大的平面,只是希望用户永远不会在我们的关卡中前进到足以让他们离开平面的程度。

那是糟糕的设计,并且希望人们玩我们的 游戏 不足以体验错误似乎违反直觉。

我们不是在一个方向上无限期地移动我们的相机,而是让相机保持静止并移动它周围的环境。这有几个好处。

一是我们总是知道火箭飞船在哪里,因为火箭的位置不会移到远处;它只会左右移动。这让我们很容易判断物体是否在相机后面,并且可以从场景中移除以释放资源。

另一个好处是我们可以选择远处的一个点来创建对象。这意味着当物体接近玩家时,新的物品或物体将不断地在玩家视野之外的距离创建。

当它们从视野中消失时,无论是玩家与它们发生碰撞还是从玩家身后消失,这些物品都会从场景中移除,以降低内存使用量。

要创建这种效果,我们需要做两件事:首先,我们需要在程序上沿深度轴移动每个项目,以将对象移向相机。其次,我们必须为我们的水面提供一个可以抵消的值,并随着时间的推移增加这个偏移量。

这将产生水面移动越来越快的效果。

现在我们已经解决了如何在场景中向前移动火箭,让我们继续设置我们的项目。

让我们开始制作 游戏 吧!我们需要做的第一件事是设置构建环境。对于这个例子,我选择使用 Typescript 和 Webpack。这篇文章不是要讨论这些技术的有点,所以除了快速总结之外,我不会在这里详细介绍它们。

使用 Webpack 意味着当我们开发项目并保存文件时,Webpack 将看到我们的文件已更改,并使用保存的更改自动重新加载浏览器。

在我们的示例中使用 TypeScript 意味着我们的项目将具有类型安全性。我发现这在使用 的一些内部类型时特别有用,比如Vector3s 和Quaternions. 知道我将正确类型的值分配给变量是非常有价值的。

我们还将在 UI 中使用Materialize CSS。对于我们将用作 UI 的几个按钮和卡片,这个 CSS 框架将有很大帮助。

要开始我们的项目,请创建一个新文件夹。在文件夹中,创建一个并粘贴以下内容:

然后,在命令窗口中,键入npm i以将包安装到新项目中。

我们现在需要创建三个文件,一个基本的 Webpack 配置文件,然后是我们项目的开发和生产配置文件。

在项目文件夹中创建一个文件并粘贴以下配置:

然后,创建一个文件并粘贴这些详细信息。这配置了 Webpack 开发服务器的热重载功能:

最后,创建一个文件并粘贴这些详细信息:

我们需要做的下一件事是配置 TypeScript 环境以允许我们使用来自 JavaScript 文件的导入。为此,请创建一个文件并粘贴以下详细信息:

我们的构建环境现在已经配置好了。现在是时候开始为我们的玩家创造一个美丽而可信的场景了。

我们的场景包含以下元素:

我们将在一个名为 的文件中完成大部分工作,但我们也会将部分 游戏 拆分为单独的文件,这样我们就不会得到一个非常长的文件。我们现在可以继续创建文件。

因为我们正在处理一个非常复杂的主题,所以我还将包含指向此代码在 GitHub 上的项目中的位置的链接。这应该有望帮助你保持自己的方向,而不是在更大的项目中迷失方向。

我们需要做的第一件事是创建一个Scene,以便 有一些东西可以渲染。在我们的中,我们将添加以下行来构建我们的并将 一个ScenePerspectiveCamera放置在场景中,这样我们就可以看到发生了什么。

最后,我们将为稍后分配的渲染器创建一个引用:

为了设置我们的场景,我们需要执行一些任务,比如创建一个新的WebGLRenderer和设置我们想要绘制的画布的大小。

为此,让我们创建一个init函数并将其也放入我们的中。此init函数将为我们的场景执行初始设置,并且只运行一次(当 游戏 首次加载时):

我们还需要为场景利用渲染和动画循环。我们需要动画循环来根据需要在屏幕上移动对象,并且我们需要渲染循环来将新帧绘制到屏幕上。

让我们继续在中创建render函数。 一开始,这个函数看起来很简单,因为它只是请求一个动画帧然后渲染场景。

我们请求动画帧的原因有很多,但其中一个主要原因是如果用户更改选项卡,我们的 游戏 将暂停,这将提高性能并减少设备上可能浪费的资源:

好了,现在我们有了一个空的场景,里面有一个相机,但没有别的了。让我们在场景中添加一些水。

幸运的是, 包含一个我们可以在场景中使用的水对象示例。它包括实时反射,看起来相当不错;你可以在这里查看。

对我们来说幸运的是,这种水将完成我们在场景中想要做的大部分事情。我们唯一需要做的就是稍微改变水的着色器,这样我们就可以在渲染循环中更新它。

我们这样做是因为如果随着时间的推移,我们越来越多地抵消我们的水纹理,那么它会给我们带来速度的感觉。

作为展示,下面就是我们 游戏 的开场场景,但我每帧都增加了偏移量。随着偏移量的增加,感觉就像我们下方海洋的速度正在增加(即使火箭实际上是静止的)。

水对象可以在 GitHub 上找到。我们唯一需要做的就是做一个小的改变,使这个偏移量可以从我们的渲染循环中控制(所以我们可以随着时间的推移更新它)。

我们要做的第一件事是在 存储库中获取 示例的副本。我们将把这个文件objects/放在我们的项目中。如果我们打开文件,大约一半,我们将开始看到如下所示的内容:

这是海洋材质的着色器。着色器本身的介绍超出了本文的范围,但基本上,它们是我们的 游戏 将向用户的计算机提供的关于如何绘制此特定对象的说明。

这里还有我们的着色器代码,它是用 OpenGraph 着色器语言 (GLSL) 编写的,并合并到一个原本是 JavaScript 的文件中。

这没有什么问题,但是如果我们将这个着色器代码单独移动到一个文件中,那么我们可以将 GLSL 支持安装到我们选择的 IDE 中,我们将获得语法着色和验证之类的东西,这有助于我们自定义 GLSL .

要将 GLSL 分解为单独的文件,让我们在当前objects目录中创建一个shader目录,选择 我们的vertexShader和 fragmentShader 的内容, 并将它们分别移动到和文件中。

在我们文件的顶部,我们有一个getNoise函数。默认情况下,它看起来像这样:

为了使这个偏移量可以从我们的 游戏 代码中调整,我们想在我们的 GLSL 文件中添加一个参数,允许我们在执行期间对其进行修改。为此,我们必须将此函数替换为以下函数:

你会注意到我们在这个 GLSL 文件中包含了一个新变量:speed变量。这是我们将更新以提供速度感的变量。

在我们的中,现在需要配置水的设置。在我们文件的顶部,添加以下变量:

然后,在我们的init函数中,必须配置水平面的旋转和位置,如下所示:

这将为海洋提供正确的旋转。

带有一个相当令人信服的天空,我们可以在项目中免费使用它。你可以在此处的 示例页面中查看此示例。

在我们的项目中添加天空非常容易;只需要将天空添加到场景中,设置天空盒的大小,然后设置一些参数来控制天空的外观。

我们需要对初始场景初始化做的最后一件事是添加一些光照并添加我们的火箭模型和母舰模型:

现在我们有了一些漂亮的水和火箭的场景。但是,我们缺乏任何可以真正使它成为 游戏 的东西。为了解决这个问题,我们需要构建一些基本参数来控制 游戏 并允许玩家朝着某些目标前进。

在我们文件的顶部,我们将添加以下sceneConfiguration变量,这有助于我们跟踪场景中的对象:

现在,我们必须为玩家所在的当前关卡执行初始化。这个场景设置函数很重要,因为每次用户开始一个新的关卡时都会调用它。

因此,我们需要将火箭的位置设置回起点并清理所有正在使用的旧资产。我在代码行内添加了一些注释,以便你可以看到每一行在做什么:

我们预计有两种类型的设备可以玩我们的 游戏 :台式电脑和手机。为此,我们需要适应两种类型的输入选项:

现在让我们配置这些。

在我们的开始,我们将添加以下变量来跟踪键盘上是否按下了左键或右键:

然后,在我们的init函数中,我们将注册keydownandkeyup事件来分别调用onKeyDownandonKeyUp函数:

最后,对于键盘输入,我们将记录按下这些键时要执行的操作:

我们的移动用户没有键盘可以输入,因此,我们将使用nippleJS在屏幕上创建一个操纵杆,并使用操纵杆的输出来影响火箭在屏幕上的位置。

在我们的init函数中,我们将通过检查它在屏幕上是否有非零数量的触摸点来检查设备是否是触摸设备。如果是,我们将创建操纵杆,但一旦玩家释放操纵杆的控制,我们还将将火箭的运动设置回零:

在我们的animate函数中,我们会跟踪此时按下左键或右键或操纵杆是否正在使用中的操作。我们还将火箭的位置夹在可接受的左右位置,这样火箭就不能完全移出屏幕:

正如我们已经讨论过的,火箭飞船在我们的场景中保持静止,并且物体朝它移动。这些物体移动的速度随着用户继续玩而逐渐增加,随着时间的推移增加了关卡的难度。

仍然在我们的动画循环中,我们希望逐步将这些对象移向玩家。当对象离开玩家的视野时,我们希望将它们从场景中移除,这样我们就不会占用玩家计算机上不必要的资源。

在我们的渲染循环中,我们可以像这样设置这个功能:

我们可以看到有几个函数是这个调用的一部分:

让我们 探索 一下这些函数在我们的 游戏 中完成了什么。

碰撞检测是我们 游戏 的重要途径。没有它,我们将不知道我们的火箭飞船是否达到了任何目标,或者它是否撞到了岩石并应该减速。这就是我们想要在 游戏 中使用碰撞检测的原因。

通常,我们可以使用物理引擎来检测场景中对象之间的碰撞,但是 没有包含物理引擎。

不过,这并不是说 不存在物理引擎。他们当然可以,但是为了我们的需要,我们不需要添加物理引擎来检查我们的火箭是否击中了另一个物体。

本质上,我们想回答这个问题,“我的火箭模型目前是否与屏幕上的任何其他模型相交?” 我们还需要根据受到的打击以某些方式做出反应。

例如,如果我们的玩家不断将火箭撞到岩石上,我们需要在受到一定程度的伤害后结束关卡。

为了实现这一点,让我们创建一个函数来检查我们的火箭和场景中的对象的交集。根据玩家击中的内容,我们会做出相应的反应。

我们将把这段代码放在我们的game目录中的一个文件中:

对于碰撞检测,我们唯一需要做的另一件事是添加一个短动画,当用户与对象碰撞时播放该动画。此函数将获取发生碰撞的位置并从该原点生成一些框。

完成的结果将如下所示。

为了实现这一点,我们必须在碰撞发生的地方创建一个圆圈中的盒子,并将它们向外设置动画,这样看起来它们就像从碰撞中爆炸一样。为此,让我们在文件中添加此功能:

这就是我们整理出来的碰撞检测,当物体被破坏时会有一个漂亮的动画。

随着场景的进行,我们希望在玩家的两侧添加一些悬崖,这样感觉就像他们的运动在某个空间内得到了适当的限制。我们使用模运算符在程序上将岩石添加到用户的右侧或左侧:

随着场景的进行,我们还希望将“挑战行”添加到场景中。这些是包含岩石、水晶或盾牌物品的物体。每次创建这些新行中的一个时,我们都会为每一行随机分配岩石、水晶和盾牌。

因此,在上面的示例中,单元格 1、2 和 4 没有添加任何内容,而单元格 3 和 5 分别添加了水晶和盾牌项目。

为了实现这一点,我们将这些挑战行分为五个不同的单元格。我们根据随机函数的输出在每个单元格中生成某个项目,如下所示:

可以在这些链接中的任何一个查看岩石、水晶和盾牌创建功能。

我们需要在渲染循环中完成的最后一件事是:

在我们的渲染函数结束时,我们可以添加以下代码来适应这个功能:

这就是我们的渲染循环完成了。

当人们加载我们的 游戏 时,他们会看到一些让他们能够开始玩的按钮。

这些只是简单的 HTML 元素,我们根据 游戏 中发生的情况以编程方式显示或隐藏它们。问题图标让玩家对 游戏 的内容有所了解,并包含有关如何玩 游戏 的说明。它还包括我们模型的(非常重要的!)许可证。

并且,按下红色按钮开始 游戏 。请注意,当我们点击红色的“播放”按钮时,摄像机会移动并旋转到火箭后面,让玩家准备好开始场景。

在我们的场景init函数中,我们将要执行此操作的事件注册到此按钮的onClick处理程序。要创建旋转和移动功能,我们需要执行以下操作:

为此,我们将在init函数中添加以下代码,如下所示:

当我们的关卡结束时,我们还必须连接我们的逻辑,并且可以在此处查看执行此操作的代码。

在 中创建 游戏 可以让你接触到数量惊人的潜在客户。由于人们可以在浏览器中玩 游戏 而无需下载或安装到他们的设备上,因此它成为开发和分发 游戏 的一种非常有吸引力的方式。

正如我们所见,为广泛的用户创造一种引人入胜且有趣的体验是非常有可能的。所以,唯一需要解决的是,你将在 中创建什么?

原文链接:

若对本页面资源感兴趣,请点击下方或右方图片,注册登录后

搜索本页相关的【资源名】【软件名】【功能词】或有关的关键词,即可找到您想要的资源

如有其他疑问,请咨询右下角【在线客服】,谢谢支持!

Three.js 从零到精通:掌握 3D 网络开发的艺术 (three.js要学多久) 第2张
发表评论
欢迎你第一次访问网站!