1.创建 webgl
function initGl(id) {
var canvas = document.getElementById(id);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var gl = canvas.getContext('webgl');
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
});
return gl;
}
2. 加载着色器
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
顶点着色器
attribute vec3 position;
attribute float scale;
attribute vec3 color;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying vec3 vColor;
void main() {
vec4 mvPosition = uModelViewMatrix * vec4( position, 1.0 );
//根据距离远近显示点的大小
gl_PointSize = scale * ( 300.0 / - mvPosition.z );
gl_Position = uProjectionMatrix * mvPosition ;
vColor=color;
}
片元着色器
precision highp float;
varying vec3 vColor;
void main() {
//超过一定视觉范围内,不显示
if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;
gl_FragColor = vec4( vColor, 1.0 );
}
3.初始化 program
function initShaderProgram(gl, vsSource, fsSource) {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
gl.program = shaderProgram;
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.log('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
return null;
}
return shaderProgram;
}
4. 批量创建波浪的点
const SEPARATION = 100,
AMOUNTX = 50,
AMOUNTY = 50;
const numParticles = AMOUNTX * AMOUNTY;
var count = 0;
const positions = new Float32Array(numParticles * 3);
const scales = new Float32Array(numParticles);
const colors = new Float32Array(numParticles * 3);
function createPoints() {
let i = 0,
j = 0;
for (let ix = 0; ix < AMOUNTX; ix++) {
let c = [Math.random(), Math.random(), Math.random()];
for (let iy = 0; iy < AMOUNTY; iy++) {
positions[i] = ix * SEPARATION - (AMOUNTX * SEPARATION) / 2;
positions[i + 1] = 0;
positions[i + 2] = iy * SEPARATION - (AMOUNTY * SEPARATION) / 2;
scales[j] = 1;
colors[i] = c[0];
colors[i + 1] = c[1];
colors[i + 2] = c[2];
i += 3;
j++;
}
}
}
5. 让点动起来
移动点的 y 轴位置,对应大小的改变
function movePoints() {
let i = 0,
j = 0;
for (let ix = 0; ix < AMOUNTX; ix++) {
for (let iy = 0; iy < AMOUNTY; iy++) {
positions[i + 1] = Math.sin((ix + count) * 0.3) * 50 + Math.sin((iy + count) * 0.5) * 50;
scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 20 + (Math.sin((iy + count) * 0.5) + 1) * 20;
i += 3;
j++;
}
}
}
清空重画
function cleanGl(gl) {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
正交投影
function initProjectionMatrix(gl) {
const fieldOfView = (75 * Math.PI) / 180;
const aspect = gl.canvas.width / gl.canvas.height;
const zNear = 1;
const zFar = 10000.0;
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);
gl.uniformMatrix4fv(
gl.getUniformLocation(gl.program, 'uProjectionMatrix'),
false,
projectionMatrix
);
return projectionMatrix;
}
设置缓冲区
function initArrBuffer(gl, code, value, perLen) {
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, value, gl.STATIC_DRAW);
let aVal = gl.getAttribLocation(gl.program, code);
gl.vertexAttribPointer(aVal, perLen, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(aVal);
}
动画帧
function animate() {
cleanGl(gl);
var projectionMatrix = initProjectionMatrix(gl);
const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -300.0]);
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uModelViewMatrix'), false, modelViewMatrix);
movePoints();
initArrBuffer(gl, 'position', positions, 3);
initArrBuffer(gl, 'color', colors, 3);
initArrBuffer(gl, 'scale', scales, 1);
gl.drawArrays(gl.POINTS, 0, scales.length);
count += 0.1;
requestAnimationFrame(animate);
}
github 代码地址
https://github.com/xiaolidan00/webgl-wave-points
参考: https://threejs.org/examples/#webgl_points_waves