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

news2024/11/24 13:24:11

目录

  • 最终效果
  • 什么是 Shader
  • ShaderMaterial类
    • 常用属性
      • uniforms属性
      • vertexShader属性
      • fragmentShader属性
  • 代码实现
    • 新建ShaderView.vue文件并引入Threejs
    • 定义初始化函数
    • 创建initMesh函数
      • 实例化ShaderMaterial类
      • 实例化TextureLoader()
      • 定义uniforms 全局变量
      • 定义vertexShader顶点着色器
      • 定义fragmentShader 片元着色器
      • 定义Mesh并添加到scene中
      • 定义render()函数
      • 完整代码

最终效果

先看下这次代码最终要实现的效果,
在这里插入图片描述
效果分析:
要实现上述效果,我们需要两张图片,作为纹理贴图,使其图案产生明暗效果;然后通过定义ShaderMaterial对象通过自定义Shader实现上述效果;后面代码中会进行详细分析;
这里我们先介绍下基础知识

什么是 Shader

Shader(着色器)是一种在图形处理单元(GPU)上执行的程序,它定义了如何根据输入数据(例如顶点位置,纹理坐标等)计算出各个像素的颜色。shader 主要包含两种类型,分别为顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)。

顶点着色器处理顶点数据,例如坐标、法线、纹理坐标等,并对每个顶点进行分析、转换和计算。然后将这些处理过的数据传递给片元着色器进行下一步的计算。

片元着色器则处理每个像素的数据,包括颜色、深度和透明度等,并根据计算结果为像素上色。最终渲染出多个像素点。片元也可以理解为 “像素片段”,因为它们不能完全匹配显示设备上的物理像素,而是在设备上渲染为多个物理像素。

在Three.js中,可以使用ShaderMaterial来创建自定义的着色器材质,以实现更加复杂的渲染效果。

ShaderMaterial类

ShaderMaterial是Three.js中用来定义着色器材质的一个类,其构造函数的基本语法如下:

ShaderMaterial( parameters )

其中,parameters是一个对象,包含了所有需要设置的属性和方法

常用属性

  • uniforms:一个对象,用来传递顶点着色器和片元着色器之间需要共享的数据,例如光照、纹理等。
  • vertexShader:字符串类型,表示顶点着色器的代码。
  • fragmentShader:字符串类型,表示片元着色器的代码。
  • clipping:定义此材质是否支持剪裁; 如果渲染器传递clippingPlanes uniform,则为true。默认值为false。

uniforms属性

Uniform变量是着色器中一个全局的变量,其值可以由Three.js中的JavaScript代码设置。ShaderMaterial的uniforms属性通常是一个对象,其中定义了uniform变量的名称、类型和初始值。
用于在顶点着色器和片元着色器之间传递数据,它在着色器中被声明为一个uniform变量,可以包含标量、向量、矩阵等类型。在构造函数中,可以通过设置uniforms属性来传入需要在着色器中使用的数据。

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表示顶点着色器的代码,这里的代码是字符串形式的着色器代码,它负责生成最终的点的位置。

var vertexShaderCode = `
    uniform float time;
    void main() {
        // 处理顶点位置
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
`;

fragmentShader属性

fragmentShader表示片元着色器的代码,这里的代码是字符串形式的着色器代码,它用于给模型添加材质、纹理、光照等效果的代码

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及其相关库

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等基础场景

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.WebGLRenderer({ antialias: true } )
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize(window.innerWidth,window.innerHeight)
  document.querySelector('#scene').appendChild(renderer.domElement)
}
function initEffect() {
    const renderModel = new RenderPass( scene, camera );
	const effectBloom = new BloomPass( 1.25 );
	const effectFilm = new FilmPass( 0.35, 0.95, 2048, false ); 
	composer = new EffectComposer( renderer ); 
	composer.addPass( renderModel );
	composer.addPass( effectBloom );
	composer.addPass( effectFilm );
}
function initControls() {
  controls = new OrbitControls(camera,renderer.domElement)
}
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth,window.innerHeight)
}
function animate() { 
  requestAnimationFrame(animate) 
  renderer.render(scene,camera)
} 

