邂逅Three.js探秘图形世界之美

可能了解过three.js等大型的3D 图形库同学都知道啊,学习3D技术都需要有图形学、线性代数、webgl等基础知识,以前读书学的线性代数足够扎实的话听这节课也会更容易理解,这是shader课程,希望能帮助你理解着色器,也面向第一次了解threejs的同学。

本文相关文献资料:

  • three.js https://threejs.org/
  • Become a Three.js developer https://threejs-journey.com/
  • WebGL Shader 魔法指南:创意图形编程入门 https://juejin.cn/book/7267462574734573604?utm_source=course_list
  • 下雨特效 https://www.shadertoy.com/view/ltffzl
  • GAMES101-现代计算机图形学入门-闫令琪 https://www.bilibili.com/video/BV1X7411F744/?spm_id_from=333.999.0.0&vd_source=ae1012c48d1ebdad8a46df1d056238b9
  • -UP主汉语配音-【线性代数的本质】合集-转载于3Blue1Brown官方双语】https://www.bilibili.com/video/BV1ib411t7YR/?spm_id_from=333.999.0.0&vd_source=ae1012c48d1ebdad8a46df1d056238b9
  • 【双语字幕】什么是仿射变换?https://www.bilibili.com/video/BV1254y1h7R7/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click&vd_source=ae1012c48d1ebdad8a46df1d056238b9
  • 图形学:MVP变换概述 https://zhuanlan.zhihu.com/p/551648397

学习Three.js有啥用?

  • 工作上的可视化智慧小区、园区的建筑模型,包括外观、内部布局和房间分配等。

  • 元宇宙交互式导览 用户可以在元宇宙内自由探索,虚拟世界!VR和AR等技术应用。

  • 个人上的学习 Three.js 可以让你在 Web 上轻松地创建出令人惊叹的 3D 图形和交互体验,为你的项目添加更多视觉上的吸引力和创造力。
    经典案例:https://lusion.co/

让我们探秘数学的魅力,了解优雅的图形学,让枯燥无味的数字渲染出绚丽多彩的3D世界,了解底层的着色器原理是如此的精彩绝伦,今天的文章就让我们从底层开始揭秘优雅的three.js的面纱吧!

three.js的介绍和特点

Three.js 非常庞大,你可以用它做很多的事情。我们将学习所有基础知识,例如创建第一个场景、渲染、添加对象、选择正确的材料、添加纹理、为所有内容制作动画、添加光和阴影,甚至有些人可能会觉得这部分有点无聊,因为都是一些API的讲解。刚体(物理physic)很重要,可以看我之前发的文章

还有blender帮助我们导入导出模型和自己建模(有些偏离three的课题,但是真的很酷)

元神启动!!!!

tutieshi_640x432_20s.gif
但是篇幅不够了,所以后半段我选择给大家着重讲一讲底层的原理,大名鼎鼎的“着色器”,这是大家开始觉得学习困难的地方,并且有充分的理由。着色器很难,但着色器将释放 WebGL 的真正力量

hello!three.js

铺垫了这么久,我们直接进入正题吧~!
郭隆邦安装three.js教程:
http://www.webgl3d.cn/pages/cd35b2/
安装glsl环境:

要添加语法着色,如果您使用的是 VSCode,请转到您的插件,搜索shader并安装该Shader languages support for VS Code插件。如果您使用其他代码编辑器,请寻找兼容的插件并关注流行度和评论。

three四要素

这是最简单最基础的渲染three的方式,所以我们花时间讲一下,本文还会出现动画,自适应尺寸,debug UI调试界面等看下注释就懂了。

场景

import * as THREE from 'three'

// Scene 场景就像一个容器。我们将对象、模型、粒子、灯光等放入其中,并在某个时候要求 Three.js 渲染该场景。
const scene = new THREE.Scene()

网格

three内置有许多种几何体和材料的类型,但我们今天先简单的创建一个BoxGeometry和一个MeshBasicMaterial。

// Object 
// 形状 参数:长宽高
const geometry = new THREE.BoxGeometry(1, 1, 1)
// 材质 参数: 颜色
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
// 网格是几何体(形状)和材质的组合。
const mesh = new THREE.Mesh(geometry, material)

// 如果不向scene场景添加mesh对象,那么这个对象就无法渲染了。
scene.add(mesh)

相机

// Sizes
const sizes = {
  width: 800,
  height: 600
}

