【 Threejs 】- Shader 着色器实例渲染教程

着色器在threejs中是一个难点,话不多说,先来看看着色器是什么?

如果您已经有使用计算机绘图的经验,您就会知道在这个过程中您先画一个圆,然后画一个矩形、一条线、一些三角形,直到您组成您想要的图像。这个过程与手写一封信或一本书非常相似——它是一组指令,一个接一个地完成任务。> 着色器也是一组指令,但这些指令是针对屏幕上的每个像素一次性执行的。这意味着您编写的代码必须根据像素在屏幕上的位置而有所不同。就像打字机一样,您的程序将作为接收位置并返回颜色的函数工作,并且在编译时运行速度非常快。

着色器的来源

那么好,说人话,着色器到底是什么?字面意思,给物品上色的(简单_

关于着色器(shader) 它是用GLSL(着色器语言)写的,执行shader的是GPU,我们可以把一部分GLSL的工作交给GPU来提升性能。这样就好理解了,着色器程序通常在计算机的图形处理单元 (GPU) 上运行,它们可以在其中并行运行。

GLSL官方解释为 :OpenGL Shading Language 也称作 GLslang,是一个以C语言为基础的高阶着色语言。它是由 OpenGL ARB 所建立,提供开发者对绘图管线更多的直接控制,而无需使用汇编语言或硬件规格语言。

  • 有些人就会问了,为什么使用着色器?
  • 答:因为它快啊!

为了回答这个问题,我介绍了并行处理的奇迹。

将你的计算机的 CPU 想象成一个大型工业管道,每项任务都是通过它的东西 - 就像一条工厂生产线。有些任务比其他任务更大,这意味着它们需要更多的时间和精力来处理。由于计算机的体系结构,作业被迫连续运行;每项工作必须一次完成一项。现代计算机通常有四个处理器组,它们像这些管道一样工作,一个接一个地完成任务以保持运行顺畅。每个管道也称为线程

【 Threejs 】- Shader 着色器实例渲染教程_第1张图片

视频游戏和其他图形应用程序比其他程序需要更多的处理能力。由于它们的图形内容,它们必须进行大量的逐像素操作。屏幕上的每个像素都需要计算,在 3D 游戏中,几何和透视也需要计算。

让我们回到管道和任务的比喻。屏幕上的每个像素代表一个简单的小任务。单独每个像素任务对 CPU 来说不是问题,但是(这就是问题所在)必须对屏幕上的每个像素完成小任务!这意味着在旧的 800x600 屏幕中,每帧必须处理 480,000 个像素,这意味着每秒要进行 14,400,000 次计算!是的!这是一个足以使微处理器过载的问题。在以每秒 60 帧的速度运行的现代 2880x1800 视网膜显示器中,该计算加起来达到每秒 311,040,000 次计算。图形工程师如何解决这个问题?

【 Threejs 】- Shader 着色器实例渲染教程_第2张图片

这是并行处理成为一个很好的解决方案的时候。与其拥有几个大而强大的微处理器或_管道_,不如让许多微型微处理器同时并行运行更聪明。这就是图形处理器单元 (GPU)。

【 Threejs 】- Shader 着色器实例渲染教程_第3张图片

将微型微处理器想象成一张管道表,将每个像素的数据想象成一个乒乓球。每秒 14,400,000 个乒乓球几乎可以阻塞任何管道。但是每秒接收 30 个 480,000 像素波的 800x600 微型管道表可以顺利处理。这在更高的分辨率下同样有效——你拥有的并行硬件越多,它可以管理的流就越大。

GPU 的另一个“超能力”是通过硬件加速的特殊数学函数,因此复杂的数学运算直接由微芯片而不是软件来解决。这意味着超快的三角函数和矩阵运算 - 与电流一样快。

痛并快乐着

下面将从一个实例来介绍着色器的使用,听咱给你细细道来~ 着色器的功能和拓展很丰富,如果你对UI和一些图形学感兴趣,那么你学shader就很容易上手,但对我们一些前端学习者们,依旧要迎难而上。不过首先你得对threejs有一定的了解,当然你可以参考我的上一篇文章里面也有对threejs的分享和一些整合:juejin.cn/post/716652…

案例分析

首先拿到图片,给我了三个信号:会动的 有颜色的 立体盒子,旋转好说,立体盒子更好说,有颜色嘛?好像也不难,那岂不是很easy!?

Action:

  • 在工作区新建一个文件夹 threejs_box 并cd进入该文件夹
  • npm init -y + npm i parcel-bundler + npm i three 初始化 并安装依赖
  • 在该文件夹下新建src文件下 并创建 main.js 和 index.html 文件
  • 最后在生成的package文件中的"scripts"里把原有的替换为
 "dev": "parcel src/index.html","build": "parcel build src/index.html" 

这样一个基础框架就搭好了 接下来index.html里

main.js中 创建threejs的三要素摄像机,场景,渲染器,以及加一个clock时钟。

import * as THREE from 'three';
let camera, scene, renderer, clock;
init();
animate();
function init() {// 绑定到DOMconst container = document.getElementById( 'container' );// 创建摄像机camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 3000 );camera.position.z = 4;// 创建场景scene = new THREE.Scene();// 创建时钟clock = new THREE.Clock();// 创建渲染器renderer = new THREE.WebGLRenderer();renderer.setPixelRatio( window.devicePixelRatio );container.appendChild( renderer.domElement );onWindowResize();window.addEventListener( 'resize', onWindowResize );
}
// 自适应屏幕
function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {requestAnimationFrame( animate );render();
}
function render() {renderer.render( scene, camera );
} 

接下来要引入着色器的使用

着色器的分类

shaders有两种,一个是vertex shaders(顶点着色器),另一个是fragment shaders(片元着色器)

顶点着色器
【 Threejs 】- Shader 着色器实例渲染教程_第4张图片

这样的script标签,浏览器不能识别,所以不会执行,我们需要通过threejs来执行

  • uniform float time , uniform变量可以在顶点着色器和片元着色器中共同使用,time 变量在运行的时候,是以毫秒为单位。
  • varing vec2 vUv , varing 变量是顶点着色器和片元着色器的接口,这里的vUv保存的是UV映射,存储每个顶点的位置关系,我们可以使用vUv在片元着色器中。
  • void main 函数,所有的着色器必须要有这个函数,在函数中可以把uv映射进行转换(转换顶点位置),最后,我们使用gl_position来实际改变每个顶点的位置,这个改变位置的通用公式是projectionMatrix * modelViewMatrix * vec4(newPosition , 1.0),使用乘积,如果没有这一步,GPU将不会识别我们对UV进行的操作(不会修改UV的位置)
片元着色器
【 Threejs 】- Shader 着色器实例渲染教程_第5张图片

前两个变量是不变的,需要记住的是,所有要用到的变量,都要写在每个着色器中(片元和顶点)

  • 在main函数中,我们通过uv和time 来计算颜色(颜色必须是正的)
  • gl_FragColor 来设置片元的颜色。
  • 这里只是设置好了顶点着色器和片元着色器,还需要在threejs中结合shaderMaterial材质进行设计
THree.shaderMaterial

先定义一个uniforms变量(只是个对象结构的变量)

【 Threejs 】- Shader 着色器实例渲染教程_第6张图片

定义shaderMaterial材质,使用了uniforms变量,还有两个定义的着色器!

  • 为什么要定义这个unifroms变量? 用于修改uv(顶点的位置),这里定义了time,修改time也是同样的效果。
【 Threejs 】- Shader 着色器实例渲染教程_第7张图片
使用总结

1.顶点着色器和片元着色器需要保持变量统一,uniform变量和varying变量
2.顶点着色器和片元着色器的都需要main函数
3.顶点着色器的实际修改是gl_Position,片元着色器的实际修改是gl_FragColor
4.浏览器不执行着色器语言,需要借助Threejs
5.Threejs使用ShaderMaterial来使用着色器,可以通过变量间接修改着色器内容!
6.着色器是通过GPU渲染的,不会占用CPU资源

好了接下来所学即所用,把这些枯燥的知识运用到代码当中。

  • main.js 完整代码
import * as THREE from 'three';

let camera, scene, renderer, clock;
let uniforms1,uniforms2;
init();
animate();function init() {const container = document.getElementById( 'container' );camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 3000 );camera.position.z = 4;scene = new THREE.Scene();clock = new THREE.Clock();// 构建基本的立体框大小const geometry = new THREE.BoxGeometry( 0.75, 0.75, 0.75 );// 创建第一个变量uniforms1 = {'time': { value: 1.0 }};// 创建第二个变量uniforms2 = {'time': { value: 1.0 },// 第二个变量与第一个不同的就在这里,添加了图片贴图'colorTexture': { value: new THREE.TextureLoader().load( '../texture/disturb.jpeg' ) }};// 把他的样式重复并且对称uniforms2[ 'colorTexture' ].value.wrapS = uniforms2[ 'colorTexture' ].value.wrapT = THREE.RepeatWrapping;// 创建数组来保存这四个立体框const params = [[ 'fragment_shader1', uniforms1 ],[ 'fragment_shader2', uniforms2 ],[ 'fragment_shader3', uniforms1 ],[ 'fragment_shader4', uniforms1 ]];// 循环输出 并渲染到页面for ( let i = 0; i < params.length; i ++ ) {// geometry已经在前面确定了 这里只需要定material就行了const material = new THREE.ShaderMaterial( {// 定义的uniforms 用于修改顶点位置uniforms: params[ i ][ 1 ],// 顶点着色器 绑定vertexShader: document.getElementById( 'vertexShader' ).textContent,// 片元着色器绑定fragmentShader: document.getElementById( params[ i ][ 0 ] ).textContent} );// 绑定geometry 和 materialconst mesh = new THREE.Mesh( geometry, material );// 设置位置mesh.position.x = i - ( params.length - 1 ) / 2;mesh.position.y = i % 2 - 0.5;// 添加到场景scene.add( mesh );}renderer = new THREE.WebGLRenderer();renderer.setPixelRatio( window.devicePixelRatio );container.appendChild( renderer.domElement );onWindowResize();window.addEventListener( 'resize', onWindowResize );}function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize( window.innerWidth, window.innerHeight );}function animate() {requestAnimationFrame( animate );render();}function render() {// 设置旋转周期时间 - 获得前后两次执行该方法的时间间隔const delta = clock.getDelta();uniforms1[ 'time' ].value += delta * 5;uniforms2[ 'time' ].value = clock.elapsedTime;for ( let i = 0; i < scene.children.length; i ++ ) {const object = scene.children[ i ];object.rotation.y += delta * 0.5 * ( i % 2 ? 1 : - 1 );object.rotation.x += delta * 0.5 * ( i % 2 ? - 1 : 1 );}// 将场景和摄像机渲染到页面renderer.render( scene, camera );} 
  • index.html


	
		three.js webgl - shader [Monjori]
		
		
	
	

		

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

你可能感兴趣的:(着色器,javascript,前端)