创建initMesh函数

创建initMesh()函数,在initMesh()函数中创建自定义Shader

实例化ShaderMaterial类

在initMesh()中实例化ShaderMaterial类

const size = 0.65;
material = new THREE.ShaderMaterial()

实例化TextureLoader()

const textureLoader = new THREE.TextureLoader()

定义uniforms 全局变量

 // uniforms 全局变量
  uniforms = {
    'fogDensity': { value: 0.45 }, //定义一个名为fogDensity的浮点数uniform变量,初始值为0.45。
	'fogColor': { value: new THREE.Vector3( 0, 0, 0 ) },//定义一个名为fogColor的THREE.Vector3类型的uniform变量,初始值为黑色(0,0,0)'time': { value: 1.0 },//定义一个名为time的浮点数uniform变量,初始值为1.0。
	'uvScale': { value: new THREE.Vector2( 3.0, 1.0 ) },//定义一个名为uvScale的THREE.Vector2类型的uniform变量,初始值为(3.0, 1.0)'texture1': { value: textureLoader.load( 'textures/cloud.png' ) },//定义一个名为texture1的uniform变量,是一个纹理贴图,初始值设置为使用textureLoader从文件路径加载的纹理图片。
	'texture2': { value: textureLoader.load( 'textures/lavatile.jpg' ) }//定义一个名为texture2的uniform变量,是一个纹理贴图,初始值设置为使用textureLoader从文件路径加载的纹理图片。
  }
  uniforms[ 'texture1' ].value.wrapS = uniforms[ 'texture1' ].value.wrapT = THREE.RepeatWrapping;//设置uniform变量的纹理寻址模式为重复(RepeatWrapping),设置wrapS 和 wrapT属性,将水平和垂直方向的纹理寻址模式都设置为THREE.RepeatWrapping
  uniforms[ 'texture2' ].value.wrapS = uniforms[ 'texture2' ].value.wrapT = THREE.RepeatWrapping;//设置uniform变量的纹理寻址模式为重复(RepeatWrapping),设置wrapS 和 wrapT属性,将水平和垂直方向的纹理寻址模式都设置为THREE.RepeatWrapping
  material.uniforms = uniforms

定义vertexShader顶点着色器

这里每行代码都有注释,不在一步步介绍了

  //这是顶点着色器是进行顶点变换处理的代码,它负责生成最终的点的位置。
  material.vertexShader = `
      uniform vec2 uvScale;//定义了一个uniform变量uvScale,它是一个vec2类型的二维向量变量。顶点着色器使用这个变量来缩放顶点着色器中的uv纹理坐标属性。
	  varying vec2 vUv;//定义了一个存储纹理坐标的varying变量vUv。varying变量是用于从顶点着色器传递数据到片元着色器的一种方式。这里的vUv将在顶点着色器和片元着色器之间进行传递,用于对渲染的纹理进行采样。
      void main()
	  {
         vUv = uvScale * uv;
		 vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
		 gl_Position = projectionMatrix * mvPosition;//主函数gl_Position根据传入的顶点位置(position)和模型视图矩阵(modelViewMatrix)计算出视图空间中该顶点的最终位置,然后计算出变换后的裁剪空间坐标gl_Position。
      }
  `

定义fragmentShader 片元着色器

这里每行代码都有注释,不在一步步介绍了

