Threejs进阶之十五:在Thereejs 使用自定义shader

2023-10-14 09:00:09 浏览数 (2)

最终效果

先看下这次代码最终要实现的效果,

效果分析:

要实现上述效果,我们需要两张图片,作为纹理贴图,使其图案产生明暗效果;然后通过定义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

0 人点赞