// Camera 相机不可见。这更像是一种理论观点。当我们对场景进行渲染时,将从该摄像机的视觉角度进行渲染。(mvp会讲怎么做到的)
// 参数一:视野
// 参数二:纵横比
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height)
scene.add(camera)

渲染器

// Canvas
const canvas = document.querySelector('canvas.webgl')

// ...

// Renderer 渲染器的工作是进行渲染
const renderer = new THREE.WebGLRenderer({
  canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.render(scene, camera)

一个完整的项目代码

直接看注释就懂了,用法很简单

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'lil-gui'

/**
 * Base
 */
// Debug
const gui = new dat.GUI({ width: 340 })

// Canvas
const canvas = document.querySelector('canvas.webgl')

// Scene
// 创建场景
const scene = new THREE.Scene()

/**
 * Object
 */
// Geometry
// 创建平面几何体
const geometry = new THREE.PlaneGeometry(2, 2, 128, 128)

// Material
// 创建基础材质
const material = new THREE.MeshBasicMaterial()

// Mesh
// 创建网格
const mesh = new THREE.Mesh(geometry, material)
// 添加到场景上(很重要)
scene.add(mesh)

/**
 * Sizes
 */
// 获取用户浏览器宽高
const sizes = {
  width: window.innerWidth,
  height: window.innerHeight
}

// 监听屏幕缩放事件
window.addEventListener('resize', () =>
  {
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    // 更新投影矩阵
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    // 更新像素比
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  })

/**
 * Camera
 */
// Base camera
// 创建投影相机
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
// 相机默认在原点,所以要设置位置,否则就和mesh重合了
camera.position.set(1, 1, 1)
// 别忘了把相机添加到场景上!
scene.add(camera)

// Controls
// 轨道控制器
const controls = new OrbitControls(camera, canvas)
// 开启阻尼效果
controls.enableDamping = true

/**
 * Renderer
 */
// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  canvas: canvas
})
// 设置渲染器的大小
renderer.setSize(sizes.width, sizes.height)
// 设置渲染器的像素比
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

/**
 * Animate
 */
// 获取时间的类
const clock = new THREE.Clock()

const tick = () =>
  {
    // 获取当前经过的时间
    const elapsedTime = clock.getElapsedTime()

    // Update controls
    // 更新控制器
    controls.update()

    // Render
    // 重新渲染场景和相机
    renderer.render(scene, camera)

    // Call tick again on the next frame
    // 递归调用自身,无限循环钩子
    window.requestAnimationFrame(tick)
  }

tick()

Shader 的魅力

这是最值得期待的一部分。我们上半节课已经讨论过着色器,所以大家可能会好奇它究竟是用来干什么的?
着色器是用 GLSL 编写的发送到 GPU 的程序。
看看着色器能做些什么:
https://zero.tech/
https://homunculus.jp/
Shader本身固然十分强大,但学起来也是相当有难度的。
一方面,它代码的核心就是计算,这涉及到了大量的数学和线性代数的知识,非常抽象;另一方面,它没有跟传统编程语言类似的调试工具,想要了解变量值的变化,只能通过观察画面的输出,对于初学者来说并不是很友好,这就是原生 WebGL 的学习如此困难的原因。

什么是 WebGL?

WebGL 是一种 JavaScript API,可以以惊人的速度在画布中绘制三角形。它与大多数现代浏览器兼容,而且速度很快,因为它是直接操作使用我们的图形处理单元 (GPU)。GPU 可以进行数千次并行计算。想象一下,想要渲染一个 3D 模型,而这个模型由 900 个三角形组成——仔细想想,这并不多。每个三角形包括 3 个点。当我们想要渲染我们的模型时,GPU 将不得不计算这 2700 个点的位置。因为 GPU 可以进行并行计算,所以它会在一个原始数据中处理所有的三角形点。
tutieshi_640x385_10s.gif

image.png

GLSL语言介绍

我们简单介绍一下GLSL语言!
用于编码着色器的语言称为 GLSL,代表 OpenGL 着色语言。很接近C语言。让我们了解其语法的基础知识。(学过c语言的有福了)

float fooBar = 0.123; // 浮点
int foo = 123; // 整数
bool foo = true; // 布尔

// 函数
float loremIpsum()
{
  float a = 1.0;
  float b = 2.0;

  return a + b;
}

