这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

hree.js 是一个基于 WebGL 的 JavaScript 3D 库,用于创建和渲染 3D 图形场景。

一、 图像渲染过程

1、webGL

webGL: WebGL 是一种基于 JavaScript API 的图形库,它允许在浏览器中进行高性能的 3D 图形渲染。webGL的渲染依赖于底层GPU的渲染能力。

通过获取<canvas>元素获取WebGL的上下文,从而获得WebGL API和GPU。

GPU 图形处理器:处理图形计算的硬件。GPU运行着一个着色器小程序。包含两种类型的着色器程序,顶点着色器片元着色器

2、着色器

着色器:

3、坐标系

(1)模型空间:物体在其自身坐标系下的位置、大小、方向。

(2)世界空间:所有模型都放置在同一坐标系下的空间。每个物体都有唯一的坐标系。如three.js的AxesHelper是基于世界空间创建的坐标系。

(3)视图空间:相机所在的坐标系。简单来说就是以相机为原点,物体在相机眼中的位置。

(4)投影空间:将3D图形投影到二维屏幕上的坐标系。将3D坐标转化为2D坐标。

各个坐标系之间的转换:通过矩阵变换来完成。例如,将物体从模型空间转换到世界空间,可以使用模型变换矩阵将局部坐标转换为全局坐标。将物体从世界空间转换到视图空间,可以使用相机变换矩阵将全局坐标变换为相机坐标。最后,将视图空间中的坐标投影到屏幕上,可以使用投影变换矩阵将相机坐标变换为裁剪坐标。通过这些矩阵变换,可以将坐标从一个空间转换到另一个空间,从而实现3D图形的渲染和显示。

4、GPU渲染过程

(1)渲染管线:就是将3D坐标转化为屏幕像素(屏幕都是二维的,也就是二维坐标)的过程。分为以下几个阶段。

应用阶段:由CPU控制,主要负责数据的准备和处理。CPU将数据发送的GPU,包括图形的顶点坐标、纹理坐标、颜色信息等 。

几何阶段:运行在GPU中。将顶点坐标变换到屏幕空间中。

光栅化阶段:阶段运行在GPU中。光栅化阶段主要将渲染图元转换为像素,并进行颜色插值、纹理采样等处理,最终输出渲染像素。

(2)GPU具体渲染过程。

齐次裁剪空间:简单来说就是相机视锥体的范围。如下图

二、着色器材质

three.js中有两个着色器材质ShaderMaterial和原始着色器材质RawShaderMaterial,它是用着色器语言GLSL编写的程序,可以让我们自定义物体的着色器程序,从而实现复杂的效果。 1、ShaderMaterial:材质接收两个着色器,顶点着色器和片元着色器。着色器代码需要我们自己编写,来实现复杂的效果。来看下如何使用。

用着色器材质实现下面这个效果:

搭建目录结构和基础看这里

