鸿蒙5.0版开发:ArkUI框架-利用WebGL绘制图形

打印 上一主题 下一主题

主题 872|帖子 872|积分 2616

 往期鸿蒙全套实战文章必看:(文中附带鸿蒙全栈学习资料)



  • 鸿蒙开发焦点知识点,看这篇文章就够了
  • 最新版!鸿蒙HarmonyOS Next应用开发实战学习路线
  • 鸿蒙HarmonyOS NEXT开发技术最全学习路线指南
  • 鸿蒙应用开发实战项目,看这一篇文章就够了(部分项目附源码)

场景介绍

WebGL的全称为Web Graphic Library(网页图形库),主要用于交互式渲染2D图形。目前HarmonyOS中利用的WebGL是基于OpenGL裁剪的OpenGL ES,可以在HTML5的Canvas元素对象中利用,无需利用插件,支持跨平台。WebGL程序是由JavaScript代码组成的,其中利用的API可以利用用户设备提供的GPU硬件完成图形渲染和加速。
说明
   目前该功能仅支持利用兼容JS的类Web开发范式开发。
  基本概念

着色器程序

将缓冲区中的数据推送到着色器中还需涉及“着色器程序”,一个负责关联着色器和缓冲区的JavaScript对象。一个WebGLProgram对象由两个编译事后的 WebGLShader组成,即顶点着色器和片元着色器(均由GLSL语言所写)。
着色器

着色器可以理解为运行在显卡中的指令和数据。在WebGL中,着色器是用OpenGL ES着色语言(GLSL)编写的。
完整的着色器包罗顶点着色器和片元着色器。顶点着色器和片元着色器的交互则涉及到图片光栅化。


  • 顶点着色器:最基本的任务是吸收三维空间中点的坐标,将其处理处罚为二维空间中的坐标并输出。
  • 片元着色器:最基本的任务是对须要处理处罚的屏幕上的每个像素输出一个颜色值。
图片光栅化

将顶点着色器输出的二维空间中的点坐标,转化为须要处理处罚的像素并通报给片元着色器的过程。
帧缓冲对象

帧缓冲区对象为绘图缓冲区提供更换出现目标。它们是颜色、字母、深度和模板缓冲区的聚集,通常用于渲染图像。
纹理

纹理是一种图像,可以应用到3D模子的表面上。WebGL中的纹理有许多属性,包罗宽度、高度、格式和范例。在利用纹理时,须要将其加载到WebGL中,并将其绑定到一个纹理单位上。
变量与接口说明

变量范例

范例对应Web IDL范例形貌GLenumunsigned long用于摆列。GLbooleanboolean纹理true或者false。GLbitfieldunsigned long无符号整数,可以包含多个位标志。每个位标志都代表一个特定的选项。GLbytebyte纹理八位(一个字节),2的补码体现的有符号整数。GLshortshort16位2的补码体现的有符号整数。GLintlong32位2的补码体现的有符号整数。GLsizeilong用来形貌尺寸(例如:绘画缓冲drawing buffer 的宽和高)。GLintptrlong long用来体现指针的特殊范例,通常用于指定缓冲区对象的偏移量。GLsizeiptrlong long用来体现指针的特殊范例,通常用于指定缓冲区对象的巨细。GLubyteoctet八位(一个字节)2的补码体现的无符号整数。GLushortunsigned short16位2的补码体现的无符号整数。GLuintunsigned short32位2的补码体现的有符号整数。GLfloatunrestricted float32位的IEEE标准的浮点数。GLclampfunrestricted float限值32位IEEE浮点数。 接口说明

接口名形貌canvas.getContext获取canvas对象上下文。webgl.createBuffer(): WebGLBuffer | null创建与初始化WebGL数据缓冲区。webgl.bindBuffer(target: GLenum, buffer: WebGLBuffer | null): void将WebGL数据缓冲区与目标举行绑定。webgl.bufferData(target: GLenum, srcData: ArrayBufferView, usage: GLenum, srcOffset: GLuint, length?: GLuint): void创建并初始化WebGL的数据存储区。webgl.getAttribLocation(program: WebGLProgram, name: string): GLint从给定WebGL着色程序中获取着色器中attribute变量的地址。webgl.vertexAttribPointer(index GLuint, size: GLint, type: GLenum, normalized: GLboolean, stride: GLsizei, offset: GLintptr): void将缓冲区对象分配给变量。webgl.enableVertexAttribArray(index: GLuint): void连接变量与分配给它的缓冲区对象。webgl.clearColor(red: GLclampf, green:GLclampf, blue: GLclampf, alpha: GLclampf): void清空<canvas>指定的颜色。webgl.clear(mask: GLbitfield): void清空<canvas>。webgl.drawArrays(mode: GLenum, first:;GLint, count: GLsizei): void实行数据绘制。webgl.flush(): void革新数据至GPU,清空缓冲区。webgl.createProgram(): WebGLProgram | null创建着色器程序对象。 开发步骤