GLSL内置了很多经典的函数如也有非常实用的函数。

向量 vector

向量(也叫矢量)(vector),具有大小和方向的量。向量可以理解为是空间中的箭头。
基向量(basis vectors)
我们可以认为任何向量都是由2个基向量通过伸缩得到的,比如 向量v[-5,2] 可以由 基向量i[1,0] 向左伸缩5倍,基向量j[0,1]向上伸缩2倍得到
即 v = ai + bj = -5i + 2j
image.png
我们可以选择不同的基向量来获取一个合理的不同的坐标系
要注意一点,每当我们用数字描述一个向量时,都是基于基向量的
齐次坐标
vec4可以是一个齐次坐标(x, y, z,w)即为x/w, y/w, z/w),由此齐次坐标有规模不变性,还可以表示无穷远处的点。
在计算机图形学中,齐次坐标是一种扩展了传统的笛卡尔坐标系的表示方法,它包含了额外的一个分量’w’。
我们思考这样一个问题:两条平行线可以相交吗?
但是齐次坐标坐标中结果是不一样的,试想一条铁轨:
image.png
可以发现,在无穷远处,两条铁轨相交汇合为一点!
齐次坐标就是将一个原本是n维的向量用一个n+1维向量来表示。

image.png

向量加法:

向量乘法:

矩阵 matrix

线性变换(linear transformation),其实也可以理解成函数处理,该函数接收一个向量,经过处理后输出另一个向量。
在空间里,一个向量可以通过移动得到另一个向量
线性变换可以理解为原始的时候在xy坐标系中,是一个个正方形表格,通过线性变换后,这些表格线还是保持平行的且等距分布。
**矩阵(matrix)**代表一个特定的线性变换,矩阵跟向量的乘积就是将线性变换作用于这个向量

看视频更直观的了解一下:
https://www.bilibili.com/video/BV1ib411t7YR/?p=5&vd_source=ae1012c48d1ebdad8a46df1d056238b9
三维矩阵乘法

了解MVP变换

image.png
了解到了线性代数的一些基础知识,我们开始讲解MVP变换
MVP变换,就是Model模型、View观察、Projection投影变换三个单词的缩写。
我们先拆分一下这三个矩阵来认识了解这三个矩阵

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

attribute vec3 position;

void main()
{
  vec4 modelPosition = modelMatrix * vec4(position, 1.0); 
  vec4 viewPosition = viewMatrix * modelPosition;
  vec4 projectedPosition = projectionMatrix * viewPosition;

  gl_Position = projectedPosition;
}

仿射变换

https://www.bilibili.com/video/BV1254y1h7R7/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click&vd_source=ae1012c48d1ebdad8a46df1d056238b9

MVP变换

我们已经了解到了仿射变换,接下来了解模型矩阵就比较好理解了看下面

模型矩阵

根据线代的知识,对于三维空间中的一个点进行平移,可以将坐标乘上一个平移矩阵,那么想让一个小盒子进行平移,则对其所有顶点都乘上一个平移矩阵,使其所有顶点都进行平移。
同理,想让一个小盒子进行大小的放缩,让其顶点都成上一个放缩矩阵即可。
除了平移和放缩,变换还包括旋转,在三维空间中,绕哪个轴进行旋转,都有不同的公式。具体的公式由极坐标即可较容易推导出。具体推导过程以及绕Y轴旋转的特殊性可以看:
将上述三中类型的矩阵作用在一起,即可得到模型变换矩阵,要注意**矩阵的顺序是从右到左作用到局部空间中的顶点上的。**即先进行缩放、旋转后,再进行平移。
后面的视图矩阵、投影矩阵不是我们本节课的重点,所以不会过多讲解了,感兴趣可以自行去了解,这两个矩阵也更复杂。可以参考课程GAMES101-现代计算机图形学入门-闫令琪https://www.bilibili.com/video/BV1X7411F744/?spm_id_from=333.999.0.0&vd_source=ae1012c48d1ebdad8a46df1d056238b9】

让我们开始创建第一个shader吧!

顶点着色器

创建vertex.glsl

uniform mat4 projectionMatrix; //透视矩阵
uniform mat4 viewMatrix; // 视图矩阵
uniform mat4 modelMatrix; // 模型矩阵

attribute vec3 position; // 传入的顶点坐标

void main()
{   
  gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
}

