文章目录
前言
在webgl程序在使用attribute变量向顶点着色器传入位置,使用uniform向片元着色器传入颜色,给画布绑定事件实现动态绘制点
使用attribute更改顶点坐标
attribute使用流程
虽然我们可以直接在顶点着色器中定义顶点坐标,采用这种“硬编码”的方式将使程序缺乏扩展性。attribute变量可以将在javascript中定义的顶点信息传递至顶点着色器中

gl.getAttribLocation() , gl.vertexAttrib3f()
gl.getAttribLocation(program, name):获取参数名为name的attribute变量存储地址
- program: 包含顶点着色器和片元着色器的片元对象
- name: 要获取的目标attribute地址对应的attribute变量名称
gl.vertexAttrib3f(location, v0, v1, v2):向attribute变量赋值
- location: 目标attribute的存储位置
- v0: 填充attribute的第一个分量
- v1: 填充attribute的第二个分量
- v2: 填充attribute的第三个分量
vertexAttrib3f还有几个同族函数gl.vertexAttrib1f(location, v0), gl.vertexAttrib2f(location, v0, v1), gl.vertexAttrib4f(location, v0, v1, v2,v3),它们具有相同的作用,其中v0 v1 v2默认值为0.0,v3默认值为1.0
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webgl</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
body {
margin: 0;
padding: 0;
}
canvas {
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
/** @type {HTMLCanvasElement} */
// 获取canvas元素对象
let canvas = document.getElementById('canvas');
// 获取webgl绘图上下文
const gl = canvas.getContext('webgl');
if (!gl) {
throw new Error('WebGL not supported');
}
canvas.width = 500;
canvas.height = 500;
gl.viewport(0, 0, canvas.width, canvas.height)
// 设置背景色
gl.clearColor(1.0, 1.0, 0.0, 1.0)
// 清空缓冲区
gl.clear(gl.COLOR_BUFFER_BIT)
const vertex = `
attribute vec4 aPosition;
void main() {
gl_Position = aPosition;
gl_PointSize = 10.0;
}
`
const fragment = `
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
// 创建program
const program = initShader(gl, vertex, fragment)
// 获取attribute变量的数据存储位置
const aPosition = gl.getAttribLocation(program, 'aPosition');
// 向attribute赋值
gl.vertexAttrib3f(aPosition,0.75,0.75,1.0)
// 绘制
gl.drawArrays(gl.POINTS, 0, 1);
</script>
</body>
</html>
使用uniform更改顶点颜色
attribute变量只能将javascript中定义的顶点信息传递至顶点着色器中 ,使用uniform可以将信息传递给片元着色器,比如片元颜色。 整体使用与attribute几乎没有区别,唯一注意的是片元着色器没有设置默认精度,需要手动设置
示例
<script>
/** @type {HTMLCanvasElement} */
//------------------------------------------------------创建画布
// 获取canvas元素对象
let canvas = document.getElementById('canvas');
// 获取webgl绘图上下文
const gl = canvas.getContext('webgl');
if (!gl) {
throw new Error('WebGL not supported');
}
canvas.width = 500;
canvas.height = 500;
gl.viewport(0, 0, canvas.width, canvas.height)
// 设置背景色
gl.clearColor(1.0, 1.0, 0.0, 1.0)
// 清空缓冲区
gl.clear(gl.COLOR_BUFFER_BIT)
const vertex = `
attribute vec4 aPosition;
void main() {
gl_Position = aPosition;
gl_PointSize = 10.0;
}
`
const fragment = `
precision highp float;
uniform vec4 uColor;
void main(){
gl_FragColor = uColor;
}
`
// 创建program
const program = initShader(gl, vertex, fragment)
// 获取attribute变量的数据存储位置
const aPosition = gl.getAttribLocation(program, 'aPosition');
// 获取uniform变量的数据存储位置
const uColor = gl.getUniformLocation(program, 'uColor');
// 向attribute赋值
gl.vertexAttrib3f(aPosition,0.75,0.75,1.0)
// 向uniform赋值
gl.uniform4f(uColor,1.0,0.0,1.0,1.0)
// 绘制
gl.drawArrays(gl.POINTS, 0, 1);
</script>
动态绘制
流程

平面坐标系转齐次坐标
点击事件直接获取的坐标为屏幕坐标,需要转换至canvas坐标,如图

- 屏幕坐标和canvas坐标的定义规则是一致的,唯一可能不同的只有原点位置
- canvas坐标至webgl坐标既需要考虑原点平移,又需要将前两项范围固定值[-1.0, 1.0]
// 屏幕坐标系
const x = ev.clientX;
const y = ev.clientY;
const bound = ev.target.getBoundingClientRect(); // 获取原点之间相对位置
// 转换至canvas坐标
const canvasX = (x-bound.left);
const canvasY = (y-bound.top);
// 转换至齐次坐标
glX = (2 * canvasX/canvas.width - 1)
glY = (1 - 2 * canvasY/canvas.height)
示例效果

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webgl</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
body {
margin: 0;
padding: 0;
}
canvas {
margin: 50px 30px;
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
/** @type {HTMLCanvasElement} */
//------------------------------------------------------创建画布
// 获取canvas元素对象
let canvas = document.getElementById('canvas');
// canvas绑定事件
canvas.onmousedown = function(ev){click(ev,gl,canvas,aPosition)}
let pointList = []
// 获取webgl绘图上下文
const gl = canvas.getContext('webgl');
if (!gl) {
throw new Error('WebGL not supported');
}
canvas.width = 500;
canvas.height = 500;
gl.viewport(0, 0, canvas.width, canvas.height)
// 设置背景色
gl.clearColor(1.0, 1.0, 0.0, 1.0)
// 清空缓冲区
gl.clear(gl.COLOR_BUFFER_BIT)
const vertex = `
attribute vec4 aPosition;
void main() {
gl_Position = aPosition;
gl_PointSize = 10.0;
}
`
const fragment = `
precision highp float;
uniform vec4 uColor;
void main(){
gl_FragColor = uColor;
}
`
// 创建program
const program = initShader(gl, vertex, fragment)
// 获取attribute变量的数据存储位置
const aPosition = gl.getAttribLocation(program, 'aPosition');
// 获取uniform变量的数据存储位置
const uColor = gl.getUniformLocation(program, 'uColor');
// 点击绘制函数
function click(ev, gl, canvas, aPosition) {
console.log(pointList)
const x = ev.clientX;
const y = ev.clientY;
const bound = ev.target.getBoundingClientRect();
// 转换至canvas坐标
const canvasX = (x - bound.left);
const canvasY = (y - bound.top);
// 转换至齐次坐标
glX = (2 * canvasX / canvas.width - 1)
glY = (1 - 2 * canvasY / canvas.height)
pointList.push([glX, glY])
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制点
pointList.forEach(function (point) {
gl.vertexAttrib2f(aPosition, point[0], point[1])
gl.uniform4f(uColor, point[0], point[1], 1.0, 1.0)
gl.drawArrays(gl.POINTS, 0, 1);
})
}
</script>
</body>
</html>
不足
- 虽然实现了动态绘制点,但是每个点绘制都要调用一次gl.drawArrays方法
- 当绘制多组图形时,需要在全局定义多个list,容易导致管理混乱
总结
- attribute变量向顶点着色器写入位置
- 使用uniform向片元着色器写入颜色
- 给画布绑定事件实现动态绘制点
- 不足:多次调用gl.drawArrays方法,当绘制多组时,管理混乱
162

被折叠的 条评论
为什么被折叠?



