哈
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: texturevalue: 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 = trueconst clock = new THREE.Clock()
const render = () => {// 获取,自Clock对象创建以来,经过的秒数const elapsedTime = clock.getElapsedTime()// 色器中的uTime变量,就会随着时间的推移而更新rawShaderMaterial.uniforms.uTime.value = elapsedTimecontrols.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;