WebGL入门---交互绘制点
上一节只是实现了一个静态点的绘制,但是真正的 WebGL 应用总是需要通过网页和用户进行交互,进而改变画面的。所以接下来,我们要实现一个简单交互程序:在鼠标点击过的位置绘制一个点,而且这个点的颜色是随机的。
这要求我们有能力通过 JavaScript 往着色器程序中传入顶点位置和颜色数据,从而改变点的位置和颜色。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>从一个点开始</title><meta charset="UTF-8">
</head>
<body>
<canvas id="canvas"></canvas>
<script type="shader-source" id="vertexShader">
// 设置浮点数精度为中等精度
precision mediump float;
// 接收点在canvas坐标系上的坐标
attribute vec2 a_Position;
//接受canvas的宽高尺寸
attribute vec2 a_Screen_Size;
void main(){
// start 将屏幕坐标系转化为裁剪坐标系
//上面这句代码用来将浏览器窗口坐标转换成裁剪坐标,之后通过透视除法,除以 w 值(此处为 1 )转变成设备坐标(NDC坐标系)。
//这个算法首先将(x,y) 转化到【0, 1】区间,再将 【0, 1】之间的值乘以 2 转化到 【0, 2】区间,之后再减去 1 ,转化到 【-1, 1】之
//间的值,即 NDC 坐标
vec2 position = (a_Position/a_Screen_Size)*2.0 - 1.0;
position = position*vec2(1.0,-1.0);
// 声明顶点的位置
gl_Position = vec4(position, 0.0, 1.0);
// 声明待绘制点的大小
gl_PointSize = 10.0;
}
</script>
<script type="shader-source" id="fragmentShader">
// 设置浮点数精度为中等精度
precision mediump float;
//接收javascript 传过来的颜色值
uniform vec4 u_Color;
void main(){
vec4 color = u_Color / vec4(255, 255, 255, 1);
gl_FragColor = color;
}
</script>
<script>
var random = Math.random;
function randomColor() {
return {
r: random() * 255,
g: random() * 255,
b: random() * 255,
a: random() * 1
};
}
function $$(str) {
if (!str) return null;
if (str.startsWith('#')) {
return document.querySelector(str);
}
let result = document.querySelectorAll(str);
if (result.length == 1) {
return result[0];
}
return result;
}
function getCanvas(id) {
return $$(id);
}
function getContext(canvas) {
return canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
}
/**
*
*
* @param {*} gl,webgl绘图环境
* @param {*} type,着色器类型
* @param {*} scriptId,着色器源码scrpit标签id
* @returns 返回着色器对象
*/
function createShaderFromScript(gl, type, scriptId) {
let sourceScript = $$('#' + scriptId);
if (!sourceScript) {
return null;
}
return createShader(gl, type, sourceScript.innerHTML);
}
function createShader(gl, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
//检测是否编译正常。
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
function createProgram(gl, vertexShader, fragmentShader) {
let program = gl.createProgram();
vertexShader && gl.attachShader(program, vertexShader);
fragmentShader && gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
let result = gl.getProgramParameter(program, gl.LINK_STATUS);
if(result){
return program
}
let errorLog = gl.getProgramInfoLog(program);
gl.deleteProgram(program);
throw errorLog;
}
</script>
<script>
// var canvas = document.querySelector('#canvas');
var canvas = getCanvas('#canvas');
//获取webgl绘图环境
var gl = getContext(canvas);
//创建顶点着色器
var vertexShader = createShaderFromScript(gl, gl.VERTEX_SHADER,'vertexShader');
//创建片元着色器
var fragmentShader = createShaderFromScript(gl, gl.FRAGMENT_SHADER,'fragmentShader');
var program = createProgram(gl ,vertexShader, fragmentShader);
// 使用刚创建好的着色器程序。
gl.useProgram(program);
//找到顶点着色器中的变量a_Position
var a_Position = gl.getAttribLocation(program, 'a_Position');
//找到顶点着色器中的变量a_Screen_Size
var a_Screen_Size = gl.getAttribLocation(program, 'a_Screen_Size');
//找到片元着色器中的变量u_Color
var u_Color = gl.getUniformLocation(program, 'u_Color');
//为顶点着色器中的 a_Screen_Size 传递 canvas 的宽高信息
gl.vertexAttrib2f(a_Screen_Size, canvas.width, canvas.height);
//存储点击位置的数组。
var points = [];
canvas.addEventListener('click', e => {
var x = e.pageX;
var y = e.pageY;
var color = randomColor();
points.push({ x: x, y: y, color: color })
gl.clearColor(0, 0, 0, 1.0);
//用上一步设置的清空画布颜色清空画布。
gl.clear(gl.COLOR_BUFFER_BIT);
for (let i = 0; i < points.length; i++) {
var color = points[i].color;
//为片元着色器中的 u_Color 传递随机颜色
gl.uniform4f(u_Color, color.r, color.g, color.b, color.a);
//为顶点着色器中的 a_Position 传递顶点坐标。
gl.vertexAttrib2f(a_Position, points[i].x, points[i].y);
//绘制点
gl.drawArrays(gl.POINTS, 0, 1);
}
})
// 设置清屏颜色
gl.clearColor(0, 0, 0, 1.0);
// 用上一步设置的清空画布颜色清空画布。
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body>
</html>