邂逅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 sizessizes.width = window.innerWidthsizes.height = window.innerHeight// Update cameracamera.aspect = sizes.width / sizes.height// 更新投影矩阵camera.updateProjectionMatrix()// Update rendererrenderer.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上的案例。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图象去噪1-使用中值滤波与均值滤波

1、中值滤波 使用中值滤波去除图像的异常像素点,使用cv2.cv2.medianBlur(img, 3)表示再图像在中值滤波窗口3*3的范围内,从下到大排序,将当前值替换为排序中值(如下图所示)将56替换为(56,66,90,…

JeecgFlow并行网关概念及案例演示

概念讲解 并行网关能够在一个流程中用于进行并发建模处理,将单条线路拆分成多条路径并行执行,或者将多条路径合并处理。 在一个流程模型中引入并发最直接的网关就是并行网关,它基于进入和外出顺序流,有分支和合并两种行为&#xf…

想拥有一个独一无二的AI人物?Lora炼丹训练模型教程来啦

之前答应过大家放出来的Lora本地训练教程,终于写好啦。 会训练lora,代表着你可以生成属于你的独一无二的角色。 你可以让这个角色在各种不同背景的地方出现,可以让它摆出各种姿势,满足你的无限幻想。 还有的商家,用…

10分钟安装好torch的GPU版本(Windows)

pytorch-gpu 1. 确定cuda版本2. 确定Python版本3 开始下载-cu118-cp383.1 下载cuda3.2 下载torchvision 4.下载好了5.开始安装6. 开始验证 1. 确定cuda版本 nvcc -V 版本为11.8 , 一会下载的版本为cu118 2. 确定Python版本 确定python版本为为3.8,一会下载为cp38 3 …

Java包介绍

今天看jdk文档,顺便写一下java几个包的作用。 java.applet 主要用于创建java applet小应用程序,可以嵌入到网页中能够呈现出特殊的效果,现在基本已经被废弃,很少使用。 java.awt AWT 是Abstract Window ToolKit (抽象窗口工具包…

猫头虎分享已解决Bug: Illegal State Exception: java.lang.IllegalStateException

猫头虎分享已解决Bug: Illegal State Exception: java.lang.IllegalStateException 🐯 摘要 📄 大家好,我是猫头虎,一名专注于后端技术的博主。在日常开发中,我们经常会遇到各种各样的Bug,其中Illegal St…

Python基础教程(二十七):urllib模块

💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 💝&#x1f49…

ssl证书90天过期?保姆级教程——使用acme.sh实现证书的自动续期

腾讯云相关文档相关参考-有的点不准确 前言 最近https到期了,想着手动更新一下https证书,结果发现证书现在的有效期只有90天,于是想找到一个自动更新证书的工具,发现了acme.sh,但是网上的文章质量参差不齐&#xff0…

数据结构——二分算法

二分查找 1. 在排序数组中查找元素的第一个和最后一个位置 代码实现&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/int binarySearch(int *nums, int numsSize, int target) {int l 0, r numsSize - 1; while (l <…

【面试题】前端 移动端自适应?_前端移动端适配面试题

设备像素比 设备像素比 (DevicePixelRatio) 指的是设备物理像素和逻辑像素的比例 。比如 iPhone6 的 DPR 是2。 设备像素比 物理像素 / 逻辑像素。可通过 window.devicePixelRatio 获取&#xff0c;CSS 媒体查询代码如下 media (-webkit-min-device-pixel-ratio: 3), (min-…

6.折半查找

折半查找 基本思路在有序表中,取中间元素作为比较对象,若给定值与中间元素的要查找的数相等,则查找成功;若给定值小于中间元素的要查找的数,则在中间元素的左半区继续查找;若给定值大于中间元素的要查找的数,则在中间元素的右半区继续查找。不断重复上述查找过 程,直到查找成功…

揭秘循环购:消费即收益,如何助力商家月销百万?

大家好&#xff0c;我是吴军&#xff0c;今天要和大家分享一种颠覆性的商业模式——循环购。你是否听说过“消费1000送2000”这样的促销活动&#xff1f;是不是觉得太不可思议&#xff0c;商家岂不是在“送钱”&#xff1f;别急&#xff0c;让我为你揭开这背后的秘密。 循环购&…

C和C++实现stack的对比

本篇文章&#xff0c;我们将对比C语言和C实现栈的不同来体会C的魅力&#xff01; 1.栈的介绍 栈&#xff08;Stack&#xff09;是一种常见的数据结构&#xff0c;它是一种特殊的线性表&#xff0c;只允许在一端进行数据的插入和删除操作。这一端通常被称为栈顶&#xff08;Top…

路由器ARP和ARP-proxy(华为)

#交换设备 路由器ARP和ARP-proxy(华为) 当一个广播域中的主机想要访问另外一个广播域的主机时&#xff0c;会广播ARP报文&#xff0c;询问目标IP地址所对应的MAC地址&#xff0c;默认情况下&#xff0c;arp记录是设备自动生成的&#xff0c;但是这样会容易受到ARP欺骗攻击&am…

python实训day4

1、查看数据库的版本 2、查看当前用户 3、查看当前数据库 4、计算表达式的结果; 任何一个数据库,无论大小,都首先是一个超级计算器 5、查看当前MySQL环境中所有的数据库; 系统数据库(只能看)和自定义数据库(任何操作) 6、先建数据库 gaoming 7、如果表已经存在,则创建不能成功 …

【ai】tx2 nx: jetson Triton Inference Server 运行YOLOv4

【ai】tx2 nx: jetson Triton Inference Server 部署YOLOv4 部署了服务端。需要对其测试【ai】tx2-nx 查看 jetpack 版本信息及对应的tritonserver【ai】tx2-nx:配置tritonserver2.17.0-jetpack4.6 环境并运行例子C++ Triton YoloV4 client 是基于 r21.05的 服务端的tensort 的…

springboot3 连接 oceanbase + logproxy数据同步到redis

我这用的是 社区版的 单机&#xff0c; rocky liunx 安装oceanbase 注意事项&#xff1a; logproxy 是 CDC 模式 &#xff0c; springboot 可以直接订阅 canal 是 binlog模式&#xff0c; canal 订阅 logproxy&#xff0c; springboot 订阅 canal logproxy 也可以转 bi…

嵌入式实验---实验七 SPI通信实验

一、实验目的 1、掌握STM32F103SPI通信程序设计流程&#xff1b; 2、熟悉STM32固件库的基本使用。 二、实验原理 1、使用STM32F103R6通过74HC595控制一位LID数码管&#xff0c;实现以下两个要求&#xff1a; &#xff08;1&#xff09;数码管从0到9循环显示&#xff1b; …

无人机巡检小羊仿真

详细视频地址 仿真效果 可视化三维仿真 gazebo物理仿真 px4 飞控仿真 仿qgc简易地面站 详细视频地址

计算机组成原理 | CPU子系统(1)基本概述

基本结构模型 运算与缓存部件 数据寄存部件 PSW不是很清楚 存储器是什么&#xff1f;属于那个结构里&#xff1f; 时序处理部件 cpu是大脑&#xff0c;控制器是神经元 ①通过硬件产生控制信号 ②通过软件产生控制信号 外频&#xff08;系统时钟信号&#xff09;&#xff0c;…