3.js - modelPosition.z = sin((modelPosition.x+uTime)*10.0)*0.05;

1、完整的动态波纹效果吧

main.js
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

// 顶点着色器
import basicVertexShader from './shader/11-01/raw/vertex.glsl?raw'
// 片元着色器
import basicFragmentShader from './shader/11-01/raw/fragment.glsl?raw'

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 700)
camera.position.set(0, 0, 10)

// const axesHelper = new THREE.AxesHelper(5)
// scene.add(axesHelper)

// --------------------------------------------------------------------

/*
  宽度,
  高度,
  宽度分段数:它决定了平面在宽度方向上被分割成多少个小矩形(或更准确地说,是顶点网格的宽度分辨率)。
             更高的值会创建更平滑的曲线(虽然对于平面来说,这主要体现在边缘的圆形或平滑处理上,如果有的话),但也会增加渲染的顶点和面数。
  高度分段数:与宽度分段数类似,它决定了平面在高度方向上被分割成多少个小矩形。
*/
const planeGeometry = new THREE.PlaneGeometry(1, 1, 64, 64)

// 普通材质
const material = new THREE.MeshBasicMaterial({
  color: 0x00ff00
})

// 着色器材质
// const shaderMaterial = new THREE.ShaderMaterial({
//   vertexShader: `
//     void main(){
//       gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
//     }
//   `,
//   fragmentShader: `
//     void main(){
//       gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
//     }
//   `
// })

const textureLoader = new THREE.TextureLoader()
const texture = textureLoader.load('../public/assets/texture/ca.jpeg')

/*
  原始着色器材质,
*/
const rawShaderMaterial = new THREE.RawShaderMaterial({
	vertexShader: basicVertexShader, // 顶点着色器的GLSL代码(一个字符串)
	fragmentShader: basicFragmentShader, // 片元着色器的GLSL代码(一个字符串)
	side: THREE.DoubleSide,
	`uniforms:一个对象,包含了在着色器代码中使用的全局变量,这些变量可以在JS中设置,并在着色器中被访问和修改。`
	uniforms: {
		`uTime:用于在着色器中模拟时间,其value属性被初始化为0,但可以在JS中随时间更新,以在着色器中创建【动态效果】。`
		uTime: {
			value: 0
		},
		`uTexture:用于将纹理传递给着色器`
		uTexture: {
			// value: texture
			value: null
		}
	}
})

const plane = new THREE.Mesh(planeGeometry, rawShaderMaterial)
scene.add(plane)

// --------------------------------------------------------------------

const renderer = new THREE.WebGLRenderer({
	antialias: true
})
renderer.shadowMap.enabled = true
renderer.toneMapping = THREE.ReinhardToneMapping
renderer.toneMappingExposure = 1
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true

const clock = new THREE.Clock()
const render = () => {
	// 获取,自Clock对象创建以来,经过的秒数
	const elapsedTime = clock.getElapsedTime()
	// 色器中的uTime变量,就会随着时间的推移而更新
	rawShaderMaterial.uniforms.uTime.value = elapsedTime
	
	controls.update()
	requestAnimationFrame(render)
	renderer.render(scene, camera)
}
render()

window.addEventListener('resize', () => {
	// 重置相机的宽高比
	camera.aspect = window.innerWidth / window.innerHeight
	// 更新相机的投影矩阵
	camera.updateProjectionMatrix()
	// 重置渲染器的宽高比
	renderer.setSize(window.innerWidth, window.innerHeight)
	// 更新渲染器的像素比
	renderer.setPixelRatio(window.devicePixelRatio)
})

vertex.glsl
/*
  precision关键字:设置着色器中使用的浮点数精度为lowp(低精度),有助于在不影响视觉效果的情况下提高渲染性能
      precision lowp float;   // 后续,所有浮点数的精度,为lowp(低精度:-2^8 - 2^8)
      precision lowp mediump; // 后续,所有浮点数的精度,为mediump(中精度:-2^10 - 2^10)
      precision lowp highp;   // 后续,所有浮点数的精度,为highp(高精度:-2^16 - 2^16)*/
precision lowp float;

/*
  attribute:在顶点着色器中声明变量(在较新的GLSL版本中,attribute已经被in关键字所取代)
  vec3:一个数据类型,代表一个三维向量 */
attribute vec3 position;

attribute vec2 uv; // uv:每个顶点的纹理坐标(u, v)

