嗨,我是小路。今天主要和大家分享的主题是“vue+ThreeJs 创建过渡圆圈效果”。
今天在做着色器过渡效果练习,发现出现了很多新的函数,这些都超出了js之外,需要更多的掌握和学习。以下是自己的复盘和梳理。
1.获取距离
定义:distance获取两个点之间的距离
2.平滑过渡
定义:smoothstep函数是用来平滑插值的函数。在HLSL或者其他着色语言中,smoothstep可能用于生成平滑的过渡效果,比如在边缘模糊或者渐变时使用。
3.计算模型长度
定义:length
函数用于计算向量的欧几里得长度(模长)。
二、实例代码
<!--创建一个球体-->
<template>
<div class="pageBox">
<div class="leftBox" ref="leftRef"></div>
</div>
</template>
<script setup>
import { onMounted, onUnmounted, reactive, ref } from 'vue';
import * as THREE from 'three';
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { getRandomColor, createLight } from '../utils/commonThree';
const leftRef = ref();
// 定义相机输出画布的尺寸(单位:像素px)
let width = window.innerWidth; //宽度
let height = window.innerHeight; //高度
// 创建3D场景对象Scene
const scene = new THREE.Scene();
//设置背景色
scene.background = new THREE.Color(0x646d59);
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
//三角形缩放过大时,会形成多种三角形形成的背景图
camera.position.z = 5;
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
let tubes = [];
let material
const createTube = () => {
const geometry = new THREE.PlaneGeometry(10,10);
material = new THREE.ShaderMaterial({
transparent: true,
uniforms: {
iResolution: { value: new THREE.Vector2() },
iTime: { value: 0 },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec2 iResolution;
uniform float iTime;
varying vec2 vUv;
float circle(float radius, vec2 center, vec2 uv) {
float d = distance(center, uv);
return 1.0 - smoothstep(radius-1./iResolution.y, radius+1./iResolution.y, d);
}
vec2 angleRadius(vec2 uv) {
float anglePixel = atan(uv.y, uv.x);
//length 计算模型长度
float lengthPixel = length(uv);
//画一条贝塞尔曲线
return vec2(anglePixel, lengthPixel);
}
float filterPositive(float n) {
return smoothstep(0.0, 0.005, n);
}
void main() {
// 坐标系转换
vec2 uv = (vUv - 0.5) * vec2(iResolution.x/iResolution.y, 1.0)*2.0;
//生成原型的半径
float radius = 0.3;
//边缘的宽度
float ringThick = 0.05;
//画一条贝塞尔曲线
vec2 stPolar = angleRadius(uv);
float sPolar = stPolar.x * 3.0 + iTime * 10.0;
float cosSPolarTemp = cos(sPolar);
float cosSPolar = filterPositive(cosSPolarTemp);
vec3 color = vec3(cosSPolar);
float inCircleAA = smoothstep(radius, radius + 0.005, stPolar.y);
float smallCircleAA = smoothstep(radius - ringThick, radius - ringThick + 0.005, stPolar.y);
vec3 col = 1.0 - vec3(inCircleAA);
vec3 col_2 = 1.0 - vec3(smallCircleAA);
vec3 colorGap = col - col_2;
vec3 finalColor = color * colorGap;
vec3 colorMask = vec3(10.0, 1.5, 1.0);
finalColor /= 10.0;
finalColor *= colorMask;
//实现平滑过渡效果
float centerCircleAA = smoothstep(0.1, 0.1 + 0.005, stPolar.y);
vec3 centerCircleColor = 1.0 - vec3(centerCircleAA);
centerCircleColor /= 10.0;
centerCircleColor *= colorMask;
vec2 centerC = vec2(0.0);
float bubbleRadius = abs(sin(iTime * 3.0)) / 3.0;
float bubbleCircleColor = circle(bubbleRadius, centerC, uv);
//创建一个四位结构体 x、y、z、w 或者颜色 rgba
vec4 bubbleColor = vec4(vec3(bubbleCircleColor) / 10.0 * colorMask, 1.0);
gl_FragColor = vec4(finalColor + centerCircleColor, 1.0);
gl_FragColor += bubbleColor;
}
`,
side: THREE.DoubleSide
});
const tube = new THREE.Mesh(geometry, material);
//设置网格的随机位置
// sphere.position.set(
// (Math.random() - 0.5) * 2000,
// (Math.random() - 0.5) * 1000,
// (Math.random() - 0.5) * 1000
// );
return tube;
}
onMounted(() => {
initData()
//添加相机空间
const controls = new OrbitControls(camera, renderer.domElement);
// controls.enableDamping = false;
controls.enableZoom = false;
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
//将innerHTML置空,避免append重复添加渲染
leftRef.value.innerHTML = ''
leftRef.value.append(renderer.domElement);
})
const initData = () => {
createLight(scene);
// for (let i = 1; i >= 0; i--) {
const outSphere = createTube();
tubes.push(outSphere);
scene.add(outSphere);
// }
render();
}
function render() {
requestAnimationFrame(render);
// 旋转所有三角形
// tubes.forEach(tube => {
// tube.rotation.x += 0.01;
// tube.rotation.y += 0.01;
// tube.rotation.z += 0.01;
// });
console.log( 'dfffff',material.uniforms.iResolution);
material.uniforms.iResolution.value.set(
window.innerWidth,
window.innerHeight
);
material.uniforms.iTime.value = performance.now() / 1000;
renderer.render(scene, camera);
}
onUnmounted(() => {
//释放内存
renderer.dispose();
})
</script>
<style scoped lang="less">
.pageBox {
width: 100%;
height: 100vh;
padding: 0;
margin: 0;
display: flex;
justify-content: space-between;
align-items: center;
.rightBox {
width: 100%;
height: 100%;
}
}
</style>
三、总结
在学习中,重新复盘了着色器的知识点,并寻找更好掌握着色器的方法,同时了解到了glsl编程中的动态过渡、获取模型长度、计算两个元素之间距离的方法。
都看到这里了,记得【点赞】+【关注】哟。
参考文章: