WebGL - 绘制点
WebGL - 绘制点
Section titled “WebGL - 绘制点”本章演示了在 WebGL 中绘制简单点的基本过程。我们将沿用之前建立的核心步骤,并根据 gl.POINTS 原始类型进行调整。
绘制点的核心步骤
Section titled “绘制点的核心步骤”绘制点与绘制三角形或其他原始类型遵循相同的通用流程:
步骤 1: 设置 Canvas 和 WebGL 上下文
获取 canvas 元素和 WebGL 渲染上下文 (gl)。包含错误处理。
步骤 2: 定义几何体(顶点)和缓冲区
定义要绘制的每个点的 3D 坐标。将这些坐标存储在 Float32Array 中,并使用 gl.createBuffer、gl.bindBuffer 和 gl.bufferData 将它们传输到 WebGL 顶点缓冲区对象 (VBO)。
// 定义三个点的坐标 (X, Y, Z)const pointVertices = new Float32Array([ -0.5, 0.5, 0.0, // 点 1 0.0, 0.5, 0.0, // 点 2 -0.25, 0.0, 0.0 // 点 3]);
// ... (创建 VBO 和 bufferData 代码) ...步骤 3: 创建和编译着色器
编写、编译和链接顶点着色器和片段着色器。
- 顶点着色器: 接收顶点位置属性 (
a_position)。它必须将最终位置输出到gl_Position。关键的是,对于点,你可以通过写入内置变量gl_PointSize来控制其像素大小。 - 片段着色器: 决定每个点的颜色。设置
gl_FragColor。
// 绘制点的顶点着色器attribute vec3 a_position;uniform float u_pointSize; // 从 JS 传递大小
void main() { gl_Position = vec4(a_position, 1.0); gl_PointSize = u_pointSize; // 设置点的大小}// 绘制点的片段着色器precision mediump float;
void main() { gl_FragColor = vec4(1.0, 0.8, 0.0, 1.0); // 橙色}步骤 4: 链接着色器和缓冲区
获取 a_position 属性和任何 Uniform(如 u_pointSize)的位置。启用属性数组 (gl.enableVertexAttribArray) 并使用 gl.vertexAttribPointer 配置其数据源,使其指向 VBO。
步骤 5: 设置 Uniform 并绘制
使用 gl.uniform1f 为 u_pointSize Uniform 设置值。清除 canvas,设置视口 (viewport),然后使用 gl.drawArrays(gl.POINTS, first, count) 渲染点。count 应该是顶点缓冲区中定义的点数量。
gl.drawArrays(gl.POINTS, 0, 3); // 从索引 0 开始绘制 3 个点示例:绘制三个点
Section titled “示例:绘制三个点”这个完整的例子绘制了三个不同的橙色点。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>WebGL Drawing Points</title> <style> body { margin: 0; overflow: hidden; background-color: #222; } canvas { display: block; border: 1px solid lightgray; } </style></head><body> <canvas id="webgl-canvas" width="500" height="500"></canvas>
<script> // --- 着色器源码 --- const vsSource = ` attribute vec3 a_position; uniform float u_pointSize; void main() { gl_Position = vec4(a_position, 1.0); gl_PointSize = u_pointSize; } `;
const fsSource = ` precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.8, 0.0, 1.0); // 橙色 } `;
// --- 工具函数:创建着色器(包含错误检查)--- function createShader(gl, type, source) { /* ... (same as previous example) ... */ const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error(`着色器编译错误 (${type === gl.VERTEX_SHADER ? '顶点' : '片段'}):`, gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; }
// --- 工具函数:创建程序(包含错误检查)--- function createProgram(gl, vsSource, fsSource) { /* ... (same as previous example) ... */ const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource); if (!vertexShader || !fragmentShader) return null; const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error('程序链接错误:', gl.getProgramInfoLog(program)); gl.deleteProgram(program); return null; } return program; }
// --- 主 WebGL 初始化 --- const canvas = document.getElementById('webgl-canvas'); const gl = canvas.getContext('webgl');
if (!gl) { alert('WebGL 不受支持。'); throw new Error('WebGL 不受支持'); }
// --- 着色器程序 --- const shaderProgram = createProgram(gl, vsSource, fsSource); if (!shaderProgram) throw new Error('创建着色器程序失败。');
// --- 几何体和缓冲区 --- const pointVertices = new Float32Array([ -0.5, 0.5, 0.0, // 点 1(左上) 0.0, 0.5, 0.0, // 点 2(中上) -0.25, 0.0, 0.0 // 点 3(左中) ]);
const vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, pointVertices, gl.STATIC_DRAW);
// --- 属性和 Uniform 位置 --- const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'a_position'); const pointSizeUniformLocation = gl.getUniformLocation(shaderProgram, 'u_pointSize');
// --- 位置错误检查 --- if (positionAttribLocation < 0) console.error("未找到属性 'a_position'"); if (!pointSizeUniformLocation) console.error("未找到 Uniform 'u_pointSize'");
// --- 配置属性指针 --- gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // 设置指针前先绑定 gl.vertexAttribPointer( positionAttribLocation, // 位置 3, // 大小 (x, y, z) gl.FLOAT, // 类型 false, // 是否标准化? 0, // 步长 0 // 偏移量 ); gl.enableVertexAttribArray(positionAttribLocation);
// --- 设置 Uniform 并绘制 --- gl.useProgram(shaderProgram); // 设置 Uniform 前先激活程序
const desiredPointSize = 10.0; gl.uniform1f(pointSizeUniformLocation, desiredPointSize);
// --- 绘制设置 --- gl.clearColor(0.15, 0.15, 0.15, 1.0); // 深灰色背景 gl.enable(gl.DEPTH_TEST); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// --- 绘制调用 --- gl.drawArrays( gl.POINTS, // 原始类型 0, // 起始索引 3 // 要绘制的点数量 );
</script></body></html>预期输出:
这段代码将在深灰色背景上显示三个不同的橙色方形点(大小为 10x10 像素)。根据提供的坐标,一个点将靠近 canvas 左上角,一个靠近 canvas 中上部,一个靠近 canvas 左中部。
进一步探索:
Section titled “进一步探索:”- 改变
desiredPointSize的值。 - 在
pointVertices数组中添加更多点,并更新gl.drawArrays中的数量。 - 修改片段着色器以改变点的颜色。
- 将点的大小设置为属性 (
attribute float a_pointSize;),以便为每个点指定不同的大小(需要将大小数据添加到缓冲区并配置另一个属性指针)。