1、首先搭建一个three.js场景
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>threejs_collision</title>
<link rel="stylesheet" href="./asstes/css/style.css">
</head>
<body>
<script src="./main/index.js" type="module"></script>
</body>
</html>
*{
padding: 0;
margin: 0;
}
body,html {
background: green;
}
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// 设置相机位置
camera.position.set(0, 0, 10);
//将相机添加到
scene.add(camera); //创建环境光,环境光会均匀的照亮场景中的所有物体。
const light = new THREE.AmbientLight(0x404040);
//将环境光添加到场景
scene.add(light);
// 创建平行光
const directionalLight = new THREE.DirectionalLight();
//设置光源位置
directionalLight.position.set(0, 5, 0);
//添加到场景
scene.add(directionalLight);
//设置光源投射阴影
directionalLight.castShadow= true // 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
//开启渲染器阴影计算
renderer.shadowMap.enabled = true
//将canvas添加到body中
document.body.appendChild(renderer.domElement); // 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 轨道控制器的阻尼感
controls.enableDamping = true;
//辅助坐标轴
const axesHelp = new THREE.AxesHelper();
scene.add(axesHelp); const clock = new THREE.Clock()
//渲染函数
function render() {
//阻尼
controls.update()
let time = clock.getDelta();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
// 初始化渲染函数
render();
// 监听浏览器窗口尺寸变化
window.addEventListener('resize',() => {
//重新设置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
//更新相机投影矩阵
camera.updateProjectionMatrix();
//重新设置渲染器尺寸
renderer.setSize(window.innerWidth,window.innerHeight);
//设置设备像素比
renderer.setPixelRatio(window.devicePixelRatio)
})
2、创建着色器材质

从上面动图可以看出,是一个平面贴了一张图,然后给这个平面加了wave的效果。
所以,先创建一个平面。

const planeGeometry = new THREE.PlaneGeometry(1,1,64,64);

引入贴图

const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load(
require('../asstes/img/texture/xx.jpeg')
)

创建一个shader文件夹存放着色器代码,新建一个两个.glsl文件,来写顶点着色器和片元着色器代码。如下:


在js文件中引入这两个着色器:

import vertexShaderText from '../shader/basic/vertex.glsl'
import fragmentShaderText from '../shader/basic/fragment.glsl'

创建着色器材质

const material = new THREE.ShaderMaterial({
// 顶点着色器
vertexShader:vertexShaderText,
// 片元着色器
fragmentShader:fragmentShaderText,
// 设置两面可见,默认只能看见一面
side:THREE.DoubleSide,
})
3、编写着色器代码

顶点着色器 使用的是着色器语言(GLSL),不会也没事,知道怎么接收参数,在哪写逻辑就够了。首先要有一个入口点,也就是下面的void main函数,对数据的处理就写在这里面。

shader中有三种类型的变量: uniforms, attributes, 和 varyings

uniforms:从应用程序(CPU)传到着色器的变量(GPU),顶点着色器和片元着色器都能访问,比如我们可以在ShaderMaterial传递uniforms,在着色器程序中接收。用法:接收:uniform float uTime;

attributes:与每个顶点关联的变量。例如,顶点位置,法线和顶点颜色都是存储在attributes中的数据。attributes  可以在顶点着色器中访问。用法:attribute vec3 position

Varyings:在顶点着色器和片元着色器中传递数据。可以将顶点着色器处理过的数据通过varyings传给片元着色器。用法:varying vec2 vUv;

// 高精度浮点数
precision highp float;
void main(){
// vec4 四维向量
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
// projectionMatrix 投影矩阵;viewMatrix 视图矩阵;modelMatrix 模型矩阵;跟上面提到的坐标系对应。这些都是内置的uniform,使用ShaderMaterial会自动到GLSL shader代码中。使用RawShaderMaterial不会自动添加,需要手动接收。
//gl_Position是一个内置变量,它表示经过投影、视图和模型变换后的顶点位置。
gl_Position = projectionMatrix * viewMatrix * modelPosition;
}

片元着色器:

// 中等精度浮点数
precision mediump float;
void main(){
// gl_FragColor内置对象,片元的颜色值 vec4是个思维变量这里代表了红色分量、绿色分量、蓝色分量和透明度分量。
gl_FragColor = vec4(1.0,1.0,0.0,1.0);
}

效果:注意这个平面的颜色是片元着色器里gl_FragColor对象决定的,现在是写死的(当然也可以写活)。

接下来给这个平面添加wave的效果:这个平面在X、y轴,通过改变Z轴的坐标来使平面有上下波动的效果,这个波动的效果像不像正弦余弦曲线,可以通过sin,cos实现这个效果。可以通过Uniforms变量将数据传给顶点着色器和片元着色器。

const clock = new THREE.Clock()
//渲染函数
function render() {
let time = clock.getElapsedTime()
material.uniforms.uTime.value = time;
} const material = new THREE.ShaderMaterial({
uniforms:{
uTime:{
value:0
},
// 贴图
uTexture:{
value: texture
}
}
})

顶点着色器程序:

precision mediump float;
uniform float uTime;
//varying:从顶点着色器传递到片元着色器的变量。 将uv传递到片元着色器。uv是二维坐标,是物体顶点在纹理上的映射位置(相当于将一个3维物体展开后的对应的二维位置)。传递给片元着色器可以读取该坐标处的颜色,赋值给gl_FragColor,实现贴图效果。
varying vec2 vUv;
void main(){
vUv = uv;
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
modelPosition.z = sin((modelPosition.x + uTime) * 10.0) * 0.05;
modelPosition.z += sin((modelPosition.y + uTime) * 10.0) * 0.05;
gl_Position = projectionMatrix * viewMatrix * modelPosition;
}
片元着色器程序:
precision mediump float;
// sampler2D类型的纹理变量
uniform sampler2D uTexture;
// 接收顶点着色器传来的uv
varying vec2 vUv;
void main(){
// texture2D是用于读取纹理颜色值的函数
vec4 textureColor = texture2D(uTexture,vUv);
gl_FragColor = textureColor; }

这样就是实现了以上效果。

如果是RawShaderMaterial材质,内置的uniform需要手动去接收,以上代码改成:
顶点着色器程序:

precision mediump float;
// 定义顶点
attribute vec3 position;
//定义位置参数
attribute vec2 uv;
// 传入投影矩阵
uniform mat4 projectionMatrix;
// 传入视图矩阵
uniform mat4 viewMatrix;
// 传入模型矩阵
uniform mat4 modelMatrix;
//接收着色器材质传递的时间参数
uniform float uTime;
// uv传递到片元着色器 varying是从顶点着色器传递到片元着色器的变量
varying vec2 vUv;
void main(){
vUv = uv;
vec4 modelPosition = modelMatrix * vec4(position,1.0);
modelPosition.z = sin((modelPosition.x + uTime) * 10.0) * 0.05;
modelPosition.z += sin((modelPosition.y + uTime) * 10.0) * 0.05;
gl_Position = projectionMatrix * viewMatrix * modelPosition;
}

三、着色器实现一个水波纹

水波纹相对于上面旗帜飘动的效果,多了些随机性。如水波的高度是变化的,波浪的起伏是随机的,高处和低处的颜色不一样,水波波动的大小、频率等。这里用到了一些随机函数。将这些随机性添加给波浪的高度来达到更真实的效果。下面定义了很多参数,这些参数可以自己去调节看看它们是什么作用。

const material = new THREE.ShaderMaterial({
vertexShader:vertexShaderText,
fragmentShader:fragmentShaderText,
side:THREE.DoubleSide,
uniforms:{
uTime:{
value:0
},
uWaresFrequency:{
value:params.uWaresFrequency
},
uScale:{
value:params.uScale
},
uNoiseFrequency:{
value:params.uNoiseFrequency
},
uNoiseScale:{
value: params.uNoiseScale
},
uXzScale:{
value: params.uXzScale
},
uLowColor:{
value:new THREE.Color(params.uLowColor)
},
uHighColor: {
value:new THREE.Color(params.uHighColor)
},
uOpacity:{
value:params.uOpacity
}
},
transparent: true
}) const plane = new THREE.Mesh(planeGeometry,material)
plane.rotation.x = -Math.PI / 2
scene.add(plane) // 将这些uniforms变量添加到gui在,方便看效果,找到最合适的值。
gui.add(params,'uWaresFrequency').min(1).max(50).step(0.1).onChange(val => {
material.uniforms.uWaresFrequency.value = val;
});
gui.add(params,'uScale').min(0).max(0.2).step(0.01).onChange(val => {
material.uniforms.uScale.value = val;
});
gui.add(params,'uNoiseFrequency').min(0).max(100).step(0.1).onChange(val => {
material.uniforms.uNoiseFrequency.value = val;
});
gui.add(params,'uNoiseScale').min(0).max(5).step(0.01).onChange(val => {
material.uniforms.uNoiseScale.value = val;
});
gui.add(params,'uXzScale').min(1).max(5).step(0.01).onChange(val => {
material.uniforms.uXzScale.value = val;
});
gui.addColor(params,'uLowColor').onFinishChange(val => {
material.uniforms.uLowColor.value = new THREE.Color(val)
})
gui.addColor(params,'uHighColor').onFinishChange(val => {
material.uniforms.uHighColor.value = new THREE.Color(val)
})
gui.add(params,'uOpacity').min(0).max(1).onChange(val => {
material.uniforms.uOpacity.value = val;
})

顶点着色器程序:里面的函数都是从这本书里抄的

uniform float uTime;
uniform float uWaresFrequency;
uniform float uScale;
uniform float uNoiseFrequency;
uniform float uNoiseScale;
uniform float uXzScale;
varying float vElevation; float random (vec2 st) {
return fract(sin(dot(st.xy,vec2(12.9898,78.233)))*43758.5453123);
}
// 旋转函数
vec2 rotate(vec2 uv, float rotation, vec2 mid)
{
return vec2(
cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x,
cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y
);
} // 2d噪声函数
float noise (in vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f*f*(3.0-2.0*f);
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
// 随机函数
vec4 permute(vec4 x)
{
return mod(((x*34.0)+1.0)*x, 289.0);
}
vec2 fade(vec2 t)
{
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
float cnoise(vec2 P)
{
vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);
vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);
Pi = mod(Pi, 289.0); // To avoid truncation effects in permutation
vec4 ix = Pi.xzxz;
vec4 iy = Pi.yyww;
vec4 fx = Pf.xzxz;
vec4 fy = Pf.yyww;
vec4 i = permute(permute(ix) + iy);
vec4 gx = 2.0 * fract(i * 0.0243902439) - 1.0; // 1/41 = 0.024...
vec4 gy = abs(gx) - 0.5;
vec4 tx = floor(gx + 0.5);
gx = gx - tx;
vec2 g00 = vec2(gx.x,gy.x);
vec2 g10 = vec2(gx.y,gy.y);
vec2 g01 = vec2(gx.z,gy.z);
vec2 g11 = vec2(gx.w,gy.w);
vec4 norm = 1.79284291400159 - 0.85373472095314 * vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11));
g00 *= norm.x;
g01 *= norm.y;
g10 *= norm.z;
g11 *= norm.w;
float n00 = dot(g00, vec2(fx.x, fy.x));
float n10 = dot(g10, vec2(fx.y, fy.y));
float n01 = dot(g01, vec2(fx.z, fy.z));
float n11 = dot(g11, vec2(fx.w, fy.w));
vec2 fade_xy = fade(Pf.xy);
vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);
float n_xy = mix(n_x.x, n_x.y, fade_xy.y);
return 2.3 * n_xy;
} void main() {
vec4 modelPosition = modelMatrix * vec4(position,1.0);
// 波浪高度
float elevation = sin(modelPosition.x * uWaresFrequency) * sin(modelPosition.z * uWaresFrequency * uXzScale);
elevation += cnoise(vec2(modelPosition.xz*uNoiseFrequency+uTime))
*uNoiseScale;
elevation *= uScale;
// 传到片元着色器
vElevation = elevation;
modelPosition.y += elevation;
gl_Position = projectionMatrix * viewMatrix * modelPosition;
}
片元着色器程序:
varying float vElevation;
uniform vec3 uLowColor;
uniform vec3 uHighColor;
uniform float uOpacity;
void main(){
float a = (vElevation + 1.0) / 2.0;
// 混合颜色
vec3 color = mix(uLowColor,uHighColor,a);
gl_FragColor = vec4(color,uOpacity);
}
最终效果(效果可以调节参数,调到自己满意的效果):

