3.js - 着色器设置点材质(螺旋星系特效)

上图



着色器设置点材质时,在顶点着色器中,最好设置gl_PointSize,不然看不到你在页面中添加的点



main.js

javascript 复制代码
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

import gsap from 'gsap'

import theVertexShader from './shader/13/vertex.glsl?raw'
import theFragmentShader from './shader/13/fragment.glsl?raw'

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(75, window.innerHeight / window.innerHeight, 0.1, 1000)
camera.position.set(0, 0, 5)
camera.aspect = window.innerWidth / window.innerHeight
scene.add(camera)

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

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

// 加载纹理
const textureLoader = new THREE.TextureLoader()
let texture = textureLoader.load('../public/assets/texture/particles/9.png')
let texture1 = textureLoader.load('../public/assets/texture/particles/10.png')
let texture2 = textureLoader.load('../public/assets/texture/particles/11.png')

const params = {
	count: 1000, // 数量
	size: 0.1, // 大小
	radius: 5, // 半径
	branch: 4, // 分支
	color: '#ff6030',
	outColor: '#1b3984'
}

let geometry = null
let material = null
let point = null
let galaxyColor = new THREE.Color(params.color)
let outGalaxyColor = new THREE.Color(params.outColor)

const generateGalaxy = () => {
	// 如果已经存在这些顶点,那么先释放内存,在删除顶点数据
	if (point !== null) {
		geometry.dispose()
		material.dispose()
		scene.remove(point)
	}

	geometry = new THREE.BufferGeometry() // 生成顶点
	const position = new Float32Array(params.count * 3) // 顶点位置
	const colors = new Float32Array(params.count * 3) // 顶点颜色
	const imgIndex = new Float32Array(params.count) // 贴图
	const size_arr = new Float32Array(params.count) // 大小

	for (let i = 0; i < params.count; i++) {
		const current = i * 3
		
		const branchAngel = (i % params.branch) * ((2 * Math.PI) / params.branch)
		// 当前点,距离圆心的距离
		const distance = Math.random() * params.radius
		
		// 中心点多,外围的点少
		const randomX = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5
		const randomY = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5
		const randomZ = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5
		
		position[current] = Math.cos(branchAngel) * distance + randomX
		position[current + 1] = 0 + randomY
		position[current + 2] = Math.sin(branchAngel) * distance + randomZ
		
		const mixColor = galaxyColor.clone()
		mixColor.lerp(outGalaxyColor, distance / params.radius)
		// 设置颜色
		colors[current] = mixColor.r
		colors[current + 1] = mixColor.g
		colors[current + 2] = mixColor.b
		
		// 根据索引值,设置不同的贴图
		imgIndex[current] = i % 3
		
		// 顶点的大小
		size_arr[current] = Math.random()
	}

	geometry.setAttribute('position', new THREE.BufferAttribute(position, 3))
	geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3))
	geometry.setAttribute('imgIndex', new THREE.BufferAttribute(imgIndex, 1))
	geometry.setAttribute('asize', new THREE.BufferAttribute(size_arr, 1))

	//#region 点材质
	// material = new THREE.PointsMaterial({
		//   color: new THREE.Color(params.color),
		//   size: params.size,
		//   map: texture,
		//   transparent: true,
		//   alphaMap: texture,
		//   depthWrite: false,
		//   sizeAttenuation: true,
		//   blending: THREE.AdditiveBlending
	// })
	//#endregion

	material = new THREE.ShaderMaterial({
		vertexShader: theVertexShader,
		fragmentShader: theFragmentShader,
		transparent: true,
		vertexColors: true,
		depthWrite: false,
		blending: THREE.AdditiveBlending,
		uniforms: {
			uTime: {
				value: 0
			},
			uTexture: {
				value: texture
			},
			uTexture1: {
				value: texture1
			},
			uTexture2: {
				value: texture2
			},
			uColor: {
				value: galaxyColor
			}
		}
	})

	point = new THREE.Points(geometry, material)
	scene.add(point)
}

generateGalaxy()

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

//#region

const renderer = new THREE.WebGLRenderer()
renderer.shadowMap.enabled = true
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

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

const clock = new THREE.Clock()
function animate() {
	// controls.update()
	const elapsedTime = clock.getElapsedTime()
	material.uniforms.uTime.value = elapsedTime
	requestAnimationFrame(animate)
	renderer.render(scene, camera)
}
animate()

window.addEventListener('resize', () => {
	camera.aspect = window.innerWidth / window.innerHeight
	camera.updateProjectionMatrix()
	renderer.setSize(window.innerWidth, window.innerHeight)
	renderer.setPixelRatio(window.devicePixelRatio)
})

