当前位置:   article > 正文

WebGL系列教程 — 绘制点(版本2、版本3、版本4、版本5)_webgl 版本

webgl 版本

目录

0.WebGL简介:

绘制一个点

绘制一个点(版本2)

绘制一个点(版本3)

绘制一个点(版本4)

实现

绘制一个点(版本5)


0.WebGL简介:

WebGL是一种用于在Web浏览器中绘制3D图形的API,它允许开发者使用JavaScript和OpenGL ES 2.0来创建高性能的3D图形应用程序。WebGL是HTML5标准的一部分,可以在所有现代的桌面和移动浏览器中运行,包括Chrome、Firefox、Safari、Edge和Opera。

WebGL提供了一种在Web浏览器中创建交互式3D图形的方法。它是一个基于着色器的渲染引擎,可以直接在Web浏览器中渲染3D图形,而无需使用任何插件或附加组件。WebGL使用JavaScript和OpenGL ES 2.0 API来提供高性能的图形渲染,可以在各种设备上使用,包括PC、移动设备和智能电视等。

WebGL是建立在HTML5 Canvas元素上的。Canvas元素允许开发者使用JavaScript在浏览器中绘制2D图形。WebGL使用Canvas元素作为其3D渲染的目标,通过将3D渲染结果绘制到Canvas元素上来呈现3D场景。WebGL还提供了许多高级功能,如光照、阴影、纹理映射和反射等,以帮助开发者创建更加逼真的3D图形应用程序。

WebGL开发需要具备一定的数学和图形学知识,熟悉OpenGL ES 2.0的API和着色器编程。开发者需要编写自己的着色器程序来控制渲染流程和图形效果。WebGL的API非常强大,可以创建复杂的3D模型和场景,但同时也需要处理复杂的渲染管道和硬件兼容性问题。

总之,WebGL为Web开发者提供了一种在浏览器中创建高性能3D图形应用程序的方法。它使用JavaScript和OpenGL ES 2.0 API来提供强大的3D渲染能力,可以在各种设备上运行,包括PC、移动设备和智能电视等。开发者需要具备一定的数学和图形学知识,并熟悉OpenGL ES 2.0 API和着色器编程。

绘制一个点

我们初步认识了 webgl,本篇主要围绕绘制一个点的示例,逐步实现下面功能:

  1. 点的位置从 js 传入着色器
  2. 点的大小由 js 传入着色器
  3. 通过鼠标点击绘点
  4. 通过鼠标点击绘点,并改变点的颜色

绘制一个点(版本2)

需求

在上篇中我们在canvas中心绘制了一个点(效果如下),但这点的位置是直接写在顶点着色器中 gl_Position = vec4(0.0, 0.0, 0.0, 1.0);

需求:点的位置从 js 传入着色器

思路

将位置信息从 js 传入着色器有两种方式:attribute 变量、uniform 变量

  • attribute - 传输的是那些与顶点相关的数据
  • uniform - 传输的是那些对于所有顶点都相同的数据

 

 

我们这里使用 attribute 变量,因为每个点都有各自的坐标

Tip:GLSL 中有三种类型的“变量”或者说数据存储类型。每一种类型都有特定的目标和使用方法:: attributes、varyings(复杂,暂不介绍)和uniforms —— Data in WebGL

attribute

attribute 是一种着色器语言(GLSL ES)变量,用于向顶点着色器内传输数据,只有顶点着色器能使用它。

使用 attribute 有如下三步:

  1. 在顶点着色器中声明 attribute 变量
  2. 在顶点着色器中将声明的 attribute 变量赋值给 gl_Position 变量
  3. 向 attribute 变量传输数据

实现

完整代码如下:

  1. // point02.js
  2. // 必须要 ;
  3. const VSHADER_SOURCE = `
  4. // 声明 attribute 变量
  5. attribute vec4 a_Position;
  6. void main() {
  7. // 将声明的 attribute 变量赋值给 gl_Position 变量
  8. gl_Position = a_Position;
  9. gl_PointSize = 10.0;
  10. }
  11. `
  12. const FSHADER_SOURCE = `
  13. void main() {
  14. gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  15. }
  16. `
  17. function main() {
  18. const canvas = document.getElementById('webgl');
  19. const gl = canvas.getContext("webgl");
  20. // 初始化着色器
  21. if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
  22. console.log('初始化着色器失败');
  23. return;
  24. }
  25. // 获取 attribute 变量的存储位置。如果找不到该属性则返回 -1
  26. const a_Position = gl.getAttribLocation(gl.program, 'a_Position')
  27. if (a_Position < 0) {
  28. console.log('找不到 a_Position 的存储位置');
  29. return;
  30. }
  31. // 为顶点 attibute 变量赋值
  32. gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
  33. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  34. gl.clear(gl.COLOR_BUFFER_BIT);
  35. gl.drawArrays(gl.POINTS, 0, 1);
  36. }