如下以实现一个彩色正方形为例,来演示利用WebGL绘制2D图形的过程。

  • 利用WebGL举行3D渲染前,起首须要一个Canvas元素。以下示例创建了一个Canvas元素并设置一个onclick变乱处理处罚程序来初始化WebGL上下文。
    1. <div class="container">
    2.      <canvas ref="canvas1" style="width : 400px; height : 400px; background-color : lightyellow;"></canvas>
    3.      <button class="btn-button" onclick="BtnColorTriangle">BtnColorTriangle</button>
    4. </div>
    复制代码
  • 设置WebGL的上下文。

    • JavaScript 代码中的 main() 函数将会在文档加载完成之后被调用。它的任务是设置WebGL上下文并开始渲染内容。
    • 当获取到canvas之后,会调用getContext函数并向它通报 "webgl" 参数,来尝试获取WebGLRenderingContext。如果欣赏器不支持WebGL, getContext将会返回null,如果WebGL上下文乐成初始化,变量'gl'会用来引用该上下文。
    1. function main() {
    2.   const canvas = document.querySelector("#glcanvas");
    3.   // 初始化WebGL上下文
    4.   const gl = canvas.getContext("webgl");
    5.   // 确认WebGL支持性
    6.   if (!gl) {
    7.     alert("你的浏览器、操作系统或硬件等可能不支持WebGL。");
    8.     return;
    9.   }
    10.   // 使用完全不透明的黑色清除所有图像
    11.   gl.clearColor(0.0, 0.0, 0.0, 1.0);
    12.   // 用上面指定的颜色清除缓冲区
    13.   gl.clear(gl.COLOR_BUFFER_BIT);
    14. }
    复制代码

  • 定义顶点着色器。
    顶点着色器须要对顶点坐标举行须要的转换,在每个顶点基础上举行其他调整或盘算,然后通过将其保存在由GLSL提供的特殊变量中来返回变更后的顶点。
    在矩阵盘算之前须要先引入gl-matrix开源工具库,可以从gl-matrix官网下载,也可以利用npm命令下载:
    npm install gl-matrix
    1. // 引入mat4
    2. import { mat4 } from 'gl-matrix'
    3. const vsSource = `
    4.     attribute vec4 aVertexPosition;
    5.     uniform mat4 uModelViewMatrix;
    6.     uniform mat4 uProjectionMatrix;
    7.     void main() {
    8.       gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
    9.     }
    10.   `;
    复制代码
  • 定义片段着色器。
    片段着色器在顶点着色器处理处罚完图形的顶点后,会被要绘制的每个图形的每个像素点调用一次。
    1. const fsSource = `
    2.     void main() {
    3.       gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    4.     }
    5. `;
    复制代码
  • 将着色器通报给WebGL。
    定义顶点着色器与片段着色器之后,须要将它们通报给WebGL,并将其编译连接在一起。
    如下代码通过调用 loadShader(),为着色器通报范例和来源。创建了两个着色器。然后创建一个附加着色器的程序,将它们连接在一起。如果编译或链接失败,代码将弹出alert。
    1. // 初始化着色器程序,让WebGL知道如何绘制数据
    2. function initShaderProgram(gl, vsSource, fsSource) {
    3.   const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
    4.   const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
    5.   // 创建着色器程序
    6.   const shaderProgram = gl.createProgram();
    7.   gl.attachShader(shaderProgram, vertexShader);
    8.   gl.attachShader(shaderProgram, fragmentShader);
    9.   gl.linkProgram(shaderProgram);
    10.   // 如果创建失败,将会弹出alert
    11.   if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    12.     alert(
    13.       "无法初始化着色器程序: " +
    14.      gl.getProgramInfoLog(shaderProgram),
    15.     );
    16.     return null;
    17.   }
    18.   return shaderProgram;
    19. }
    20. // 创建指定类型的着色器,上传source源码并编译
    21. function loadShader(gl, type, source) {
    22.   const shader = gl.createShader(type);
    23.   // 将资源发送到着色器对象
    24.   gl.shaderSource(shader, source);
    25.   // 编译着色器程序
    26.   gl.compileShader(shader);
    27.   // 查看是否编译成功
    28.   if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    29.     alert(
    30.    "编译着色器时出错:" + gl.getShaderInfoLog(shader),
    31.     );
    32.     gl.deleteShader(shader);
    33.     return null;
    34.   }
    35.   return shader;
    36. }
    复制代码
  • 查找WebGL返回分配的输入位置。

    • 在创建着色器程序之后,须要查找WebGL返回分配的输入位置。上述有一个属性和两个Uniform。
    • 属性从缓冲区吸收值。顶点着色器的每次迭代都从分配给该属性的缓冲区吸收下一个值。
    • Uniform类似于JavaScript全局变量。它们在着色器的全部迭代中保持相同的值。由于属性的位置是特定于单个着色器程序的,因此将它们存储在一起以易于通报。
    1. const programInfo = {
    2.   program: shaderProgram,
    3.   attribLocations: {
    4.     vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
    5.   },
    6.   uniformLocations: {
    7.     projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
    8.     modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
    9.   },
    10. };
    复制代码

  • 创建缓冲器对象。

    • 在画正方形前,须要创建一个缓冲器来存储它的顶点。
    • 起首调用gl的成员函数createBuffer()得到缓冲对象并存储在顶点缓冲器。然后调用 bindBuffer() 函数绑定上下文。
    • 创建一个Javascript数组去记录每一个正方体的每一个顶点。然后将其转化为WebGL浮点型范例的数组,并将其传到gl对象的bufferData()方法来创建对象的顶点。
    1. function initBuffers(gl) {
    2.   const positionBuffer = initPositionBuffer(gl);
    3.   return {
    4.     position: positionBuffer,
    5.   };
    6. }
    7. function initPositionBuffer(gl) {
    8.   // 为正方形的位置创建一个缓冲区。
    9.   const positionBuffer = gl.createBuffer();
    10.   // 选择positionBuffer作为应用缓冲区的位置。
    11.   gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    12.   // 创建一个正方形的位置数组。
    13.   const positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];
    14.   //将位置列表传递给WebGL以构建形状。
    15.   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    16.   return positionBuffer;
    17. }
    18. export { initBuffers };
    复制代码

  • 渲染场景。

    • 用背景致擦除画布,然后创建摄像机透视矩阵。设置45度的视图角度,并且设置一个适合实际图像的宽高比。指定在摄像机间隔0.1到100单位长度的范围内的物体可见。
    • 加载特定位置,并把正方形放在间隔摄像机6个单位的位置。然后,绑定正方形的顶点缓冲到上下文,并配置好,再通过调用drawArrays()方法来画出对象。
    1. function drawScene(gl, programInfo, buffers) {
    2.   gl.clearColor(0.0, 0.0, 0.0, 1.0);
    3.   gl.clearDepth(1.0); // 清除所有内容。
    4.   gl.depthFunc(gl.LEQUAL);
    5.   // 清除画布。
    6.    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    7.   //创建透视矩阵用于模拟相机中的透视变形。
    8.   const fieldOfView = (45 * Math.PI) / 180;
    9.   const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    10.   const zNear = 0.1;
    11.   const zFar = 100.0;
    12.   const projectionMatrix = mat4.create();
    13.   mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);
    14.   // 将绘制位置设置为标识点,即场景的中心。
    15.   const modelViewMatrix = mat4.create();
    16.   // 开始绘制正方形。
    17.   mat4.translate(
    18.     modelViewMatrix, // 目标矩阵
    19.     modelViewMatrix, // 要转换的矩阵
    20.     [-0.0, 0.0, -6.0],
    21.   );
    22.   {
    23.     const numComponents = 2;
    24.     const type = gl.FLOAT;
    25.     const normalize = false;
    26.     const stride = 0; // 从一组值到下一组值需要多少字节
    27.     const offset = 0;
    28.     gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
    29.     gl.vertexAttribPointer(
    30.       programInfo.attribLocations.vertexPosition,
    31.       numComponents,
    32.       type,
    33.       normalize,
    34.       stride,
    35.       offset,
    36.     );
    37.     gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
    38.   }
    39.   gl.useProgram(programInfo.program);
    40.   gl.uniformMatrix4fv(
    41.     programInfo.uniformLocations.projectionMatrix,
    42.     false,
    43.     projectionMatrix,
    44.   );
    45.   gl.uniformMatrix4fv(
    46.     programInfo.uniformLocations.modelViewMatrix,
    47.     false,
    48.     modelViewMatrix,
    49.   );
    50.   {
    51.     const offset = 0;
    52.     const vertexCount = 4;
    53.     gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
    54.   }
    55. }
    56. // 告诉WebGL如何从位置中拉出位置缓冲到vertexPosition属性中。
    57. function setPositionAttribute(gl, buffers, programInfo) {
    58.   const numComponents = 2;
    59.   const type = gl.FLOAT;
    60.   const normalize = false;  
    61.   const stride = 0; // 从一组值到下一组值需要多少字节
    62.   const offset = 0;
    63.   gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
    64.   gl.vertexAttribPointer(
    65.     programInfo.attribLocations.vertexPosition,
    66.     numComponents,
    67.     type,
    68.     normalize,
    69.     stride,
    70.     offset,
    71.   );
    72.   gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
    73. }
    74. export { drawScene };
    复制代码

最终实现效果示意如下:





免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

水军大提督

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表