我们最重要的就是要知道这三个变量具体的含义projectionMatrix * viewMatrix * modelMatrix (mvp变换)

片段着色器

片段着色器的目的是为几何体的每个可见片段着色。
创建fragment.glsl

void main(){ 
    gl_FragColor = vec4(0.5, 0.8, 1.0, 1.0); 
}

我们只要操作顶点着色器和片段着色器即可,不用理会图元和光栅化。

//首先替换我们的material
const material = new THREE.RawShaderMaterial({
    vertexShader: testVertexShader,
    fragmentShader: testFragmentShader
})

创建着色器glsl文件并且导入

import testVertexShader from './shaders/test/vertex.glsl'
import testFragmentShader from './shaders/test/fragment.glsl'

这时候我们可以看到页面上出现了一个蓝色的平面了。

入门课程就到这里结束了,这些基础知识希望可以帮助你在学习threejs的过程中走的更远!
下集分享 - shader实操和shader-toy上的案例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/740581.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

红队内网攻防渗透:内网渗透之内网对抗:横向移动篇域控系统提权NetLogonADCSPACKDC永恒之蓝CVE漏洞

红队内网攻防渗透 1. 内网横向移动1.1 横向移动-域控提权-CVE-2020-1472 NetLogon1.2 横向移动-域控提权-CVE-2021-422871.3 横向移动-域控提权-CVE-2022-269231.4 横向移动-系统漏洞-CVE-2017-01461.5 横向移动-域控提权-CVE-2014-63241. 内网横向移动 1、横向移动-域控提权-…

Excel 宏录制与VBA编程 —— 11、工作表及工作簿操作(附:Worksheets与Sheets区别)

