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);
}
相关推荐
Nan_Shu_6142 天前
学习: Threejs (15)& Threejs (16)
学习·three.js
Charlie_lll2 天前
学习Three.js–材质(Material)
前端·three.js
答案—answer6 天前
开源项目:Three.js3D模型可视化编辑系统
javascript·3d·开源·开源项目·three.js·three.js编辑器
贝格前端工场6 天前
困在像素里:我的可视化大屏项目与前端价值觉醒
前端·three.js
全栈王校长7 天前
Three.js 材质进阶
webgl·three.js
全栈王校长7 天前
Three.js Geometry进阶
webgl·three.js
烛阴7 天前
3D字体TextGeometry
前端·webgl·three.js
全栈王校长8 天前
Three.js 开发快速入门
three.js
全栈王校长8 天前
Three.js 环境搭建与开发初识
three.js
DaMu8 天前
Dreamcore3D ARPG IDE “手搓”游戏引擎,轻量级实时3D创作工具,丝滑操作,即使小白也能轻松愉快的创作出属于你自己的游戏世界!
前端·架构·three.js