本文是通过三角形扇
来绘制一个圆的。通过颜色插值来实现圆的渐变色。实现代码如下所示:
# 1、着色器代码
- 顶点着色器代码
<script type="shader-source" id="vertexShader">
//浮点数设置为中等精度
precision mediump float;
//接收 JavaScript 传递过来的点的坐标(X, Y)
attribute vec2 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main(){
// 最终的顶点坐标。
gl_Position = vec4(a_Position, 0.0, 1.0);
// 将顶点颜色传递给片元着色器,进行颜色插值计算
v_Color = a_Color;
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- 片元着色器代码
//浮点数设置为中等精度
precision mediump float;
//全局变量,用来接收 JavaScript传递过来的颜色。
varying vec4 v_Color;
void main(){
// 点的最终颜色。
gl_FragColor = v_Color;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 2、javascript
代码部分
WebGL
上下文的获取
function init() {
//获取canvas
//通过getElementById()方法获取canvas画布
const canvas = document.getElementById('canvas');
//设置canvas尺寸为满屏
//注意设置canvas的尺寸必须在获取WebGL上下文之前调用
resizeCanvas(canvas);
//通过方法getContext()获取WebGL上下文
const gl = canvas.getContext('webgl');
//创建着色器程序
const program = initShader(gl);
//跟着色器中的代码交互
initBuffer(gl,program)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- 初始化着色器函数
function initShader(gl) {
//顶点着色器源码
const vertexShaderSource = document.getElementById('vertexShader').innerText;
//片元着色器源码
const fragmentShaderSource = document.getElementById('fragmentShader').innerText;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
return program;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 初始化缓冲区及与着色器的交互
function initBuffer(gl,program) {
// 定义组成矩形的两个三角形,共计四个顶点,每个顶点包含2个坐标分量和4个颜色分量,其中 V0,V1,V2代表左下角三角形,V1,V2,V3代表右上角三角形。
const x=Math.round(canvas.width/2);
const y=Math.round(canvas.height/2);
let positions = createCircleVertex(x, y, 200, 350);
let a_Position = gl.getAttribLocation(program, 'a_Position');
let a_Color = gl.getAttribLocation(program, 'a_Color');
gl.enableVertexAttribArray(a_Position);
gl.enableVertexAttribArray(a_Color);
// 创建缓冲区
let buffer = gl.createBuffer();
// 绑定缓冲区为当前缓冲
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
// 设置 a_Position 属性从缓冲区读取数据方式
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 24, 0);
// 设置 a_Color 属性从缓冲区读取数据方式
gl.vertexAttribPointer(a_Color, 4, gl.FLOAT, false, 24, 8);
// 向缓冲区传递数据
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
render(gl,positions.length/6);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 设置画布的大小
//设置画布的大小
//设置canvas的大小
function resizeCanvas(canvas, width, height) {
if (canvas.width !== width) {
canvas.width = width ? width : window.innerWidth;
}
if (canvas.height !== height) {
canvas.height = height ? height : window.innerHeight;
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 创建圆的坐标及颜色数据
function createCircleVertex(x, y, radius, n) {
let positions = [...coordTransform(x, y), 1, 1, 0, 1];
for (let i = 0; i <= n; i++) {
let angle = (i * Math.PI * 2) / n;
const [positionX, positionY] = coordTransform(x + radius * Math.sin(angle), y + radius * Math.cos(angle))
positions.push(positionX, positionY,1,0,0,1)
}
return positions;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 将屏幕坐标换算成设备坐标系中的坐标
function coordTransform(x, y) {
const { width, height } = canvas;
// 将 canvas 的坐标值 转换为 [-1.0, 1.0]的范围。
const posisionX = 2 * x / width - 1;
// canvas的 Y 轴坐标方向和 设备坐标系的相反。
const positionY = -(2 * y / height - 1);
return [posisionX, positionY];
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
WebGL
渲染
function render(gl,count) {
//设置清屏颜色为黑色。
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_FAN, 0, count);
}
1
2
3
4
5
6
2
3
4
5
6
参考
WebGL零基础入门教程(郭隆邦) (opens new window) WebGL 入门与实践 (opens new window) WebGL官方文档 (opens new window)