//这是片元着色器的代码。片元着色器是用于给模型添加材质、纹理、光照等效果的代码。
material.fragmentShader = `
  uniform float time; //定义了uniform变量,float类型的time
  uniform float fogDensity;//定义了uniform变量,float的 fogDensity
  uniform vec3 fogColor;//定义了uniform变量,vec3的 fogColor
  uniform sampler2D texture1;//定义了纹理贴图 uniform变量,sampler2D的 texture1
  uniform sampler2D texture2;//定义了纹理贴图  uniform变量,sampler2D的 texture2
  varying vec2 vUv;//定义了一个varying变量vUv用于传递纹理坐标信息。
  void main( void ) {
    vec2 position = - 1.0 + 2.0 * vUv; //使用varying变量vUv计算像素位置。
    vec4 noise = texture2D( texture1, vUv );//使用texture2D函数从纹理贴图texture1中读取像素颜色值,
    vec2 T1 = vUv + vec2( 1.5, - 1.5 ) * time * 0.02;//并计算两个新的纹理坐标T1和T2。
    vec2 T2 = vUv + vec2( - 0.5, 2.0 ) * time * 0.01;//并计算两个新的纹理坐标T1和T2。
    T1.x += noise.x * 2.0; 
    T1.y += noise.y * 2.0;
    T2.x -= noise.y * 0.2;
    T2.y += noise.z * 0.2;//按照像素噪声纹理值修改T1和T2的坐标值。
    float p = texture2D( texture1, T1 * 2.0 ).a;//使用修改后的坐标T1进行采样,并获取采样结果的alpha通道值。
    vec4 color = texture2D( texture2, T2 * 2.0 );
    vec4 temp = color * ( vec4( p, p, p, p ) * 2.0 ) + ( color * color - 0.1 );//使用修改后的坐标T2进行采样,并计算着色器渲染出的颜色。
    if( temp.r > 1.0 ) { temp.bg += clamp( temp.r - 2.0, 0.0, 100.0 ); }
    if( temp.g > 1.0 ) { temp.rb += temp.g - 1.0; }
    if( temp.b > 1.0 ) { temp.rg += temp.b - 1.0; } //对temp中各通道的颜色进行裁剪和拓展。
    gl_FragColor = temp;//将着色器计算的颜色信息输出到gl_FragColor。
    float depth = gl_FragCoord.z / gl_FragCoord.w;
    //上面一行代码用于计算当前片元(像素)在摄像机坐标系下的深度值,其中 gl_FragCoord.z 表示由GPU自动计算的深度值,gl_FragCoord.w 是权重值,
    // 用于进行透视矫正,对于非透视投影(gl_FragCoord.w = 1.0),可以简化为 gl_FragCoord.z。
    // 因此,该行代码实际上计算了gl_FragCoord的z分量除以w分量,得到当前像素的深度值。
    const float LOG2 = 1.442695;
    //上面一行代码定义了一个常量LOG2,它的值等于自然对数的底数 e 以 2 为底的对数,即 log2(e)1.442695。
    // 这个常量的作用是用于计算雾效果的混合因子(fogFactor),它是由深度值(depth)和雾浓度(fogDensity)相乘,再乘以一个常量LOG2计算出来的。
    float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );
    //上面一行代码计算了雾的混合因子(fogFactor),它是根据深度值和雾浓度计算出来的。
    // exp2是指数函数的一种,它计算出以2为底数的幂指数,这里将其参数设为-fogDensity * fogDensity * depth * depth * LOG2。
    //这里fogDensity指雾的浓度,depth指当前像素的深度值,LOG2为预设常量,-fogDensity * fogDensity * depth * depth * LOG2的结果表示雾的混合因子,
    // 它会被用于混合雾颜色和原本像素颜色的结果。雾的混合因子会随着深度值的变化而变化,
    // 深度值越大,混合因子越小,颜色受雾的影响越小,而深度越浅,混合因子越大,颜色受雾的影响越明显。
    fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 ); 
    //上面一行代码对计算出来的雾混合因子进行了一个限制,保证它的值在[0,1]之间,由于雾混合因子是描述雾浓度的,
    // 因此当它的值小于0时,表示雾浓度为0,当它的值大于1时,表示雾浓度无限大。
    //代码将限制后的混合因子通过clamp函数限制在0和1之间,然后将其用1减去,得到一个新的雾混合因子值。
    // 这里计算的目的是为了在混合雾颜色和原本像素颜色的时候,将颜色按照一定的比例混合。这个混合因子值越大,混合的比例就越小,颜色受到的雾的影响也就越小,反之,则越大。

    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );
    // 上面一行代码使用了mix函数来将原本像素的颜色gl_FragColor和雾颜色fogColor混合,得到最终的颜色。
    // mix函数会根据第三个参数fogFactor的值,按照一定程度将第一个参数gl_FragColor和第二个参数vec4(fogColor, gl_FragColor.w)进行混合。
    // 其中vec4(fogColor, gl_FragColor.w)这个向量是将fogColor作为颜色,gl_FragColor.w作为透明度,合成为一个新的向量。
    // 当fogFactor的值为0时,混合结果中不含有雾颜色,即完全显示原本像素的颜色,当fogFactor的值为1时,混合结果中完全是雾颜色。
    // 在0和1之间的值表示在原本像素的颜色和雾颜色之间按一定的比例进行混合,
    // 根据前面代码对fogFactor的计算,深度值越浅,混合因子越大,混合的比例就越小,这样就可以实现实际中的雾效果。
    // 最终结果以gl_FragColor的形式保存到GPU的帧缓存中,该值会被传递到下一个渲染管道中。
  }
  `

