three.js例子

后期效果提升

  • 烘焙贴图

    • lightmap https://threejs.org/examples/?q=lightmap#webgl_materials_lightmap
    • pcss https://threejs.org/examples/?q=pc#webgl_shadowmap_pcss
  • 抗锯齿

    • fxaa https://threejs.org/examples/?q=fxaa#webgl_postprocessing_fxaa
    • smaa https://threejs.org/examples/?q=sma#webgl_postprocessing_smaa
    • ssaa https://threejs.org/examples/?q=ssaa#webgl_postprocessing_ssaa
  • 环境光遮蔽 屏幕光遮蔽

    • ssr https://threejs.org/examples/?q=ssr#webgl_postprocessing_ssr
    • sao https://threejs.org/examples/?q=sao#webgl_postprocessing_sao
    • ssao https://threejs.org/examples/?q=sao#webgl_postprocessing_ssao
  • 边缘检测 点选物体,把边缘轮廓阔起来

    • sobel https://threejs.org/examples/?q=sobel#webgl_postprocessing_sobel
    • outline https://threejs.org/examples/?q=outline#webgl_postprocessing_outline
  • 发光 点亮效果

    • unreal https://threejs.org/examples/?q=unreal#webgl_postprocessing_unreal_bloom_selective
  • 镜面反射

    • mirror https://threejs.org/examples/?q=mirr#webgl_mirror
    • https://github.com/0beqz/screen-space-reflections
  • react 效果库

    • https://github.com/pmndrs/drei
  • 后期处理 效果库 postprocessing

    • https://github.com/pmndrs/postprocessing

骨骼动画

第一个顶点坐标( 10.05, 30.10, 12.12 ).
第一个皮肤骨骼索引skinIndex值是( 10, 2, 0, 0 ). 第一个皮肤权重skin weight值是( 0.8, 0.2, 0, 0 ).
这两个皮肤骨骼索引skin index和皮肤权重skin weight数据表达的意思是
骨骼10 mesh.bones[10]对第一个顶点坐标影响权重80%.
骨骼2skeleton.bones[2]对第一个顶点的影响权重20%.
接下来的两个骨骼权重值的权重为0,因此对顶点坐标没有任何影响.

加载器

let event = {};
// 单张纹理图的加载
event.onLoad = function () {
  console.log("图片加载完成");
};
event.onProgress = function (url, num, total) {
  console.log("图片加载完成:", url);
  console.log("图片加载进度:", num);
  console.log("图片总数:", total);
  let value = ((num / total) * 100).toFixed(2) + "%";
  console.log("加载进度的百分比:", value);
  div.innerHTML = value;
};
event.onError = function (e) {
  console.log("图片加载出现错误");
  console.log(e);
};

// 设置加载管理器
const loadingManager = new THREE.LoadingManager(
  event.onLoad,
  event.onProgress,
  event.onError
);

// 导入纹理
const textureLoader = new THREE.TextureLoader(loadingManager);
const doorColorTexture = textureLoader.load(
  "./textures/door/color.jpg"
  //   event.onLoad,
  //   event.onProgress,
  //   event.onError
);

const doorAplhaTexture = textureLoader.load("./textures/door/alpha.jpg");// 灰度纹理,用于控制整个表面的不透明度
const doorAoTexture = textureLoader.load("./textures/door/ambientOcclusion.jpg");//导入置换贴图
const doorHeightTexture = textureLoader.load("./textures/door/height.jpg");// 导入粗糙度贴图
const roughnessTexture = textureLoader.load("./textures/door/roughness.jpg");// 导入金属贴图
const metalnessTexture = textureLoader.load("./textures/door/metalness.jpg");// 导入法线贴图
const normalTexture = textureLoader.load("./textures/door/normal.jpg");

// 添加物体
const cubeGeometry = new THREE.BoxBufferGeometry(1, 1, 1, 100, 100, 100);

