哈
1、完整的动态波纹效果吧
main.js
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 顶点着色器
import basicVertexShader from './shader/11-01/raw/vertex.glsl?raw'
// 片元着色器
import basicFragmentShader from './shader/11-01/raw/fragment.glsl?raw'
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 700)
camera.position.set(0, 0, 10)
// const axesHelper = new THREE.AxesHelper(5)
// scene.add(axesHelper)
// --------------------------------------------------------------------
/*
宽度,
高度,
宽度分段数:它决定了平面在宽度方向上被分割成多少个小矩形(或更准确地说,是顶点网格的宽度分辨率)。
更高的值会创建更平滑的曲线(虽然对于平面来说,这主要体现在边缘的圆形或平滑处理上,如果有的话),但也会增加渲染的顶点和面数。
高度分段数:与宽度分段数类似,它决定了平面在高度方向上被分割成多少个小矩形。
*/
const planeGeometry = new THREE.PlaneGeometry(1, 1, 64, 64)
// 普通材质
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00
})
// 着色器材质
// const shaderMaterial = new THREE.ShaderMaterial({
// vertexShader: `
// void main(){
// gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
// }
// `,
// fragmentShader: `
// void main(){
// gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
// }
// `
// })
const textureLoader = new THREE.TextureLoader()
const texture = textureLoader.load('../public/assets/texture/ca.jpeg')
/*
原始着色器材质,
*/
const rawShaderMaterial = new THREE.RawShaderMaterial({
vertexShader: basicVertexShader, // 顶点着色器的GLSL代码(一个字符串)
fragmentShader: basicFragmentShader, // 片元着色器的GLSL代码(一个字符串)
side: THREE.DoubleSide,
`uniforms:一个对象,包含了在着色器代码中使用的全局变量,这些变量可以在JS中设置,并在着色器中被访问和修改。`
uniforms: {
`uTime:用于在着色器中模拟时间,其value属性被初始化为0,但可以在JS中随时间更新,以在着色器中创建【动态效果】。`
uTime: {
value: 0
},
`uTexture:用于将纹理传递给着色器`
uTexture: {
// value: texture
value: null
}
}
})
const plane = new THREE.Mesh(planeGeometry, rawShaderMaterial)
scene.add(plane)
// --------------------------------------------------------------------
const renderer = new THREE.WebGLRenderer({
antialias: true
})
renderer.shadowMap.enabled = true
renderer.toneMapping = THREE.ReinhardToneMapping
renderer.toneMappingExposure = 1
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
const clock = new THREE.Clock()
const render = () => {
// 获取,自Clock对象创建以来,经过的秒数
const elapsedTime = clock.getElapsedTime()
// 色器中的uTime变量,就会随着时间的推移而更新
rawShaderMaterial.uniforms.uTime.value = elapsedTime
controls.update()
requestAnimationFrame(render)
renderer.render(scene, camera)
}
render()
window.addEventListener('resize', () => {
// 重置相机的宽高比
camera.aspect = window.innerWidth / window.innerHeight
// 更新相机的投影矩阵
camera.updateProjectionMatrix()
// 重置渲染器的宽高比
renderer.setSize(window.innerWidth, window.innerHeight)
// 更新渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio)
})
vertex.glsl
/*
precision关键字:设置着色器中使用的浮点数精度为lowp(低精度),有助于在不影响视觉效果的情况下提高渲染性能
precision lowp float; // 后续,所有浮点数的精度,为lowp(低精度:-2^8 - 2^8)
precision lowp mediump; // 后续,所有浮点数的精度,为mediump(中精度:-2^10 - 2^10)
precision lowp highp; // 后续,所有浮点数的精度,为highp(高精度:-2^16 - 2^16)*/
precision lowp float;
/*
attribute:在顶点着色器中声明变量(在较新的GLSL版本中,attribute已经被in关键字所取代)
vec3:一个数据类型,代表一个三维向量 */
attribute vec3 position;
attribute vec2 uv; // uv:每个顶点的纹理坐标(u, v)
// 模型空间 -> 世界空间 -> 观察空间 -> 裁剪空间,然后,最终映射到屏幕坐标上,这是渲染管线中的标准变换顺序
uniform mat4 modelMatrix; // modelMatrix模型矩阵:用于将顶点,从模型空间 -> 世界空间
uniform mat4 viewMatrix; // viewMatrix视图矩阵:用于将顶点,从世界空间 -> 观察空间
uniform mat4 projectionMatrix; // projectionMatrix投影矩阵:用于将顶点,从观察空间 -> 裁剪空间,并最终映射到屏幕坐标上
// 获取时间
uniform float uTime; // uTime:一个统一变量,用于传递时间信息,可以用于动画效果
varying vec2 vUv; // vUv:传递给片段着色器的纹理坐标
varying float vElevation;
void main() {
// 将输入的纹理坐标传递给片段着色器
vUv = uv;
// 顶点着色器处理的每个顶点,都有一个位置(position),这个位置是一个三维向量(vec3),包含x、y、z三个坐标
// 用于,将顶点的位置,从模型空间啊 -> 世界空间
vec4 modelPosition = modelMatrix*vec4(position, 1.0);
// modelPosition.x += 1.0;
// modelPosition.z += 1.0;
// modelPosition.z += modelPosition.x;
`
1、
通过,正弦函数sin,动态的调整,顶点的z坐标,创建一种基于时间和顶点位置的波动效果
乘以0.05:基于顶点x坐标和时间的正弦波值,并将其幅度缩放为原始值的5%,
意味着,
随着uTime(时间)的增加,顶点的z坐标,将根据其在x轴上的位置以正弦波的形式变化。
2、
sin((modelPosition.x + uTime) * 10.0),为什么乘以10呢?
因为,正弦函数sin的周期是2π,
意味着,它完成一个完整的波形(从0到1,再到0)需要2π个单位
但是,
对(modelPosition.x + uTime)乘以10之后,
实际上,是在对,正弦函数sin的周期,进行缩放,
使得原本需要2π单位才能完成的波形,现在只需要(2π / 10) = 0.2π单位,就能完成一次完整的周期,
所以,乘以10.0,
意味着,
波形在相同的空间或时间范围内,完成了更多的周期,从而增加了波形的频率`
modelPosition.z = sin((modelPosition.x+uTime)*10.0)*0.05;
modelPosition.z += sin((modelPosition.y+uTime)*10.0)*0.05;
vElevation = modelPosition.z; // 将调整后的z值传递给片段着色器
`计算顶点在裁剪空间中的位置,gl_Position,是GLSL内置的输出变量,用于存储顶点的最终位置`
gl_Position = projectionMatrix*viewMatrix*modelPosition;
}
fragment.glsl
precision lowp float; // 设置着色器中使用的浮点数精度为lowp(低精度),有助于在不影响视觉效果的情况下提高渲染性能
varying vec2 vUv; // 顶点着色器传过来的,表示,每个片段(像素)在纹理图像上的UV坐标
varying float vElevation; // 顶点着色器传过来的,表示,每个顶点的高度信息,这里用它来影响片段的颜色
uniform sampler2D uTexture; // 这是一个统一变量,指向一个二维纹理图像
void main() {
// gl_FragColor = vec4(vUv, 0.0, 1.0);
// float height = vElevation + 0.05 * 10.0;
// gl_FragColor = vec4(1.0*height,0.0, 0.0, 1.0);
/* 使用texture2D()函数,根据UV坐标,从uTexture纹理中采样颜色,
texture2D()函数,返回的是一个包含RGBA四个分量的vec4向量。 */
vec4 textureColor = texture2D(uTexture, vUv);
// 根据UV,取出对应的颜色
float height = vElevation+(0.05*20.0); `将顶点的高度vElevation,增加了一个固定值`
`将采样得到的纹理颜色(仅RGB部分)与计算出的高度值相乘,
意味着,
高度越高,纹理颜色会越亮(或越深,取决于纹理颜色的初始亮度)
这是一个简单的,颜色调制过程,
用于,根据高度信息,改变纹理的视觉效果。`
textureColor.rgb *= height;
`gl_FragColor:这是GLSL内置的输出变量,用于存储片元的最终颜色,将用于渲染到屏幕上`
gl_FragColor = textureColor;
}
效果图(图是动态的)
2、modelPosition.z = sin((modelPosition.x+uTime)*10.0)*0.05;