定义Mesh并添加到scene中

定义一个圆环缓存几何体new THREE.TorusGeometry( size, 0.3, 30, 30 ),环面的半径为size,管道的半径为0.3,管道横截面的分段数为30,管道的分段数为30, 圆环的圆心角(单位是弧度)使用默认值,为Math.PI * 2。

  const geometry = new THREE.TorusGeometry( size, 0.3, 30, 30 )
  mesh = new THREE.Mesh(geometry,material)
  scene.add(mesh)

定义render()函数

定义render()函数,并在animate()函数中调用,使其产生动画效果
在initRenderer()函数中创建clock 对象,clock = new THREE.Clock()

function render() {
  const delta = 5 * clock.getDelta(); 
  uniforms[ 'time' ].value += 0.2 * delta; 
  mesh.rotation.y += 0.0125 * delta;
  mesh.rotation.x += 0.05 * delta; 
  renderer.clear();
  composer.render( 0.01 );
}

刷新浏览器,效果已经处理了
在这里插入图片描述

完整代码

<template>
  <div id="scene"></div>
</template>
<script setup>
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';
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()
  // scene.background = new THREE.Color(0x808080)
}
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() {
  // 创建矩形
  // const geometry = new THREE.BoxGeometry(1, 1, 1);
  // ShaderMaterial
  // 1.uniforms:全局变量
  // 2.vertexShader:顶点着色器;定义位置信息
  // 3.fragmentShader:片段着色器;定义颜色
  const size = 0.65;
  material = new THREE.ShaderMaterial()
  const textureLoader = new THREE.TextureLoader()
  // uniforms 全局变量
  uniforms = {
    'fogDensity': { value: 0.45 }, //定义一个名为fogDensity的浮点数uniform变量,初始值为0.45。
		'fogColor': { value: new THREE.Vector3( 0, 0, 0 ) },//定义一个名为fogColor的THREE.Vector3类型的uniform变量,初始值为黑色(0,0,0)'time': { value: 1.0 },//定义一个名为time的浮点数uniform变量,初始值为1.0。
		'uvScale': { value: new THREE.Vector2( 3.0, 1.0 ) },//定义一个名为uvScale的THREE.Vector2类型的uniform变量,初始值为(3.0, 1.0)'texture1': { value: textureLoader.load( 'textures/cloud.png' ) },//定义一个名为texture1的uniform变量,是一个纹理贴图,初始值设置为使用textureLoader从文件路径加载的纹理图片。
		'texture2': { value: textureLoader.load( 'textures/lavatile.jpg' ) }//定义一个名为texture2的uniform变量,是一个纹理贴图,初始值设置为使用textureLoader从文件路径加载的纹理图片。
  }
  uniforms[ 'texture1' ].value.wrapS = uniforms[ 'texture1' ].value.wrapT = THREE.RepeatWrapping;//设置uniform变量的纹理寻址模式为重复(RepeatWrapping),设置wrapS 和 wrapT属性,将水平和垂直方向的纹理寻址模式都设置为THREE.RepeatWrapping
  uniforms[ 'texture2' ].value.wrapS = uniforms[ 'texture2' ].value.wrapT = THREE.RepeatWrapping;//设置uniform变量的纹理寻址模式为重复(RepeatWrapping),设置wrapS 和 wrapT属性,将水平和垂直方向的纹理寻址模式都设置为THREE.RepeatWrapping
  material.uniforms = uniforms
  material.vertexShader = `//这是顶点着色器是进行顶点变换处理的代码,它负责生成最终的点的位置。
      uniform vec2 uvScale;//定义了一个uniform变量uvScale,它是一个需要在Three.js中使用JavaScript进行更新的vec2类型的二维向量变量。顶点着色器使用这个变量来缩放顶点着色器中的uv纹理坐标属性。
			varying vec2 vUv;//定义了一个存储纹理坐标的varying变量vUv。varying变量是用于从顶点着色器传递数据到片元着色器的一种方式。这里的vUv将在顶点着色器和片元着色器之间进行传递,用于对渲染的纹理进行采样。

			void main()
			{

				vUv = uvScale * uv;
				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
				gl_Position = projectionMatrix * mvPosition;//主函数gl_Position根据传入的顶点位置(position)和模型视图矩阵(modelViewMatrix)计算出视图空间中该顶点的最终位置,然后计算出变换后的裁剪空间坐标gl_Position。

			}
  `

  material.fragmentShader = ` //这是片元着色器的代码。片元着色器是用于给模型添加材质、纹理、光照等效果的代码。
  uniform float time; //定义了uniform变量,float类型的time

  uniform float fogDensity;//定义了uniform变量,float的 fogDensity
  uniform vec3 fogColor;//定义了uniform变量,vec3的 fogColor

  uniform sampler2D texture1;//定义了纹理贴图 uniform变量,sampler2D的 texture1
  uniform sampler2D texture2;//定义了纹理贴图  uniform变量,sampler2D的 texture2

  varying vec2 vUv;//定义了一个varying变量vUv用于传递纹理坐标信息。

  void main( void ) {

    vec2 position = - 1.0 + 2.0 * vUv; //使用varying变量vUv计算像素位置。

    vec4 noise = texture2D( texture1, vUv );//使用texture2D函数从纹理贴图texture1中读取像素颜色值,
    vec2 T1 = vUv + vec2( 1.5, - 1.5 ) * time * 0.02;//并计算两个新的纹理坐标T1和T2。
    vec2 T2 = vUv + vec2( - 0.5, 2.0 ) * time * 0.01;//并计算两个新的纹理坐标T1和T2。

    T1.x += noise.x * 2.0; 
    T1.y += noise.y * 2.0;
    T2.x -= noise.y * 0.2;
    T2.y += noise.z * 0.2;//按照像素噪声纹理值修改T1和T2的坐标值。

    float p = texture2D( texture1, T1 * 2.0 ).a;//使用修改后的坐标T1进行采样,并获取采样结果的alpha通道值。

    vec4 color = texture2D( texture2, T2 * 2.0 );
    vec4 temp = color * ( vec4( p, p, p, p ) * 2.0 ) + ( color * color - 0.1 );//使用修改后的坐标T2进行采样,并计算着色器渲染出的颜色。

    if( temp.r > 1.0 ) { temp.bg += clamp( temp.r - 2.0, 0.0, 100.0 ); }
    if( temp.g > 1.0 ) { temp.rb += temp.g - 1.0; }
    if( temp.b > 1.0 ) { temp.rg += temp.b - 1.0; } //对temp中各通道的颜色进行裁剪和拓展。

    gl_FragColor = temp;//将着色器计算的颜色信息输出到gl_FragColor。

    float depth = gl_FragCoord.z / gl_FragCoord.w;
    //上面一行代码用于计算当前片元(像素)在摄像机坐标系下的深度值,其中 gl_FragCoord.z 表示由GPU自动计算的深度值,gl_FragCoord.w 是权重值,
    // 用于进行透视矫正,对于非透视投影(gl_FragCoord.w = 1.0),可以简化为 gl_FragCoord.z。
    // 因此,该行代码实际上计算了gl_FragCoord的z分量除以w分量,得到当前像素的深度值。
    const float LOG2 = 1.442695;
    //上面一行代码定义了一个常量LOG2,它的值等于自然对数的底数 e 以 2 为底的对数,即 log2(e)1.442695。
    // 这个常量的作用是用于计算雾效果的混合因子(fogFactor),它是由深度值(depth)和雾浓度(fogDensity)相乘,再乘以一个常量LOG2计算出来的。
    float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );
    //上面一行代码计算了雾的混合因子(fogFactor),它是根据深度值和雾浓度计算出来的。
    // exp2是指数函数的一种,它计算出以2为底数的幂指数,这里将其参数设为-fogDensity * fogDensity * depth * depth * LOG2。
    //这里fogDensity指雾的浓度,depth指当前像素的深度值,LOG2为预设常量,-fogDensity * fogDensity * depth * depth * LOG2的结果表示雾的混合因子,
    // 它会被用于混合雾颜色和原本像素颜色的结果。雾的混合因子会随着深度值的变化而变化,
    // 深度值越大,混合因子越小,颜色受雾的影响越小,而深度越浅,混合因子越大,颜色受雾的影响越明显。
    fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 ); 
    //上面一行代码对计算出来的雾混合因子进行了一个限制,保证它的值在[0,1]之间,由于雾混合因子是描述雾浓度的,
    // 因此当它的值小于0时,表示雾浓度为0,当它的值大于1时,表示雾浓度无限大。
    //代码将限制后的混合因子通过clamp函数限制在0和1之间,然后将其用1减去,得到一个新的雾混合因子值。
    // 这里计算的目的是为了在混合雾颜色和原本像素颜色的时候,将颜色按照一定的比例混合。这个混合因子值越大,混合的比例就越小,颜色受到的雾的影响也就越小,反之,则越大。

    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );
    // 上面一行代码使用了mix函数来将原本像素的颜色gl_FragColor和雾颜色fogColor混合,得到最终的颜色。
    // mix函数会根据第三个参数fogFactor的值,按照一定程度将第一个参数gl_FragColor和第二个参数vec4(fogColor, gl_FragColor.w)进行混合。
    // 其中vec4(fogColor, gl_FragColor.w)这个向量是将fogColor作为颜色,gl_FragColor.w作为透明度,合成为一个新的向量。
    // 当fogFactor的值为0时,混合结果中不含有雾颜色,即完全显示原本像素的颜色,当fogFactor的值为1时,混合结果中完全是雾颜色。
    // 在0和1之间的值表示在原本像素的颜色和雾颜色之间按一定的比例进行混合,
    // 根据前面代码对fogFactor的计算,深度值越浅,混合因子越大,混合的比例就越小,这样就可以实现实际中的雾效果。
    // 最终结果以gl_FragColor的形式保存到GPU的帧缓存中,该值会被传递到下一个渲染管道中。

  }
  `
  const geometry = new THREE.TorusGeometry( size, 0.3, 30, 30 )
  mesh = new THREE.Mesh(geometry,material)
  scene.add(mesh)
  // console.log(mesh);
}
function initRenderer() {
  clock = new THREE.Clock();
  renderer = new THREE.WebGLRenderer({ antialias: true } )
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize(window.innerWidth,window.innerHeight)
  document.querySelector('#scene').appendChild(renderer.domElement)
}
function initEffect() {
  const renderModel = new RenderPass( scene, camera );
	const effectBloom = new BloomPass( 1.25 );
	const effectFilm = new FilmPass( 0.35, 0.95, 2048, false );

	composer = new EffectComposer( renderer );

	composer.addPass( renderModel );
	composer.addPass( effectBloom );
	composer.addPass( effectFilm );
}
function initControls() {
  controls = new OrbitControls(camera,renderer.domElement)
}
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth,window.innerHeight)
}
function animate() {
  render();
  requestAnimationFrame(animate) 
  renderer.render(scene,camera)
}
function render() {
  const delta = 5 * clock.getDelta(); 
	uniforms[ 'time' ].value += 0.2 * delta; 
	mesh.rotation.y += 0.0125 * delta;
	mesh.rotation.x += 0.05 * delta; 
	renderer.clear();
	composer.render( 0.01 );
}
</script>
<style lang='scss' scoped>
</style>