// 材质
const material = new THREE.MeshStandardMaterial({
  color: "#ffff00",
  map: doorColorTexture, // 颜色贴图。可以选择包括一个alpha通道,通常与.transparent 或.alphaTest。默认为null。 纹理贴图颜色由漫反射颜色.color调节。
  alphaMap: doorAplhaTexture, // alpha贴图是一张灰度纹理,用于控制整个表面的不透明度。(黑色:完全透明;白色:完全不透明)。 默认值为null。
  transparent: true,
  aoMap: doorAoTexture, // 该纹理的红色通道用作环境遮挡贴图。默认值为null。aoMap需要第二组UV。
  aoMapIntensity: 1, // 环境遮挡效果的强度。默认值为1。零是不遮挡效果。
  displacementMap: doorHeightTexture, // 位移贴图会影响网格顶点的位置,与仅影响材质的光照和阴影的其他贴图不同,移位的顶点可以投射阴影,阻挡其他对象, 以及充当真实的几何体。位移纹理是指:网格的所有顶点被映射为图像中每个像素的值(白色是最高的),并且被重定位。
  displacementScale: 0.1, // 位移贴图对网格的影响程度(黑色是无位移,白色是最大位移)。如果没有设置位移贴图,则不会应用此值。默认值为1。
  roughness: 1, // 材质的粗糙程度。0.0表示平滑的镜面反射,1.0表示完全漫反射。默认值为1.0。如果还提供roughnessMap,则两个值相乘。
  roughnessMap: roughnessTexture, // 该纹理的绿色通道用于改变材质的粗糙度
  metalness: 1,// 材质与金属的相似度。非金属材质,如木材或石材,使用0.0,金属使用1.0,通常没有中间值。 默认值为0.0。0.0到1.0之间的值可用于生锈金属的外观。如果还提供了metalnessMap,则两个值相乘
  metalnessMap: metalnessTexture,//该纹理的蓝色通道用于改变材质的金属度。
  normalMap: normalTexture, //用于创建法线贴图的纹理。RGB值会影响每个像素片段的曲面法线,并更改颜色照亮的方式。法线贴图不会改变曲面的实际形状,只会改变光照
  //   opacity: 0.3,
 // side: THREE.DoubleSide,
});
material.side = THREE.DoubleSide;
const cube = new THREE.Mesh(cubeGeometry, material);
scene.add(cube);
// 给cube添加第二组uv
cubeGeometry.setAttribute(
  "uv2",
  new THREE.BufferAttribute(cubeGeometry.attributes.uv.array, 2)
);