Tip:习惯:所有 attribute 的变量以 a_ 开头,所有 uniform 的变量以 u_ 开头。

getAttribLocation

WebGLRenderingContext.getAttribLocation(program, name) 方法返回了给定 WebGLProgram对象 中某属性的下标指向位置。

vertexAttrib3f

vertexAttrib3f 是一系列同族方法中的一个,为顶点 attibute 变量赋值。

一系列方法指:

  1. void gl.vertexAttrib1f(index, v0);
  2. void gl.vertexAttrib2f(index, v0, v1);
  3. // 将数据(v0, v1, v2) 传给 index 指定的 attribute 变量
  4. void gl.vertexAttrib3f(index, v0, v1, v2);
  5. void gl.vertexAttrib4f(index, v0, v1, v2, v3);
  6. // 矢量版本 v(vector)
  7. void gl.vertexAttrib1fv(index, value);
  8. void gl.vertexAttrib2fv(index, value);
  9. void gl.vertexAttrib3fv(index, value);
  10. void gl.vertexAttrib4fv(index, value);
  • gl.vertexAttrib1f 传1个分量。第二第三分量设置为 0.0,第四个分量设置为1.0
  • gl.vertexAttrib3f 传3个分量。第四个分量设置为 1.0,以此类推。

矢量版本以 v 结尾,接受类型化数组。就像这样:

  1. const floatArray = new Float32Array([10.0, 5.0, 2.0]);
  2. gl.vertexAttrib3fv(a_foobar, floatArray);

绘制一个点(版本3)

需求

需求:点的大小由 js 传入着色器 —— 在版本2的基础上实现

实现

和版本2的实现类似:

  1. const VSHADER_SOURCE = `
  2. attribute vec4 a_Position;
  3. +attribute float a_PointSize;
  4. void main() {
  5. // 将声明的 attribute 变量赋值给 gl_Position 变量
  6. gl_Position = a_Position;
  7. - gl_PointSize = 10.0;
  8. + gl_PointSize = a_PointSize;
  9. }
  10. `
  11. function main() {
  12. gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
  13. + const a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
  14. + if (a_PointSize < 0) {
  15. + console.log('找不到 a_PointSize 的存储位置');
  16. + return;
  17. + }
  18. + // 为顶点 attibute 变量赋值
  19. + gl.vertexAttrib1f(a_PointSize, 10.0);
  20. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  21. gl.clear(gl.COLOR_BUFFER_BIT);

绘制一个点(版本4)

需求

需求:通过鼠标点击绘点

效果如下:

实现

完整代码如下:

 

  1. const VSHADER_SOURCE = `
  2. // 声明 attribute 变量
  3. attribute vec4 a_Position;
  4. attribute float a_PointSize;
  5. void main() {
  6. // 将声明的 attribute 变量赋值给 gl_Position 变量
  7. gl_Position = a_Position;
  8. gl_PointSize = a_PointSize;
  9. }
  10. `
  11. const FSHADER_SOURCE = `
  12. void main() {
  13. gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  14. }
  15. `
  16. function main() {
  17. const canvas = document.getElementById('webgl');
  18. const gl = canvas.getContext("webgl");
  19. // 初始化着色器
  20. if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
  21. console.log('初始化着色器失败');
  22. return;
  23. }
  24. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  25. gl.clear(gl.COLOR_BUFFER_BIT);
  26. const a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
  27. if (a_PointSize < 0) {
  28. console.log('找不到 a_PointSize 的存储位置');
  29. return;
  30. }
  31. // 为顶点 attibute 变量赋值
  32. gl.vertexAttrib1f(a_PointSize, 10.0);
  33. // 获取 attribute 变量的存储位置。如果找不到该属性则返回 -1
  34. const a_Position = gl.getAttribLocation(gl.program, 'a_Position')
  35. if (a_Position < 0) {
  36. console.log('找不到 a_Position 的存储位置');
  37. return;
  38. }
  39. // 存储所有点
  40. const points = [];
  41. // 注册点击事件
  42. $(canvas).click(event => {
  43. // 将点击的坐标转为 webgl 坐标系统。
  44. const rect = event.target.getBoundingClientRect();
  45. const x = ((event.clientX - rect.left) - canvas.width / 2) / (canvas.width / 2);
  46. const y = (canvas.height / 2 - (event.clientY - rect.top)) / (canvas.height / 2);
  47. console.log(x, y)
  48. // 将点保存
  49. points.push({ x, y })
  50. // 注:使用预设值来清空缓冲。如果注释这行,颜色缓冲区会被 webgl 重置为默认的透明色(0.0, 0.0, 0.0, 0.0) —— 必须
  51. gl.clear(gl.COLOR_BUFFER_BIT);
  52. // 绘点
  53. points.forEach(item => {
  54. // 为顶点 attibute 变量赋值
  55. // 注:即使 x, y 是整数程序也没问题
  56. gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
  57. gl.drawArrays(gl.POINTS, 0, 1);
  58. })
  59. })
  60. }