// 模型空间 -> 世界空间 -> 观察空间 -> 裁剪空间,然后,最终映射到屏幕坐标上,这是渲染管线中的标准变换顺序
uniform mat4 modelMatrix; // modelMatrix模型矩阵:用于将顶点,从模型空间 -> 世界空间
uniform mat4 viewMatrix; // viewMatrix视图矩阵:用于将顶点,从世界空间 -> 观察空间
uniform mat4 projectionMatrix; // projectionMatrix投影矩阵:用于将顶点,从观察空间 -> 裁剪空间,并最终映射到屏幕坐标上

// 获取时间
uniform float uTime; // uTime:一个统一变量,用于传递时间信息,可以用于动画效果

varying vec2 vUv; // vUv:传递给片段着色器的纹理坐标

varying float vElevation;

void main() {
  // 将输入的纹理坐标传递给片段着色器
  vUv = uv;

  // 顶点着色器处理的每个顶点,都有一个位置(position),这个位置是一个三维向量(vec3),包含x、y、z三个坐标

  // 用于,将顶点的位置,从模型空间啊 -> 世界空间
  vec4 modelPosition = modelMatrix*vec4(position, 1.0);

  // modelPosition.x += 1.0;
  // modelPosition.z += 1.0;
  // modelPosition.z += modelPosition.x;

 `
    1、
      通过,正弦函数sin,动态的调整,顶点的z坐标,创建一种基于时间和顶点位置的波动效果
      乘以0.05:基于顶点x坐标和时间的正弦波值,并将其幅度缩放为原始值的5%,
      意味着,
      随着uTime(时间)的增加,顶点的z坐标,将根据其在x轴上的位置以正弦波的形式变化。
    2、
      sin((modelPosition.x + uTime) * 10.0),为什么乘以10呢?
      因为,正弦函数sin的周期是2π,
      意味着,它完成一个完整的波形(从0到1,再到0)需要2π个单位
      但是,
      对(modelPosition.x + uTime)乘以10之后,
      实际上,是在对,正弦函数sin的周期,进行缩放,
      使得原本需要2π单位才能完成的波形,现在只需要(2π / 10) = 0.2π单位,就能完成一次完整的周期,
      所以,乘以10.0,
      意味着,
      波形在相同的空间或时间范围内,完成了更多的周期,从而增加了波形的频率`
  modelPosition.z = sin((modelPosition.x+uTime)*10.0)*0.05;
  modelPosition.z += sin((modelPosition.y+uTime)*10.0)*0.05;

  vElevation = modelPosition.z; // 将调整后的z值传递给片段着色器 

  `计算顶点在裁剪空间中的位置,gl_Position,是GLSL内置的输出变量,用于存储顶点的最终位置`
  gl_Position = projectionMatrix*viewMatrix*modelPosition;
}

fragment.glsl
precision lowp float; // 设置着色器中使用的浮点数精度为lowp(低精度),有助于在不影响视觉效果的情况下提高渲染性能

varying vec2 vUv; // 顶点着色器传过来的,表示,每个片段(像素)在纹理图像上的UV坐标

varying float vElevation; // 顶点着色器传过来的,表示,每个顶点的高度信息,这里用它来影响片段的颜色

uniform sampler2D uTexture; // 这是一个统一变量,指向一个二维纹理图像

void main() {
	// gl_FragColor = vec4(vUv, 0.0, 1.0);
	// float height = vElevation + 0.05 * 10.0;
	// gl_FragColor = vec4(1.0*height,0.0, 0.0, 1.0);
	
	/* 使用texture2D()函数,根据UV坐标,从uTexture纹理中采样颜色,
	   texture2D()函数,返回的是一个包含RGBA四个分量的vec4向量。 */
	vec4 textureColor = texture2D(uTexture, vUv);
	
	// 根据UV,取出对应的颜色
	float height = vElevation+(0.05*20.0); `将顶点的高度vElevation,增加了一个固定值`
	
	`将采样得到的纹理颜色(仅RGB部分)与计算出的高度值相乘,
	    意味着,
	    高度越高,纹理颜色会越亮(或越深,取决于纹理颜色的初始亮度)
	    这是一个简单的,颜色调制过程,
	    用于,根据高度信息,改变纹理的视觉效果。`
	textureColor.rgb *= height;
	
	
	`gl_FragColor:这是GLSL内置的输出变量,用于存储片元的最终颜色,将用于渲染到屏幕上`
	gl_FragColor = textureColor;
}