//#endregion

vertex.glsl

javascript 复制代码
precision lowp float;

varying vec2 vUv;

attribute float asize;
attribute float imgIndex;
varying float vImgIndex;

uniform float uTime;

varying vec3 vColor;

void main() {
	vUv = uv;
	vColor = color;
	vImgIndex = imgIndex;
	
	vec4 modelPosition = modelMatrix*vec4(position, 1.0);

	`获取顶点的角度
	    注:【atan(y, x)】计算两点之间的角度(以弧度为单位)`
	float angle = atan(modelPosition.x, modelPosition.z);

	`获取顶点到中心的距离
		注:
			按道理得这么写:【length(vec2(modelPosition.x, modelPosition.z))】
			如果 modelPosition 是一个三维向量,并且,你只需要 x 和 z 分量,length(modelPosition.xz)这样写也可以`
	float distanceToCenter = length(modelPosition.xz);

	`角度偏移angleOffset,是基于,顶点到中心距离的倒数,乘以时间uTime来计算的
	    这种方法会导致:
	      当顶点接近中心时,偏移量变得非常大,导致快速旋转`
	float angleOffset = 1.0/distanceToCenter*uTime;

	`当前旋转的度数`
	angle = angle+angleOffset;

	// modelPosition.x = cos(angle);
	// modelPosition.z = sin(angle);
	modelPosition.x = cos(angle)*distanceToCenter;
	modelPosition.z = sin(angle)*distanceToCenter;

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


  	`不设置 gl_PointSize 页面上就啥也没有
		1、GLSL中,`gl_PointSize`是一个特殊的输出变量,用于,设置点渲染时点的大小,
		2、以【gl_PointSize = 200.0/-viewPosition.z*asize】为例:
		   涉及,视点空间中,顶点的z坐标(viewPosition.z),和一个属性asize的值`
	// 根据,顶点的深度(即:离视点的距离)和每个顶点可能具有的不同大小(asize)来动态调整点的大小。
	gl_PointSize = 200.0/-viewPosition.z*asize;
	// gl_PointSize = 10.0; // 你可以写死试试
}

fragment.glsl

javascript 复制代码
precision lowp float;

uniform sampler2D uTexture;
uniform sampler2D uTexture1;
uniform sampler2D uTexture2;

varying float vImgIndex;

varying vec3 vColor;

void main() {
	// 方案- 1
	// gl_FragColor = vec4(gl_PointCoord, 0.0, 1.0);
	
	// 方案- 2 - 设置渐变圆
	// float strength = distance(gl_PointCoord,vec2(0.5));
	// strength*=2.0;
	// strength = 1.0-strength;
	// gl_FragColor = vec4(strength);
	
	// 方案- 3 - 圆形点
	// float strength = 1.0-distance(gl_PointCoord, vec2(0.5));
	// strength = step(0.5, strength);
	// gl_FragColor = vec4(strength);
	
	// 方案- 4 - 设置贴图
	// vec4 textureColor = texture2D(uTexture, gl_PointCoord);
	// gl_FragColor = vec4(textureColor.rgb, textureColor.r);
	
	// 方案- 5 - 设置多种贴图
	vec4 textureColor;
	if(vImgIndex==0.0) {
		textureColor = texture2D(uTexture, gl_PointCoord);
	} else if(vImgIndex==1.0) {
		textureColor = texture2D(uTexture1, gl_PointCoord);
	} else {
		textureColor = texture2D(uTexture2, gl_PointCoord);
	}
	gl_FragColor = vec4(vColor, textureColor.r);
}
相关推荐
程序员_三木5 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
MossGrower2 天前
36. Three.js案例-创建带光照和阴影的球体与平面
3d图形·webgl·three.js·光照与阴影
MossGrower2 天前
34. Three.js案例-创建球体与模糊阴影
webgl·three.js·3d渲染·阴影效果
广东数字化转型3 天前
Three.js相机Camera控件知识梳理
3d·three.js
关山月4 天前
9个学习着色器的GLSL示例
前端·three.js
程序员_三木4 天前
Three.js资源-贴图材质网站推荐
javascript·webgl·three.js·材质·贴图
MossGrower4 天前
37. Three.js案例-绘制部分球体
3d图形·webgl·three.js·球体几何体
关山月5 天前
如何使用Three.js创建3D音频可视化工具
前端·three.js
MossGrower5 天前
35. Three.js案例-创建带阴影的球体与平面
webgl·three.js·阴影·球体
MossGrower6 天前
28. Three.js案例-创建圆角矩形并进行拉伸
3d图形·webgl·three.js·圆角矩形