本文转载于:

https://juejin.cn/post/7248982532728864825

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--Threejs-着色器实现一个水波纹的更多相关文章

  1. Threejs着色器基本使用样例改造

    <!DOCTYPE html> <html lang="en"> <head> <title>three.js webgl - bu ...

  2. DirectX11--深入理解Effects11、使用着色器反射机制(Shader Reflection)实现一个复杂Effects框架

    前言 如果之前你是跟随本教程系列学习的话,应该能够初步了解Effects11(现FX11)的实现机制,并且可以编写一个简易的特效管理框架,但是随着特效种类的增多,要管理的着色器.资源等也随之变多.如果 ...

  3. Opengl_入门学习分享和记录_02_渲染管线(一)顶点着色器&片段着色器

    写在前面的废话:今天俺又来了哈哈,真的好棒棒! 今天的内容:之前我们大概描述了,我们自己定义的顶点坐标是如何被加载到GPU之中,并且介绍了顶点缓冲对象VBO用于管理这一块内存.今天开始详细分析它的具体 ...

  4. WebGL——水波纹特效

    大家好,今天我ccentry要做一个水波纹特效,我们来看看水波纹特效的做法.首先我们来看一下水波纹特效的效果是怎么样的,请看下图. 我们要做的就是类似这种纹理特效,那么我们来看看是如何制作的吧.首先鲫 ...

  5. stage3D基础二-----顶点和片段着色器(转)

    来源:http://www.adobe.com/cn/devnet/flashplayer/articles/vertex-fragment-shaders.html 本教程将介绍着色器.着色器是 S ...

  6. 三角函数之美-水波纹载入LoadingView

    一.前言 学习是要总结的.近期几天学习了画图相关的,可是使用的机会较少,如今又快要遗忘了,这次看了水波纹的绘制.认为十分有意思,还是 把实现的方法记录下来.技术无他,为手熟尔.还是要多练习,空淡误国, ...

  7. 片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但

    片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但片元着色器是一个更合适的名字, 因为此时的片元并不是一个真正意义上的像素.

  8. Unity3d 着色器语法(Shader)

    Shader "name" { [Properties] Subshaders [Fallback] } 定义了一个着色器.着色器拥有一个 Properties 的列表.着色器包含 ...

  9. OpenGL ES学习笔记(一)——基本用法、绘制流程与着色器编译

    首先声明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. 在Android.iOS等移动平台上 ...

  10. Unity 内置着色器(转)

    Unity包括超过40种内置的shader. 标准着色器家族 Normal Shader Family 这些着色器都是Unity基本的着色器.适用于大多数的不透明物体,如果想要物体有透明.发光效果等, ...

