如何将Three.js多边形粒子GLSL顶点着色器降级为面向相机单精灵?
如何把Polygon Shredder降级为单面向相机的精灵粒子
我来帮你拆解这个问题,分两部分解释:首先搞清楚为什么position属性要乘以18,然后一步步实现着色器的降级改造。
一、为什么position长度要乘以18?
原项目里每个粒子渲染的是三个面向视角的正方形,每个正方形需要用2个三角形来绘制(WebGL里没有原生四边形,都是三角面),每个三角形又需要3个顶点。算下来:3个面 × 2个三角/面 × 3个顶点/三角 = 18个顶点,所以每个粒子需要18份顶点位置数据,这就是代码里positionsLength要乘以18的原因。
二、着色器降级的具体步骤
要改成单个面向相机的精灵,我们需要砍掉立方体相关的逻辑,换成Billboard(公告板)精灵的实现方式,具体操作如下:
1. 清理无用的Uniform和顶点数据逻辑
- 移除
boxVertices和boxNormals:这两个uniform是用来定义立方体顶点偏移和法线的,现在我们不需要立方体了,直接从着色器和JS代码里删掉这两个变量的声明、赋值逻辑。 - 修改顶点数据长度:单个精灵用一个四边形(2个三角面)就够了,每个粒子只需要4个顶点,所以
positionsLength的计算要改成:
(如果想用点精灵更简单,每个粒子只需要1个顶点,长度改成var positionsLength = curlNoiseModel.width * curlNoiseModel.height * 3 * 4;width * height * 3即可)
2. 重构顶点着色器逻辑
原着色器是基于立方体顶点偏移计算位置,现在要换成面向相机的Billboard逻辑。这里提供两种常见实现方式:
方式一:使用四边形精灵(推荐,和原项目渲染方式更匹配)
- 新增一个
a_UV属性,用来传递每个顶点在精灵上的偏移(比如四个顶点分别传(-0.5,-0.5)、(0.5,-0.5)、(0.5,0.5)、(-0.5,0.5),控制精灵的四个角落)。 - 在顶点着色器里,通过视图矩阵计算相机的右方向和上方向,确保精灵始终面向相机:
attribute vec3 position; attribute vec2 a_UV; // 新增的精灵偏移属性 uniform mat4 projectionMatrix; uniform mat4 viewMatrix; uniform float spriteSize; // 控制精灵大小的uniform void main() { // 从视图矩阵提取相机的右、上方向向量 vec3 cameraRight = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]); vec3 cameraUp = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]); // 计算精灵每个顶点的最终位置 vec3 spritePos = position + cameraRight * a_UV.x * spriteSize + cameraUp * a_UV.y * spriteSize; gl_Position = projectionMatrix * viewMatrix * vec4(spritePos, 1.0); } - JS里生成顶点数据时,每个粒子要生成4个顶点,每个顶点的
position都是粒子的位置,a_UV分别对应四个角落的偏移值。
方式二:使用点精灵(更简洁)
如果不需要复杂的精灵形状,用GL_POINTS渲染模式更简单:
- 顶点数据每个粒子只存1个顶点位置,
positionsLength改成width * height * 3。 - 顶点着色器里设置点大小,片段着色器处理点的外观:
attribute vec3 position; uniform mat4 projectionMatrix; uniform mat4 viewMatrix; uniform float pointSize; void main() { gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); gl_PointSize = pointSize; // 控制点的大小 } - 渲染时把绘制模式从
TRIANGLES改成POINTS即可。
3. 调整JS渲染逻辑
- 移除JS中关于
boxVertices和boxNormals的uniform上传代码。 - 调整绘制调用的顶点数量:如果用四边形精灵,就是
curlNoiseModel.width * curlNoiseModel.height * 4;如果用点精灵,就是curlNoiseModel.width * curlNoiseModel.height。
这样改造后,每个粒子就会变成单个始终面向相机的精灵,完全符合你的需求啦!
内容的提问来源于stack exchange,提问作者VVK
相关产品推荐
相关产品推荐