OK,今天就先到这里吧,喜欢的小伙伴们关注点赞收藏哦,我会持续输出更多内容,希望小伙伴们喜欢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/552860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ad18学习笔记二:绘图工具栏、活动栏

在画原理图库的时候会经常用到顶上的绘图工具栏&#xff08;官方文档里叫做活动栏&#xff09;&#xff1a; 版本不同&#xff0c;上面的命令是不同的 ad如何自定义绘图工具栏&#xff1f; 网上介绍工具栏和设置的文章还挺多的&#xff0c;但是没有看到ad18是怎么增减绘图工具…

LeetCode高频算法刷题记录7

文章目录 1. 下一个排列【中等】1.1 题目描述1.2 解题思路1.3 代码实现 2. 两数相加【中等】2.1 题目描述2.2 解题思路2.3 代码实现 3. 括号生成【中等】3.1 题目描述3.2 解题思路3.3 代码实现 4. 滑动窗口最大值【困难】4.1 题目描述4.2 解题思路4.3 代码实现 5. 最小覆盖子串…

c++boost库学习-07-Message Queue

一、前言 boost中的消息队列&#xff08;Message Queue&#xff09;是进程间通信的一种机制&#xff0c;实际上是其内部也是采用共享内存的方式来达到进程间通信的目的。这也就意味这Message Queue有其局限性&#xff1a;只有处在同一台计算机中的不同进程才能使用消息队列进行…

