最终效果
先看下这次代码最终要实现的效果,
效果分析:
要实现上述效果,我们需要两张图片,作为纹理贴图,使其图案产生明暗效果;然后通过定义ShaderMaterial对象通过自定义Shader实现上述效果;后面代码中会进行详细分析;
这里我们先介绍下基础知识
什么是 Shader
Shader(着色器)是一种在图形处理单元(GPU)上执行的程序,它定义了如何根据输入数据(例如顶点位置,纹理坐标等)计算出各个像素的颜色。shader 主要包含两种类型,分别为顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)。
顶点着色器处理顶点数据,例如坐标、法线、纹理坐标等,并对每个顶点进行分析、转换和计算。然后将这些处理过的数据传递给片元着色器进行下一步的计算。
片元着色器则处理每个像素的数据,包括颜色、深度和透明度等,并根据计算结果为像素上色。最终渲染出多个像素点。片元也可以理解为 “像素片段”,因为它们不能完全匹配显示设备上的物理像素,而是在设备上渲染为多个物理像素。
在Three.js中,可以使用ShaderMaterial来创建自定义的着色器材质,以实现更加复杂的渲染效果。
ShaderMaterial类
ShaderMaterial是Three.js中用来定义着色器材质的一个类,其构造函数的基本语法如下:
代码语言:javascript复制ShaderMaterial( parameters )
其中,parameters是一个对象,包含了所有需要设置的属性和方法
常用属性
- uniforms:一个对象,用来传递顶点着色器和片元着色器之间需要共享的数据,例如光照、纹理等。
- vertexShader:字符串类型,表示顶点着色器的代码。
- fragmentShader:字符串类型,表示片元着色器的代码。
- clipping:定义此材质是否支持剪裁; 如果渲染器传递clippingPlanes uniform,则为true。默认值为false。
uniforms属性
Uniform变量是着色器中一个全局的变量,其值可以由Three.js中的JavaScript代码设置。ShaderMaterial的uniforms属性通常是一个对象,其中定义了uniform变量的名称、类型和初始值。
用于在顶点着色器和片元着色器之间传递数据,它在着色器中被声明为一个uniform变量,可以包含标量、向量、矩阵等类型。在构造函数中,可以通过设置uniforms属性来传入需要在着色器中使用的数据。
代码语言:javascript复制var material = new THREE.ShaderMaterial({
uniforms: {
time: {
value: 1.0 }, // 一个浮点数型的uniform变量
resolution: {
value: new THREE.Vector2() }, // 一个向量型的uniform变量
texture: {
value: new THREE.Texture() } //一个纹理类型的uniform变量
},
vertexShader: vertexShaderCode,
fragmentShader: fragmentShaderCode
});
uniform变量的常见属性:
- value 为uniform的初始值。
- type 用于定义uniform变量的类型。支持的类型包括:float、vec2、vec3、vec4、int、ivec2、ivec3、ivec4、bool、bvec2、bvec3、bvec4、mat4 和 sampler2D。
- needsUpdate 指示uniform是否需要在下一帧中更新。
可以在自定义的着色器代码中通过直接使用uniform变量的名称来引用它们。在JavaScript代码中,可以通过设置ShaderMaterial中uniforms属性中的变量值来对着色器进行控制并动态地更新外观和行为。
vertexShader属性
vertexShader表示顶点着色器的代码,这里的代码是字符串形式的着色器代码,它负责生成最终的点的位置。
代码语言:javascript复制var vertexShaderCode = `
uniform float time;
void main() {
// 处理顶点位置
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
fragmentShader属性
fragmentShader表示片元着色器的代码,这里的代码是字符串形式的着色器代码,它用于给模型添加材质、纹理、光照等效果的代码
代码语言:javascript复制var fragmentShaderCode = `
uniform vec2 resolution;
uniform float time;
uniform sampler2D texture;
void main() {
// 处理纹理采样、光照计算等
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;
代码实现
代码实现环境为:vite vue3 threejs,还不知道如何通过vite vue3 threejs构建三维场景的小伙伴可以看我以前的博客:Threejs进阶之一:基于vite vue3 threejs构建三维场景,这里不在赘述
新建ShaderView.vue文件并引入Threejs
在Vue项目的components中新建ShaderView.vue,引入Threejs及其相关库
代码语言:javascript复制import * as THREE from 'three'
import {
OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import {
onMounted } from 'vue';
import {
EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import {
BloomPass } from 'three/examples/jsm/postprocessing/BloomPass'
import {
FilmPass } from 'three/examples/jsm/postprocessing/FilmPass';
import {
RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
定义初始化函数
在onMounted函数中定义init()函数,构建scene,camera,renderer等基础场景
代码语言:javascript复制let camera,scene,renderer,material
let controls,uniforms,clock,mesh,composer
onMounted(() => {
init()
})
function init() {
initScene()
initCamera()
initMesh()
initRenderer()
initEffect()
initControls()
animate()
window.addEventListener('resize',onWindowResize)
}
function initScene() {
scene = new THREE.Scene()
}
function initCamera() {
camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000)
// 设置相机位置
camera.position.z = 5;
camera.lookAt(0,0,0)
}
function initMesh() {
}
function initRenderer() {
clock = new THREE.Clock();
renderer = new THREE.WebGL