效果图(图是动态的)

在这里插入图片描述

在这里插入图片描述







2、modelPosition.z = sin((modelPosition.x+uTime)*10.0)*0.05;

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

function createCleanNeonRing(options = {}) { const innerRadius = options.innerRadius || 2.2; const outerRadius = options.outerRadius || 2.7; const geometry = new THREE.RingGeometry(innerRadius, outerRadius, 256); const vertexShader = ` uniform float uTime; varying vec3 vPosition; varying float vRadial; float snoise(vec3 v) { return fract(sin(dot(v, vec3(12.9898,78.233,45.164))) * 43758.5453) * 2.0 - 1.0; } void main() { vPosition = position; // 计算径向距离(归一化到0~1) float r = length(position.xy); vRadial = (r - ${innerRadius.toFixed(3)}) / (${(outerRadius-innerRadius).toFixed(3)}); // 极轻微扰动 float n = snoise(position * 0.6 + uTime * 0.2); float disp = n * 0.015; vec3 newPos = position + normal * disp; gl_Position = projectionMatrix * modelViewMatrix * vec4(newPos, 1.0); } `; const fragmentShader = ` uniform float uTime; uniform vec3 uColor1; uniform vec3 uColor2; uniform vec3 uColor3; varying vec3 vPosition; varying float vRadial; vec3 hsl2rgb(vec3 c) { vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0); return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0)); } void main() { float t = (vPosition.y + 1.0) * 0.5; vec3 baseColor; if (t < 0.5) { baseColor = mix(uColor1, uColor2, t * 2.0); } else { baseColor = mix(uColor2, uColor3, (t - 0.5) * 2.0); } float hueShift = sin(uTime * 0.18) * 0.08; float h = mod(0.55 + hueShift, 1.0); vec3 hsl = vec3(h, 0.85, 0.55); vec3 color = mix(baseColor, hsl2rgb(hsl), 0.5); // 透明度:中心透明,边缘渐显 float alpha = smoothstep(0.08, 0.92, vRadial) * 0.95; // 边缘Glow float fresnel = pow(1.0 - vRadial, 2.5); color += fresnel * vec3(0.2, 0.5, 0.7); gl_FragColor = vec4(color, alpha * (0.7 + fresnel * 0.7)); // 严格裁剪外环外部 if(vRadial < 0.08 || vRadial > 1.0) discard; } `; const material = new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms: { uTime: { value: 0 }, uColor1: { value: new THREE.Color(0x00fff6) }, uColor2: { value: new THREE.Color(0x3a6cff) }, uColor3: { value: new THREE.Color(0x00ffb0) } }, transparent: true, side: THREE.DoubleSide, blending: THREE.NormalBlending, depthWrite: false }); return new THREE.Mesh(geometry, material); } // 使用方式 window.addEventListener(&#39;DOMContentLoaded&#39;, function() { const container = document.getElementById(&#39;threeBall&#39;); const width = container.offsetWidth, height = container.offsetHeight; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(45, width/height, 0.1, 100); camera.position.z = 8; const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(width, height); renderer.setPixelRatio(window.devicePixelRatio); container.appendChild(renderer.domElement); scene.add(new THREE.AmbientLight(0xffffff, 0.3)); const pointLight = new THREE.PointLight(0xffffff, 1, 100); pointLight.position.set(5, 5, 5); scene.add(pointLight); // 只保留一层主环 const ring = createCleanNeonRing(); scene.add(ring); // 动画 const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const t = clock.getElapsedTime(); ring.material.uniforms.uTime.value = t; ring.rotation.z += 0.003; renderer.render(scene, camera); } animate(); window.addEventListener(&#39;resize&#39;, () => { const w = container.offsetWidth, h = container.offsetHeight; camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); }); });如何优化使之圆环可以涌动变换流动起来
08-23
window.addEventListener(&#39;DOMContentLoaded&#39;, function() { // 初始化场景 const container = document.getElementById(&#39;threeBall&#39;) || document.body; const width = container.offsetWidth || window.innerWidth; const height = container.offsetHeight || window.innerHeight; const scene = new THREE.Scene(); // 相机 const camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 1000); camera.position.z = 8; // 渲染器 const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(width, height); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.outputEncoding = THREE.sRGBEncoding; container.appendChild(renderer.domElement); // 环境光和点光源 scene.add(new THREE.AmbientLight(0xffffff, 0.2)); const pointLight = new THREE.PointLight(0xffffff, 1, 100); pointLight.position.set(5, 5, 5); scene.add(pointLight); // 顶点着色器 const vertexShader = ` uniform float uTime; uniform float uSpeed; uniform float uIntensity; uniform float uFlowIntensity; uniform float uHueSpeed; varying vec3 vNormal; varying vec3 vPosition; varying float vNoise; varying float vFresnel; vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); } vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } float snoise(vec3 v) { const vec2 C = vec2(1.0/6.0, 1.0/3.0); const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); vec3 i = floor(v + dot(v, C.yyy)); vec3 x0 = v - i + dot(i, C.xxx); vec3 g = step(x0.yzx, x0.xyz); vec3 l = 1.0 - g; vec3 i1 = min(g.xyz, l.zxy); vec3 i2 = max(g.xyz, l.zxy); vec3 x1 = x0 - i1 + C.xxx; vec3 x2 = x0 - i2 + C.yyy; vec3 x3 = x0 - D.yyy; i = mod289(i); vec4 p = permute(permute(permute( i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0)); float n_ = 0.142857142857; vec3 ns = n_ * D.wyz - D.xzx; vec4 j = p - 49.0 * floor(p * ns.z * ns.z); vec4 x_ = floor(j * ns.z); vec4 y_ = floor(j - 7.0 * x_); vec4 x = x_ * ns.x + ns.yyyy; vec4 y = y_ * ns.x + ns.yyyy; vec4 h = 1.0 - abs(x) - abs(y); vec4 b0 = vec4(x.xy, y.xy); vec4 b1 = vec4(x.zw, y.zw); vec4 s0 = floor(b0) * 2.0 + 1.0; vec4 s1 = floor(b1) * 2.0 + 1.0; vec4 sh = -step(h, vec4(0.0)); vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; vec3 p0 = vec3(a0.xy, h.x); vec3 p1 = vec3(a0.zw, h.y); vec3 p2 = vec3(a1.xy, h.z); vec3 p3 = vec3(a1.zw, h.w); vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3))); p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w; vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); m = m * m; return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3))); } vec3 flowNoise(vec3 p) { float eps = 0.1; float n = snoise(p); float nx = snoise(p + vec3(eps, 0.0, 0.0)); float ny = snoise(p + vec3(0.0, eps, 0.0)); float nz = snoise(p + vec3(0.0, 0.0, eps)); vec3 grad = vec3(nx - n, ny - n, nz - n) / eps; vec3 curl; curl.x = grad.z - grad.y; curl.y = grad.x - grad.z; curl.z = grad.y - grad.x; return curl * uFlowIntensity; } void main() { vNormal = normal; vPosition = position; vec3 viewDir = normalize(cameraPosition - position); vFresnel = pow(1.0 - abs(dot(normal, viewDir)), 2.0); vec3 flow = flowNoise(position * 0.8 + uTime * uSpeed * 0.3); vec3 p = position + flow; float noise1 = snoise(p * 1.5 + uTime * uSpeed); float noise2 = snoise(p * 3.0 + uTime * uSpeed * 1.3); float noise3 = snoise(p * 6.0 + uTime * uSpeed * 1.7); vNoise = (noise1 * 0.6 + noise2 * 0.3 + noise3 * 0.1); float displacement = vNoise * uIntensity; vec3 newPosition = position + normal * displacement; gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); } `; // 片元着色器 const fragmentShader = ` uniform float uTime; uniform float uHueSpeed; uniform float uGlowIntensity; uniform vec3 uColor1; uniform vec3 uColor2; varying vec3 vNormal; varying vec3 vPosition; varying float vNoise; varying float vFresnel; vec3 hsl2rgb(vec3 c) { vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0); return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0)); } vec3 rgb2hsl(vec3 color) { float minVal = min(min(color.r, color.g), color.b); float maxVal = max(max(color.r, color.g), color.b); float delta = maxVal - minVal; float h = 0.0; float s = 0.0; float l = (maxVal + minVal) / 2.0; if (delta != 0.0) { s = l < 0.5 ? delta / (maxVal + minVal) : delta / (2.0 - maxVal - minVal); if (color.r == maxVal) { h = (color.g - color.b) / delta + (color.g < color.b ? 6.0 : 0.0); } else if (color.g == maxVal) { h = (color.b - color.r) / delta + 2.0; } else { h = (color.r - color.g) / delta + 4.0; } h /= 6.0; } return vec3(h, s, l); } void main() { float hueShift = sin(uTime * uHueSpeed) * 0.1; vec3 baseColor = mix(uColor1, uColor2, (vPosition.y + 1.0) * 0.5); vec3 hsl = rgb2hsl(baseColor); hsl.x += hueShift; if(hsl.x > 1.0) hsl.x -= 1.0; if(hsl.x < 0.0) hsl.x += 1.0; hsl.y = min(hsl.y * 1.5, 0.9); vec3 color = hsl2rgb(hsl); float brightness = 0.7 + vNoise * 0.3; color *= brightness; float glow = vFresnel * uGlowIntensity * (1.0 + sin(uTime * 2.0) * 0.2); float core = pow(1.0 - length(vNormal), 2.0) * 0.5; vec3 finalColor = color + glow * vec3(0.8, 1.0, 1.0) + core * vec3(1.0, 0.8, 0.5); gl_FragColor = vec4(finalColor, 1.0); } `; // 创建霓虹能量环 const geometry = new THREE.SphereGeometry(2, 96, 96); const material = new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms: { uTime: { value: 0 }, uSpeed: { value: 0.4 }, uIntensity: { value: 0.25 }, uFlowIntensity: { value: 0.3 }, uHueSpeed: { value: 0.2 }, uGlowIntensity: { value: 4.0 }, uColor1: { value: new THREE.Color(0x00ffff) }, uColor2: { value: new THREE.Color(0xff00ff) } }, transparent: true, side: THREE.DoubleSide, wireframe: false }); const neonHalo = new THREE.Mesh(geometry, material); scene.add(neonHalo); // 动画循环 const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const elapsedTime = clock.getElapsedTime(); material.uniforms.uTime.value = elapsedTime; neonHalo.rotation.y = elapsedTime * 0.1; neonHalo.rotation.x = elapsedTime * 0.05; renderer.render(scene, camera); } animate(); // 响应窗口大小变化 window.addEventListener(&#39;resize&#39;, () => { const w = container.offsetWidth || window.innerWidth; const h = container.offsetHeight || window.innerHeight; camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); }); });这个能量球的代码有没有可以用到我能量环代码上的地方
08-23
<think>我们有两个不同的代码片段:一个是能量环(之前的霓虹光环),另一个是能量球(使用ShaderMaterial的球体)。现在需要将能量球中的一些效果应用到能量环上。 能量环原本是使用2D Canvas绘制纹理然后贴到平面上的,而能量球是使用顶点和片元着色器在GPU上实时计算的。两者实现方式不同,但我们可以借鉴能量球中的着色器效果来增强能量环。 具体思路: 1. 能量球中的流动噪声(flowNoise)和多重噪声叠加可以用于驱动能量环的半径波动,代替原来的单一正弦波。 2. 能量球中的颜色动态变化(色相偏移)可以应用到能量环的颜色上。 3. 能量球中的菲涅尔效果(Fresnel)可以用于增强能量环的边缘发光。 但是,由于能量环是使用2D纹理绘制的,我们无法直接使用着色器改变每个线条的位置。因此,我们需要改变实现方式:将能量环也改为使用着色器在GPU上实时渲染。 重构计划: 我们将创建一个圆环体(TorusGeometry),然后使用类似能量球的着色器来修改其顶点位置和颜色,以实现动态能量环。 步骤: 1. 创建圆环几何体(TorusGeometry)作为基础形状。 2. 编写顶点着色器和片元着色器,包含以下特性: - 流动噪声(flowNoise)驱动顶点位移 - 多重噪声叠加 - 菲涅尔效果(用于边缘发光) - 动态色相偏移 3. 在片元着色器中实现发光效果。 注意:能量环与能量球的不同之处在于,能量环是环状,我们希望效果集中在环上,因此可能需要调整噪声的缩放和位移。 代码实现: 我们将创建一个新的ShaderMaterial,并应用到圆环上。 顶点着色器(基于能量球的顶点着色器修改): - 将原来的球面法线改为圆环的法线 - 调整噪声的缩放,使其更适合圆环形状 片元着色器(基本可以复用能量球的,但可能需要调整颜色混合方式) 由于圆环是封闭的,我们可能会看到内外两侧,因此需要设置side: THREE.DoubleSide。 具体代码: 首先,我们移除之前创建光环纹理的代码,改为创建圆环几何体和使用着色器材质。 修改后的主逻辑(在DOMContentLoaded事件中):</think>当然可以!能量球代码中的着色器技术和动态效果可以完美应用到你的能量环上,特别是噪声算法、颜色动态变化和菲涅尔发光效果。以下是优化后的能量环代码,融合了两个项目的优点: ```javascript // 创建动态霓虹光环(使用着色器) function createNeonRingShader() { // 顶点着色器(融合流动噪声和菲涅尔效果) const vertexShader = ` uniform float uTime; uniform float uSpeed; uniform float uIntensity; uniform float uFlowIntensity; varying vec3 vNormal; varying vec3 vPosition; varying float vNoise; varying float vFresnel; // 噪声函数(从能量球代码中提取) vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); } vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } float snoise(vec3 v) { // ... 保持与能量球相同的噪声实现 ... } vec3 flowNoise(vec3 p) { // ... 保持与能量球相同的流动噪声实现 ... } void main() { vNormal = normal; vPosition = position; // 菲涅尔效果(边缘发光基础) vec3 viewDir = normalize(cameraPosition - position); vFresnel = pow(1.0 - abs(dot(normal, viewDir)), 2.0); // 流动噪声驱动的顶点位移 vec3 flow = flowNoise(position * 1.2 + uTime * uSpeed * 0.5); vec3 p = position + flow; // 多频率噪声叠加(更自然的波动) float noise1 = snoise(p * 2.0 + uTime * uSpeed); float noise2 = snoise(p * 4.0 + uTime * uSpeed * 1.3); float noise3 = snoise(p * 8.0 + uTime * uSpeed * 1.7); vNoise = (noise1 * 0.6 + noise2 * 0.3 + noise3 * 0.1); // 径向位移(保持环状结构) float displacement = vNoise * uIntensity; vec3 newPosition = position + normal * displacement; gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); } `; // 片元着色器(动态彩虹色+发光效果) const fragmentShader = ` uniform float uTime; uniform float uHueSpeed; uniform float uGlowIntensity; uniform vec3 uColor1; uniform vec3 uColor2; varying vec3 vNormal; varying vec3 vPosition; varying float vNoise; varying float vFresnel; // HSL颜色转换(从能量球代码中提取) vec3 hsl2rgb(vec3 c) { vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0); return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0)); } vec3 rgb2hsl(vec3 color) { // ... 保持与能量球相同的HSL转换实现 ... } void main() { // 动态色相偏移(彩虹效果) float hueShift = sin(uTime * uHueSpeed) * 0.5; // 基于位置的颜色混合 vec3 baseColor = mix(uColor1, uColor2, (vPosition.y + 1.0) * 0.5); vec3 hsl = rgb2hsl(baseColor); hsl.x = mod(hsl.x + hueShift, 1.0); hsl.y = min(hsl.y * 1.8, 0.9); // 提高饱和度 // 噪声影响颜色亮度 vec3 color = hsl2rgb(hsl); float brightness = 0.8 + vNoise * 0.4; color *= brightness; // 菲涅尔发光效果(边缘光晕) float glow = vFresnel * uGlowIntensity * (1.0 + sin(uTime * 3.0) * 0.3); // 核心亮度(中心更亮) float core = pow(1.0 - length(vNormal), 3.0) * 0.7; // 最终颜色合成 vec3 finalColor = color * (1.0 + core) + glow * vec3(0.8, 1.0, 1.0); float alpha = min(1.0, brightness * 0.7 + glow * 0.5 + core * 0.3); gl_FragColor = vec4(finalColor, alpha); } `; // 创建圆环几何体(比球体更适合光环) const geometry = new THREE.TorusGeometry(3, 0.5, 64, 128); // 着色器材质 const material = new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms: { uTime: { value: 0 }, uSpeed: { value: 0.5 }, uIntensity: { value: 0.3 }, uFlowIntensity: { value: 0.4 }, uHueSpeed: { value: 0.3 }, uGlowIntensity: { value: 3.0 }, uColor1: { value: new THREE.Color(0x00ffff) }, // 青色 uColor2: { value: new THREE.Color(0xff00ff) } // 品红 }, transparent: true, side: THREE.DoubleSide, blending: THREE.AdditiveBlending, wireframe: false }); return new THREE.Mesh(geometry, material); } // 渲染主逻辑(更新版) window.addEventListener(&#39;DOMContentLoaded&#39;, function() { const container = document.getElementById(&#39;threeBall&#39;); const width = container.offsetWidth, height = container.offsetHeight; // 场景初始化 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(45, width/height, 0.1, 100); camera.position.z = 10; // 渲染器 const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(width, height); renderer.setPixelRatio(window.devicePixelRatio); container.appendChild(renderer.domElement); // 添加光源(增强三维感) const ambientLight = new THREE.AmbientLight(0xffffff, 0.3); scene.add(ambientLight); const pointLight = new THREE.PointLight(0xffffff, 1, 100); pointLight.position.set(5, 5, 5); scene.add(pointLight); // 创建多层能量环 const ring1 = createNeonRingShader(); const ring2 = createNeonRingShader(); const ring3 = createNeonRingShader(); // 配置不同参数 ring2.material.uniforms.uIntensity.value = 0.4; ring2.material.uniforms.uColor1.value.set(0x00ff7f); // 绿色 ring2.material.uniforms.uColor2.value.set(0xff0080); // 粉色 ring2.scale.set(1.1, 1.1, 1.1); ring3.material.uniforms.uSpeed.value = 0.7; ring3.material.uniforms.uColor1.value.set(0xffff00); // 黄色 ring3.material.uniforms.uColor2.value.set(0x0080ff); // 蓝色 ring3.scale.set(0.9, 0.9, 0.9); scene.add(ring1); scene.add(ring2); scene.add(ring3); // 动画循环 const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const elapsedTime = clock.getElapsedTime(); // 更新所有环的uniforms [ring1, ring2, ring3].forEach(ring => { ring.material.uniforms.uTime.value = elapsedTime; ring.rotation.z += 0.01; // 添加脉动缩放 const pulse = Math.sin(elapsedTime * 2) * 0.1 + 1; ring.scale.set(pulse, pulse, pulse); }); // 额外旋转 ring1.rotation.x = elapsedTime * 0.1; ring2.rotation.y = elapsedTime * 0.15; ring3.rotation.x = elapsedTime * 0.08; ring3.rotation.y = elapsedTime * 0.12; renderer.render(scene, camera); } animate(); // 窗口大小调整 window.addEventListener(&#39;resize&#39;, () => { const w = container.offsetWidth; const h = container.offsetHeight; camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); }); }); ``` ### 关键优化点说明: 1. **几何体替换**: - 将平面几何体改为 `TorusGeometry`(圆环体) - 更适合表现能量环的立体结构 - 支持三维空间中的变形和光照 2. **噪声系统融合**: - 引入能量球中的 `snoise` 和 `flowNoise` 算法 - 多频率噪声叠加(2.0, 4.0, 8.0)产生更自然的流动 - 噪声驱动顶点位移替代原来的2D线条波动 3. **动态色彩系统**: - 使用HSL颜色空间实现平滑的彩虹色过渡 - `uHueSpeed` 控制色相变化速度 - 基于位置的颜色混合(垂直方向渐变) 4. **发光效果增强**: - 菲涅尔效果(Fresnel)实现边缘发光 - 核心亮度(center glow)增强中心光效 - 使用 `AdditiveBlending` 混合模式增强光晕 5. **多层动画系统**: - 创建三个独立参数的能量环 - 不同旋转速度和缩放脉动 - 差异化颜色配置(青-品红、绿-粉、黄-蓝) ### 优化后的效果特点: 1. **更自然的能量流动**: - 三维噪声驱动顶点位移 - 多频率波动叠加消除机械感 - 流动噪声产生漩涡效果 2. **丰富的色彩变化**: - 动态彩虹色循环 - 基于垂直位置的色彩混合 - 噪声影响局部亮度变化 3. **立体发光效果**: - 菲涅尔边缘发光 - 能量核心区域更亮 - 多层叠加产生深度感 4. **性能优化**: - 完全GPU加速(不再使用2D Canvas) - 减少JS计算量 - 自动适应高分辨率屏幕 这个实现保留了原能量环的多层结构,同时引入了能量球的高级着色器技术,创造出更自然流畅的能量流动效果和更丰富的视觉表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值