在原有机械硬盘的基础上集装固态硬盘并装操作系统

1、加装固态硬盘 我的电脑出场自带的是机械硬盘&#xff08;即右边那个白色长方形&#xff0c;上面有类似于锡纸一样的东西&#xff09;&#xff0c;左边的这个光滑的正方形里面是内存条&#xff0c;可以拆开这个光滑的盖进行安装&#xff0c;而我们的固态硬盘装在左下角这个长…

性能测试——性能统计工具

性能统计工具 一、io监控命令1、io监控命令iostat2、io指标监控命令df 二、cpu监控命令1、cpu指标监控命令uptime2、cpu指标监控命令 cat /proc/cpuinfo3、cpu 指标监控命令 mpstat4、cpu指标监控命令 sar 三、mem指标监控命令1、mem指标监控命令 cat /proc/meminfo2、mem指标监…

KVM虚拟化(二)

文章目录 4.7 kvm虚拟机克隆4.7.1 完整克隆4.7.2 链接克隆 4.8 kvm虚拟机的桥接网络4.8.1 创建桥接网卡4.8.2 新虚拟机使用桥接模式4.8.3 将已有虚拟机网络修改为桥接模式 4.9 热添加技术4.9.1 kvm热添加硬盘4.9.2 kvm虚拟机在线热添加网卡4.9.3 kvm虚拟机在线热添加内存4.9.4 …