three.js优化

  1. 勾选几何数据 - 压缩 (压缩之后的文件用DRACOLoader解压缩,用GLTFLoader会报错)
  2. 服务器gzip压缩
  3. 来回拉近解决闪烁 两个物体太近 原因锯齿闪烁重影 antialias: true, // 设置抗锯齿
    logarithmicDepthBuffer: true, // 深度检测
    	renderer = new THREE.WebGLRenderer({
    	  antialias: true, // 设置抗锯齿
    	  logarithmicDepthBuffer: true, // 深度检测
    	  physicallyCorrectLights:true //默认false  物理正确光源
    	});
    
  4. 更加真实可以 physicallyCorrectLights(物理正确光源

blender 模型优化与材质优化

  1. 减少定点数: 选中模型 进入编辑模式 a 全选 网格->清理-按间距合并-合并间距 到 0.2或者0.4
  2. 材质 的bsdf 全部改成为bsdf
  3. 复制相同的物体(mesh)用alt+d 不要用shift+d , 这样项目就不会变大

blender优化导出模型

  1. 图像用 几何数据 - 图形 - JPEG格式
  2. 勾选几何数据 - 压缩 (压缩之后的文件用DRACOLoader解压缩,用GLTFLoader会报错)
  3. 服务器gzip压缩

飞线效果 three.js

import gsap from 'gsap';
import * as THREE from 'three'
import scene from '../scene'


export default function createFlyLine(){
    new FlyLine()
}

class FlyLine {
    constructor(){
        const curve = new THREE.CatmullRomCurve3( [
            new THREE.Vector3( 0, 0, 0 ),
            new THREE.Vector3( -5, 5, 5 ),
            new THREE.Vector3( -10, 0, 10 ),
        ] );
        
        this.geometry = new THREE.TubeBufferGeometry(
            curve,
            100,
            0.5,
            2, // 管道横截面的分段数目,1是线 2是平面 3是三角形
            false
          );
        
        const textLoader = new THREE.TextureLoader()
        this.texture = textLoader.load('./textures/z_11.png')
        this.texture.wrapS = THREE.RepeatWrapping
        this.texture.wrapT = THREE.MirroredRepeatWrapping
        this.texture.repeat.set(1,2) // uv是两个面
        this.material = new THREE.MeshBasicMaterial({
            map:this.texture,
            transparent:true
        })
        // Create the final object to add to the scene
        const curveObject = new THREE.Mesh( this.geometry, this.material );
  
        scene.add(curveObject)
        gsap.to(this.texture.offset,{
            x:-1,
            duration:1,
            repeat:-1,
            ease:'none'
        })
    }
}

飞线效果 glsl

建筑线框特效

顶点着色器设置远离摄像机就比较小

127:28

在场景中调整摄像机位置时,有部分开不见

127:27

修改three.js源码,实现直线光带掠过城市效果,直线,线条

import * as THREE from "three";
import gsap from "gsap";
export default function modifyCityMaterial(mesh) {
  mesh.material.onBeforeCompile = (shader) => {
    // console.log(shader.vertexShader);
    // console.log(shader.fragmentShader);
    shader.fragmentShader = shader.fragmentShader.replace(
      "#include <dithering_fragment>",
      `
        #include <dithering_fragment>
        //#end#
    `
    );
    addLightLine(shader);
  };
}
export function addLightLine(shader) {
  //   扩散的时间
  shader.uniforms.uLightLineTime = { value: -1500 };
  //   设置条带的宽度
  shader.uniforms.uLightLineWidth = { value: 200 };

  shader.fragmentShader = shader.fragmentShader.replace(
    "#include <common>",
    `
        #include <common>
        uniform float uLightLineTime;
        uniform float uLightLineWidth;
        `
  );

  shader.fragmentShader = shader.fragmentShader.replace(
    "//#end#",
    `
      float LightLineMix = -(vPosition.x+vPosition.z-uLightLineTime)*(vPosition.x+vPosition.z-uLightLineTime)+uLightLineWidth;
  
      if(LightLineMix>0.0){
          gl_FragColor = mix(gl_FragColor,vec4(0.8,1.0,1.0,1),LightLineMix /uLightLineWidth);
      }
      //#end#
      `
  );

  gsap.to(shader.uniforms.uLightLineTime, {
    value: 1500,
    duration: 5,
    ease: "none",
    repeat: -1,
  });
}

修改three.js源码,实现光圈扩散效果,圆圈

export default function modifyCityMaterial(mesh) {
    console.log(mesh);
    mesh.material.onBeforeCompile = (shader) => {
        addSpread(shader)
    }
}
function addSpread(shader, center = new THREE.Vector2(100,200)){
    // 时间 根据时间运动
    shader.uniforms.uSpreadTime = { value:0 } 
    // 扩散宽度
    shader.uniforms.uSpreadWidth = { value:100 } 
    // 设置扩散的中心点
    shader.uniforms.uSpreadCenter = {
        value : center
    }

    shader.fragmentShader = shader.fragmentShader.replace(
        `#include <common>`,
        `
        #include <common>     
        uniform float uSpreadTime;        
        uniform float uSpreadWidth;  
        uniform vec2 uSpreadCenter;  
        `
    )

    shader.fragmentShader = shader.fragmentShader.replace(
        '//#end#',
        `
        float radiusWidth = distance(vPosition.xz,uSpreadCenter);
        // 扩散范围函数
        float spreadIndex = -pow(radiusWidth-uSpreadTime,2.)+uSpreadWidth-10.;
        // 0 到 uSpreadWidth
        if(spreadIndex>0.){
            gl_FragColor = mix(gl_FragColor,vec4(1,1,1,1),spreadIndex/uSpreadWidth);
        }
        //#end#`
    )

    gsap.to(shader.uniforms.uSpreadTime,{
        value:200,
        repeat:-1,
        duration:2,
        ease: "none",
        // yoyo:true
    })
}

修改three.js源码,用glsl,根据y轴高度渐变

import * as THREE from "three";
import gsap from "gsap";
export default function modifyCityMaterial(mesh) {
  mesh.material.onBeforeCompile = (shader) => {
    // console.log(shader.vertexShader);
    // console.log(shader.fragmentShader);
    shader.fragmentShader = shader.fragmentShader.replace(
      "#include <dithering_fragment>",
      `
        #include <dithering_fragment>
        //#end#
    `
    );
    addToTopLine(shader);
  };
}
export function addToTopLine(shader) {
  //   扩散的时间
  shader.uniforms.uToTopTime = { value: 0 };
  //   设置条带的宽度
  shader.uniforms.uToTopWidth = { value: 40 };

  shader.fragmentShader = shader.fragmentShader.replace(
    "#include <common>",
    `
          #include <common>
          uniform float uToTopTime;
          uniform float uToTopWidth;
          `
  );

  shader.fragmentShader = shader.fragmentShader.replace(
    "//#end#",
    `
        float ToTopMix = -(vPosition.y-uToTopTime)*(vPosition.y-uToTopTime)+uToTopWidth;
        if(ToTopMix>0.0){
            gl_FragColor = mix(gl_FragColor,vec4(0.8,0.8,1,1),ToTopMix /uToTopWidth);
        }
        //#end#
        `
  );

  gsap.to(shader.uniforms.uToTopTime, {
    value: 500,
    duration: 3,
    ease: "none",
    repeat: -1,
  });
}

全局背景1 hdr纹理 环境背景

import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader'
const rgbeLoader = new RGBELoader()
// HDR是指高动态范围图像(High-Dynamic Range,简称HDR) 可以用ps预览
rgbeLoader.loadAsync('./assets/2k.hdr').then((texture) => {
    texture.mapping = THREE.EquirectangularReflectionMapping;
    scene.background = texture
    scene.environment = texture
})

const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.outputEncoding = THREE.sRGBEncoding
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 0.1

全局背景2 环境背景

如何获取这六张图

在这里插入图片描述

    // 在 /public/textures 下放入"px.jpg", "nx.jpg","py.jpg","ny.jpg","pz.jpg","nz.jpg"图片
    const cubeTextureLoader = new THREE.CubeTextureLoader().setPath("./textures/");
    const hdrUrls = ["px.jpg", "nx.jpg","py.jpg","ny.jpg","pz.jpg","nz.jpg",];
    const texture = cubeTextureLoader.load(hdrUrls);
    scene.background = texture
    scene.environment = texture

glsl 元素移动

// main.js 
// 传入时间
this.clock = new Three.Clock();
const elapsedTime = this.clock.getElapsedTime();
this.startMaterial.uniforms.uTime.value = elapsedTime;
// 传入要移动的距离
const astepArray = new Float32Array(3);
astepArray[0] = to.x - from.x;
astepArray[1] = to.y - from.y;
astepArray[2] = to.z - from.x;
this.startGeometry.setAttribute("aStep",new Three.BufferAttribute(astepArray, 3));

// vertex.gls
attribute vec3 aStep; // 要移动的全部距离
uniform float uTime;  // 根据时间移动
uniform float uSize;
void main(){
    vec4 modelPosition=modelMatrix*vec4(position,1.);
    // 根据时间移动
    modelPosition.xyz+=(aStep*uTime);
    vec4 viewPosition=viewMatrix*modelPosition;
    gl_Position=projectionMatrix*viewPosition;
    // 设置顶点大小
    gl_PointSize=uSize;
}

glsl 围着Y轴转圈圈

// 围着Y轴转圈圈 2.0是速度
transformed.x += sin(uTime) * 2.0;
transformed.z += cos(uTime) * 2.0;

加载音频

     // 创建音频
        this.linstener = new Three.AudioListener();
        this.linstener1 = new Three.AudioListener();
        this.sound = new Three.Audio(this.linstener);
        this.sendSound = new Three.Audio(this.linstener1);

        // 创建音频加载器
        const audioLoader = new Three.AudioLoader();
        audioLoader.load(
            `./assets/audio/pow${Math.floor(Math.random() * 4) + 1}.ogg`,
            (buffer) => {
                this.sound.setBuffer(buffer);
                this.sound.setLoop(false);
                this.sound.setVolume(1);
            }
        );

        audioLoader.load(`./assets/audio/send.mp3`, (buffer) => {
            this.sendSound.setBuffer(buffer);
            this.sendSound.setLoop(false);
            this.sendSound.setVolume(1);
        });
		
		this.sound.play();
		this.sendSound.play();
		// 如果在requestAnimationFrame中执行,可以加一个开关

坐标

,X轴红色,Y轴绿色,Z轴蓝色 红绿蓝

修改three.js顶点位置

// 传入position的文件位置
\node_modules\three\src\renderers\shaders\ShaderChunk\begin_vertex.glsl.js

在main.js 中 , 创建一个材质

let basicMaterial = new THREE.MeshBasicMaterial({
    color: "#00ff00",
    side: THREE.DoubleSide,
});
basicMaterial.onBeforeCompile = (shader, renderer) => {
    console.log('shader', shader.vertexShader);
    console.log('shader', shader.fragmentShader);
	// 修改顶点位置
	shader.vertexShader = shader.vertexShader.replace(
        `#include <begin_vertex>`,

        `#include <begin_vertex>
        transformed.x += 2.0;
        transformed.y += 2.0;
        `
    )
}

打印出顶点着色器代码

#include <common>
#include <uv_pars_vertex>
#include <uv2_pars_vertex>
#include <envmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
void main() {
	#include <uv_vertex>
	#include <uv2_vertex>
	#include <color_vertex>
	#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )
		#include <beginnormal_vertex>
		#include <morphnormal_vertex>
		#include <skinbase_vertex>
		#include <skinnormal_vertex>
		#include <defaultnormal_vertex>
	#endif

	//  目录路径为  \node_modules\three\src\renderers\shaders\ShaderChunk\begin_vertex.glsl.js
	// 所以只要改这里 可以进去这里看源码
	#include <begin_vertex>
	
	#include <morphtarget_vertex>
	#include <skinning_vertex>
	#include <project_vertex>
	#include <logdepthbuf_vertex>
	#include <clipping_planes_vertex>
	#include <worldpos_vertex>
	#include <envmap_vertex>
	#include <fog_vertex>
}
围着一个点转动, 修点顶点位置
// 声明时间对象
const basicUnifrom = {
    uTime: {
        value: 0
    }
}

// 在材质的onBeforeCompile 函数修改
basicMaterial.onBeforeCompile = (shader, renderer) => {
	
	// uniforms传入时间
    shader.uniforms.uTime = basicUnifrom.uTime
    // glsl接收
    shader.vertexShader = shader.vertexShader.replace(
        `#include <common>`,
        `#include <common>
        uniform float uTime;
        `
    )
	// 下面的 + 2. 就是围着2.0转,  *2.0是转动的速度,围着[2.,0.2.转
    shader.vertexShader = shader.vertexShader.replace(
        `#include <begin_vertex>`,
        `#include <begin_vertex>
        transformed.x += (sin(uTime) * 2.0 +2.);
        transformed.z += (cos(uTime) * 2.0+2.);
        `
    )
}

// 刷新时间的值.在外面创建时间对象,函数里面传入值
const clock = new THREE.Clock();
function animate (t) {
    const elapsetTime = clock.getElapsedTime()
    basicUnifrom.uTime.value = elapsetTime

    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}
animate();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值