核心思路如下:

  • 给 canvas 注册点击事件(这里引入jQuery)
  • 点击时将 (x, y) 转为 webgl 坐标(怎么转换?百度搜索,这不是重点),并将该点存入一个变量 points 中
  • 循环 points 不停绘制点

:循环前得通过 gl.clear 清空绘图区,否则canvas背景会被重置为透明色。

绘制一个点(版本5)

需求

需求:通过鼠标点击绘点,并改变点的颜色 —— 在版本4的基础上实现

效果如下:

思路:颜色从硬编码改成从 js 传入。使用 uniform 变量。

uniform

前面我们用 attribute 向顶点着色器传输顶点的位置,只有顶点着色器才能使用 attribute。

对于片元着色器,需要使用 uniform 变量。

使用 uniform 有如下三步(和 attribute 类似):

  1. 在顶点着色器中声明 uniform 变量
  2. 在顶点着色器中将声明的 uniform 变量赋值给 gl_FragColor 变量
  3. 向 uniform 变量传输数据

实现

完整代码:

  1. const VSHADER_SOURCE = `
  2. attribute vec4 a_Position;
  3. attribute float a_PointSize;
  4. void main() {
  5. gl_Position = a_Position;
  6. gl_PointSize = a_PointSize;
  7. }
  8. `
  9. const FSHADER_SOURCE = `
  10. // 片元着色器必须加上精度描述。否则浏览器报错:No precision specified for (float)
  11. precision mediump float;
  12. // 声明变量
  13. uniform vec4 u_FragColor;
  14. void main() {
  15. gl_FragColor = u_FragColor;
  16. }
  17. `
  18. function main() {
  19. const canvas = document.getElementById('webgl');
  20. const gl = canvas.getContext("webgl");
  21. if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
  22. console.log('初始化着色器失败');
  23. return;
  24. }
  25. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  26. gl.clear(gl.COLOR_BUFFER_BIT);
  27. const a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
  28. if (a_PointSize < 0) {
  29. console.log('找不到 a_PointSize 的存储位置');
  30. return;
  31. }
  32. gl.vertexAttrib1f(a_PointSize, 10.0);
  33. const a_Position = gl.getAttribLocation(gl.program, 'a_Position')
  34. if (a_Position < 0) {
  35. console.log('找不到 a_Position 的存储位置');
  36. return;
  37. }
  38. // 获取 uniform 变量的存储位置。如果找不到该属性则返回 -1
  39. const u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor')
  40. if (u_FragColor < 0) {
  41. console.log('找不到 u_FragColor 的存储位置');
  42. return;
  43. }
  44. const points = [];
  45. // [0, 1)
  46. const getColor = () => Number.parseFloat(Math.random())
  47. $(canvas).click(event => {
  48. const rect = event.target.getBoundingClientRect();
  49. const x = ((event.clientX - rect.left) - canvas.width / 2) / (canvas.width / 2);
  50. const y = (canvas.height / 2 - (event.clientY - rect.top)) / (canvas.height / 2);
  51. // 将点的随机颜色
  52. points.push({ x, y, rgb: [getColor(), getColor(), getColor()] })
  53. gl.clear(gl.COLOR_BUFFER_BIT);
  54. points.forEach(item => {
  55. gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
  56. // 给 unifrom 变量赋值
  57. gl.uniform4f(u_FragColor, ...item.rgb, 1.0);
  58. gl.drawArrays(gl.POINTS, 0, 1);
  59. })
  60. })
  61. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/727245
推荐阅读
相关标签
  

闽ICP备14008679号