自动化测试技术解析:Appium、Sikuli与MonkeyTalk

目录 前言&#xff1a; 一、Appium自动化测试框架 它的优点是&#xff1a; 二、Sikuli自动化测试工具 它的优点是&#xff1a; 三、MonkeyTalk自动化测试工具 它的优点是&#xff1a; 四、代码样例 总结&#xff1a; 前言&#xff1a; 随着移动应用的普及&#xff0c;…

手把手教你通过PaddleHub快速实现输入中/英文本生成图像(Stable Diffusion)

近来&#xff0c;基于Diffusion的文图生成模型比较火&#xff0c;用户输入一句话&#xff0c;模型就可以生成一副对应的图像&#xff0c;还是很有意思的。本文记录了通过PaddleHub快速实现上述任务的过程&#xff0c;以供参考。 1、安装PaddlePaddle PaddleHub底层依赖于百度…

ChatGPT工作提效之生成开发需求和报价单并转为Excel格式

ChatGPT工作提效之生成开发需求和报价单并转为Excel格式 一、提出需求如何撰写百度地图标注开发的需求文档 二、针对性地连续提问推荐下一下百度地图标注文档的详细需求列表如何撰写百度地图标注开发的技术规范如何确定百度地图标注开发后的部署计划... 三、生成报价单四、运营…