随机推荐

  1. RabbitMQ 使用细节 → 优先级队列与ACK超时

    开心一刻 今天坐在太阳下刷着手机 老妈走过来问我:这么好的天气,怎么没出去玩 我:我要是有钱,你都看不见我的影子 老妈:你就不知道带个碗,别要边玩? 我:...... 优先级队列 说到队列,相信大家一 ...

  2. JS leetcode 买卖股票的最佳时机 题解分析,我离职了。

    壹 ❀ 引 昨天下班后,还是找经理提出了辞职,没有犹豫的裸辞,今天与人事的对话不小心被后台的同事听到,一下在公司传开了,下午我与同事们多人对线,被他们的消息轰炸....没错,我真的要走了. 因为什么原 ...

  3. axios.delete传参,400错误

    我在使用axios.delete进行传参的时候,发现会报400错误 后端代码(C#) 前端代码 这样的参数请求会报400错误 后端就一个参数,前端发一个id为什么接受不到呢? 在网上找了半天,终于明白 ...

  4. CentOS7中搭建GitLab踩坑实录

    今晚闲来无事,尝试了下自己搭建一台git服务器,很多人可能不明白平时自己随手就可以提交代码,为什么还要自己搭建服务器呢?首先你有没有考虑过你是怎么能把代码提交上去的?如果公司突然有一天需要你来负责搭建 ...

  5. 【Unity3D】点选物体、框选物体、绘制外边框

    1 需求描述 ​ 绘制物体外框线条盒子 中介绍了绘制物体外框长方体的方法,本文将介绍物体投影到屏幕上的二维外框绘制方法. 点选物体:点击物体,可以选中物体,按住 Ctrl 追加选中,选中的物体设置为红 ...

  6. 基于zabbix的数据库查询各种监控数据

    select FROM_UNIXTIME(clock) as DateTime, value, round(value/1024,2) as Traffic_in from history_uint ...

  7. spring boot携手echarts实现双柱状图实战

    说明 最近做了个图书管理系统,里面有个模块是统计最近一周借书和还书的情况. 设计为柱状图模式展现,自然需要用到echarts. 实现效果 开发步骤 1.页面和JS <!DOCTYPE html& ...

  8. Flex 弹性盒子布局

    可以少去理解一些不必要的概念,而多去思考为什么会有这样的东西,它解决了什么问题,或者它的运行机制是什么? 1. 弹性盒子布局概念 Flex 是 Flexible Box 的缩写,意为"弹性布 ...

  9. 【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数

    问题描述 在Azure App Service for Windows的环境中,部署.NET应用,其中使用了 SAP NetWeaver RFC函数 (需要加载 sapnwrfc.dll).详细的错误 ...

  10. 了解 Docker 网络

    本章将会简单地讲述 Docker 中的网络,对于 CNM.Libnetwork 这些,限于笔者个人水平,将不会包含在内. Docker 的四种网络模式 Docker 有 bridge.none.hos ...