代码1 - Worksheets与Sheets区别 Worksheets表示普通工作表;Sheets即可表示普通工作表也可表示图标工作表。 下面模块中代码结果是一样的,大家理解时可结合上面区别说明进行了解 Sub Test()Worksheets("Sheet1").Range("A1").Value 100Sheets("Sheet…

python桌面应用

py文件 import osimport wx import wx.html2class MyFrame(wx.Frame):def __init__(self, parent):wx.Frame.__init__(self, parent, title"启动啦", size(1000, 700))# 创建一个Web视图组件self.browser wx.html2.WebView.New(self)# 加载本地HTML文件# self.brow…

Python重拾

1.Python标识符规则 字母,下划线,数字;数字不开头;大小写区分;不能用保留字(关键字) 2.保留字有哪些 import keyword print(keyword.kwlist)[False, None, True, and,as, assert, async, await…

在 The Sandbox 体验韩剧《碰撞搜查线》的刺激!

风靡全球的韩国电视剧《碰撞搜查线》现已登陆 The Sandbox 元宇宙! ASTORY 的电视剧《碰撞搜查线》以充满动作喜剧色彩的方式,讲述了一个交通犯罪调查小组打击公路上的恶棍的故事。该剧迅速成为 Disney 最受欢迎的节目之一! 在 The Sandbox体…

CSS阴影优化气泡框样式

<body> <div class"pop">气泡框</div> </body>body{display: flex;justify-content: center;align-items: center;height: 100% } .pop{display: flex;justify-content: center;align-items: center;background: #409eff;width: 150px;heigh…

python+unity实现数字人跟随运动

效果如下 设计思路 1 python通过摄像头提取人物肢体关键点信息 2 通过UDP将获取到人体信息发送给Unity 3 unity将获取的的人物信息进行解析 4 将解析的数据赋值给模型骨架 代码获取

GNSS接收机的工作原理

GNSS接收机的工作原理如下&#xff1a; 信号接收&#xff1a;GNSS接收机通过天线接收来自卫星导航系统的信号&#xff0c;这些信号包含卫星的位置、时间和健康状态等信息。 信号处理&#xff1a;接收的信号首先经过前置放大器放大&#xff0c;然后经过滤波器滤除噪声。接收机会…

iptables(6)扩展匹配条件--tcp-flags、icmp

简介 前面我们已经介绍了不少的扩展模块,例如multiport、iprange、string、time、connlimit模块,但是在tcp扩展模块中只介绍了tcp扩展模块中的”--sport”与--dport”选项,并没有介绍”--tcp-flags”选项,那么这篇文章,我们就来认识一下tcp扩展模块中的”--tcp-flags”和i…

【linux kernel】一文总结linux输入子系统

文章目录 一、导读二、重要数据数据结构&#xff08;2-1&#xff09;struct input_dev&#xff08;2-2&#xff09;input_dev_list和input_handler_list&#xff08;2-3&#xff09;struct input_handler 三、input核心的初始化四、常用API五、输入设备驱动开发总结(1)查看输入…

Linux:基础IO(三.软硬链接、动态库和静态库、动精态库的制作和加载)

上次介绍了基础IO&#xff08;二&#xff09;&#xff1a;Linux&#xff1a;基础IO&#xff08;二.缓冲区、模拟一下缓冲区、详细讲解文件系统&#xff09; 文章目录 1.软硬链接1.1硬链接1.2软链接使用场景 2.动态库和静态库1.1回顾1.2静态库的制作和使用为什么要有库制作者角度…

搜维尔科技:「案例」NBA新科冠军与Xsens运动捕捉的缘分

北京时间昨日&#xff0c;凯尔特人在主场106比88击败独行侠&#xff0c;以总比分4比1获胜&#xff0c;夺得队史第18冠&#xff0c;超越湖人队&#xff08;17冠&#xff09;成为历史上夺冠次数最多的球队。凯尔特人队上一次夺冠还是在2007-2008赛季。 凯尔特人队主力Jayson Tat…

Kotlin明明很优秀,为啥没像Java那样火?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「java的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;因为 kotlin 优化一件事&…

太全了吧?CISP全类别详细介绍,看完不迷惑

今天聊聊CISP&#xff0c;注册信息安全专业人员证。 很多人以为说CISP就是个证书&#xff0c;没这么简单&#xff0c;这里面区别可大了。 CISP根据工作领域和实际岗位需要&#xff0c;分为综合型、攻防领域、IT审计、软件开发、数据治理、电子取证和云安全领域等17项证书。 这么…

基于Openmv的追小球的云台

介绍 在这篇文章&#xff0c;我会先介绍需要用到且需要注意的函数&#xff0c;之后再给出整体代码 在追小球的云台中&#xff0c;比较重要的部分就是云台&#xff08;实质上就是舵机&#xff09;的控制以及对识别的色块位置进行处理得到相应信息后控制云台进行运动 1、舵机模…

LeetCode.32最长有效括号详解

问题描述 给你一个只包含 ( 和 ) 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 解题思路1 有效的括号字符串意味着每一个左括号 ( 都可以找到一个相匹配的右括号 )。栈可以帮助我们追踪尚未匹配的括号&#xff0c;并有效地处理…

推荐一款AI修图工具,支持AI去水印,AI重绘,AI抠图...

不知道大家有没有这样的一个痛点&#xff0c;发现了一张不错的“素材”&#xff0c; 但是有水印&#xff0c;因此不能采用&#xff0c;但找来找去&#xff0c;还是觉得初见的那个素材不错&#xff0c;怎么办&#xff1f; 自己先办法呗。 二师兄发现了一款功能强大的AI修图工具…

猫头虎 AI 前沿科技探索之路(持续更新):ChatGPT/GPT-4 科研应用、论文写作、数据分析与 AI 绘图及文生视频实战全攻略

猫头虎 AI 前沿科技探索之路(持续更新)&#xff1a;ChatGPT/GPT-4 科研应用、论文写作、数据分析与 AI 绘图及文生视频实战全攻略 背景介绍 随着人工智能技术的飞速发展&#xff0c;AI 的应用已经渗透到各个领域&#xff0c;从商业决策到医疗健康&#xff0c;再到日常生活中的…

win10改远程桌面端口,Windows 10 修改远程桌面端口号的专业指南

在Windows 10系统中&#xff0c;远程桌面&#xff08;Remote Desktop&#xff09;功能允许用户从一台计算机远程访问和控制另一台计算机。为了增加远程连接的安全性&#xff0c;减少潜在的安全风险&#xff0c;修改默认的远程桌面端口号是一个常见的安全措施。以下是在Windows …

postman教程-22-Newman结合Jenkins执行自动化测试

上一小节我们学习了Postman Newman运行集合生成测试报告的方法&#xff0c;本小节我们讲解一下Postman Newman结合Jenkins执行自动化测试的方法。 在软件开发过程中&#xff0c;持续集成&#xff08;CI&#xff09;是一种实践&#xff0c;旨在通过自动化的测试和构建过程来频繁…