【2023 · CANN训练营第一季】进阶班 应用开发深入讲解→模型推理

1 模型离线推理 各步要解析如下: Host&Device内存管理与数据传输: Host&Device上的内存申请与释放&#xff0c;内存间的相互拷贝;模型加载:将离线的om文件加载到Device上;在样例的资源初始化模块中进行。模型输入输出准备∶根据禹线om的输入输出&#xff0c;在Device…

微信小程序富文本组件mp-html

功能介绍 支持在多个主流的小程序平台和 uni-app 中使用支持丰富的标签&#xff08;包括 table、video、svg 等&#xff09;支持丰富的事件效果&#xff08;自动预览图片、链接处理等&#xff09;支持设置占位图&#xff08;加载中、出错时、预览时&#xff09;支持锚点跳转、…

【Linux】在Linux操作系统下对于权限的理解

目录 ❤️前言 正文 Linux下的不同用户 Linux的权限管理 文件访问者的分类 文件类型和访问权限 文件访问权限的修改方法 默认权限 目录权限 粘滞位 &#x1f340;结语 ❤️前言 大家好&#xff01;今天这篇文章主要是关于Linux操作系统下对于各种权限的理解问题&#…

AI人工智能预处理数据的方法和技术有哪些?

AI人工智能 预处理数据 在人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;领域中&#xff0c;数据预处理是非常重要的一环。它是在将数据输入到模型之前对数据进行处理和清洗的过程。数据预处理可以提高模型的准确性、可靠性和可解释性。 本文将…

Springboot +spring security,基于多种方式配置登录用户:memory、jdbc、MyBatis

一.简介 前面章节所有的用户信息(用户名和密码)都是基于配置文件配置的&#xff0c;这篇文章学习基于多种方式配置登录用户&#xff0c;比如&#xff1a; memory&#xff08;内存&#xff09;jdbcMyBatis 二.创建项目 如何创建一个SpringSecurity项目&#xff0c;前面文章已…

Python入门【序列、列表简介、列表的创建 、列表元素的增加、列表元素的删除 】(四)-全面详解(学习总结---从入门到深化)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

微服务架构初探

大家好&#xff0c;我是易安&#xff01;我们今天来谈一谈微服务架构的前世今生。 我们先来看看维基百科是如何定义微服务的。微服务的概念最早是在2014年由Martin Fowler和James Lewis共同提出&#xff0c;他们定义了微服务是由单一应用程序构成的小服务&#xff0c;拥有自己的…

chatgpt赋能Python-pythonfalse

PythonFalse&#xff1a; Python中的False值 在Python编程语言中&#xff0c;布尔(Boolean)是一种基本数据类型&#xff0c;它只有两个值&#xff1a;True和False。这篇文章将讨论Python中的False值&#xff0c;并提供有关如何使用它的指南。 什么是PythonFalse PythonFalse…

chatgpt赋能Python-pythongil

Python GIL&#xff08;全局解释器锁&#xff09;介绍 Python GIL 是全局解释器锁&#xff08;Global Interpreter Lock&#xff09;的简称&#xff0c;它是 Python 解释器中的一个重要概念。GIL 的作用是确保任何时间只有一个线程在执行 Python 指令&#xff0c;以防止多个线…

jsonmodels.model.base

欢迎来到猫子酱的学习之旅 jsonmodels.model创建模型用法验证 validate()验证器 Validators自定义验证器&#xff08;**&#xff09;默认值转换为Python结构&#xff08;和JSON&#xff09;为您的模型创建JSON模式(***) &#xff08;结合Draft7Validator&#xff09;结构和对象…

蓝桥杯单片机串口通信学习提升笔记

今日得以继续蓝桥杯国赛备赛之旅&#xff1a; 有道是 “不知何事萦怀抱&#xff0c;醒也无聊&#xff0c;醉也无聊&#xff0c;梦也何曾到谢桥。” 那我们该如何 让这位诗人纳兰 “再听乐府曲 &#xff0c;畅解相思苦”呢&#xff1f; 那就建立起串口通信吧&#xff01; 我…