书摘&心得
- Canvas是CSS中的替换元素。
- 书本中对WebGL只是基础介绍
- 要全面了解 OpenGL,请访问 www.opengl.org。
- 要全面学习 WebGL,请参考 www.learningwebgl.com
- WebGL入门教程:http://www.webgl3d.cn/WebGL/
- Three.js入门教程:http://www.webgl3d.cn/Three.js/
基于react hook和webGL基础教程开发的示例,本文所涉及所有知识点可运行源码均可在此项目中找到:
设置width和height
- width和height表示水平和垂直两个方向上可用的像素数目
- getContext()
- 取得绘图上下文对象的引用
- 检测支持性
toDataURL()
填充:fillStyle
- 描边:strokeStyle
-
2.2 绘制矩形
所涉及方法接收4个参数:矩形的x坐标、矩形的y坐标、矩形的宽度、矩形的高度
- fillRect():在画布上绘制的矩形会填充指定的颜色
- strokeRect()绘制空心矩形,与strokeStyle连用
-
2.3 绘制路径
一种主要的绘图方式
- 绘图流程
- 首先调用beginPath()方法
- 然后调用下列方法来实际绘制路径
- 创建路径后,接下来有几种可能的选择:
- closePath():闭合当前路径
- fill():填充路径
- 所有没有闭合的形状都会自动闭合
- 所有没有闭合的形状都会自动闭合
- stroke():描边路径
- 不会自动闭合
- clip():在路径上创建一个剪切区域
isPointInPath()
WebGL 是针对 Canvas 的 3D 上下文。
- WebGL 涉及的复杂计算需要提前知道数值的精度
- javascript的数值系统无法满足该精度
- WebGL使用类型化数组保存数据
使用时要先创建WebGL上下文
var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if (drawing.getContext){
var gl = drawing.getContext("experimental-webgl");
if (gl){
//使用 WebGL
}
}
核心概念
(1)常量
如GL_COLOR_BUFFER_BIT 常量,指WebGL中的一些系统内置的量。
(2)方法
WebGL中内置的方法,例如,gl.uniform4f()意味着要接收 4 个浮点数,而 gl.uniform3i()则表示要接收 3 个整数。
(3)清除画布
在实际操作 WebGL 上下文之前,一般都要使用某种实色清除
gl.clearColor(0,0,0,1); //black
gl.clear(gl.COLOR_BUFFER_BIT);
(4)定义视口
//视口是<canvas>左下角的四分之一区域
gl.viewport(0, 0, drawing.width/2, drawing.height/2);
//视口是<canvas>左上角的四分之一区域
gl.viewport(0, drawing.height/2, drawing.width/2, drawing.height/2);
//视口是<canvas>右下角的四分之一区域
gl.viewport(drawing.width/2, 0, drawing.width/2, drawing.height/2);
(5)缓冲区
顶点信息保存在 JavaScript 的类型化数组中,使用之前必须转换到 WebGL 的缓冲区。
要创建缓冲区,可以调用 gl.createBuffer(),然后使用 gl.bindBuffer()绑定到 WebGL 上下文。
(6)着色器
WebGL 中有两种着色器:顶点着色器和片段(或像素)着色器。
- 顶点着色器用于将 3D 顶点转换为需要渲染的 2D 点。
- 片段着色器用于准确计算要绘制的每个像素的颜色。
WebGL 着色器的独特之处也是其难点在于,着色器是使用 GLSL(OpenGL Shading Language,OpenGL 着色语言)写的
(7)着色器语言
每个着色器都有一个 main()方法,该方法在绘图期间会重复执行。
着色器传递数据的方式有两种:Attribute 和 Uniform。
执行绘图操作要调用 gl.drawArrays()或 gl.drawElements()方法
- gl.drawArrays()用于数组缓冲区
- gl.drawElements()用于元素数组缓冲区。
参数都是一个常量,表示要绘制的形状。
WebGL 的纹理可以使用 DOM 中的图像。
- 要创建一个新纹理,可以调用 gl.createTexture(),然后再将一幅图像绑定到该纹理。
- 必须在图片的 load 事件触发后才能设置纹理。
图像、加载到
元素中的视频,甚至其他元素都可以用作纹理。实现绘制一个多彩立方体
- 结合缓冲区+着色器+着色器语言的一个示例: ```javascript // 绘制一个多彩立方体 import React, { useState, useEffect } from ‘react’;
//顶点着色器源码 var vertexShaderSource = ‘’ + //attribute声明vec4类型变量apos ‘attribute vec4 apos;’ + // attribute声明顶点颜色变量 ‘attribute vec4 a_color;’ + //varying声明顶点颜色插值后变量 ‘varying vec4 v_color;’ +
‘void main() {‘ + ‘ float radian = radians(30.0);’ + //求解旋转角度余弦值 ‘ float cos = cos(radian);’ + //求解旋转角度正弦值 ‘ float sin = sin(radian);’ + //顶点坐标apos赋值给内置变量gl_Position //逐顶点处理数据 //创建旋转矩阵x y z //cos -sin 0 0 //sin cos 0 0 //0 0 1 0 //0 0 0 1 ‘ mat4 m1 = mat4(cos,sin,0,0, -sin,cos,0,0, 0,0,1,0, 0,0,0,1);’ + ‘ mat4 m2 = mat4(1,0,0,0, 0,cos,sin,0, 0,-sin,cos,0, 0,0,0,1);’ + ‘ mat4 m3 = mat4(cos,0,-sin,0, 0,1,0,0, sin,0,cos,0, 0,0,0,1);’ + // ‘ gl_Position = apos;’ + ‘ gl_Position = m1m2m3*apos;’ + // 顶点坐标apos赋值给内置变量gl_Position // ‘ gl_Position = apos;’ + //顶点颜色插值计算 ‘ v_color = a_color;’ + ‘}’;
//片元着色器源码 var fragShaderSource = ‘’ + // 所有float类型数据的精度是lowp ‘precision lowp float;’ + // 接收顶点着色器中v_color数据 ‘varying vec4 v_color;’ + ‘void main(){‘ + // 插值后颜色数据赋值给对应的片元 ‘gl_FragColor = v_color;’ + ‘}’;
//声明初始化着色器函数 function initShader(gl: any,vertexShaderSource: string,fragmentShaderSource: string){ //创建顶点着色器对象 var vertexShader = gl.createShader(gl.VERTEX_SHADER); //创建片元着色器对象 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
//引入顶点、片元着色器源代码 gl.shaderSource(vertexShader,vertexShaderSource); gl.shaderSource(fragmentShader,fragmentShaderSource); //编译顶点、片元着色器 gl.compileShader(vertexShader); gl.compileShader(fragmentShader);
//创建程序对象program var program = gl.createProgram(); //附着顶点着色器和片元着色器到program gl.attachShader(program,vertexShader); gl.attachShader(program,fragmentShader); //链接program gl.linkProgram(program); //使用program gl.useProgram(program); //返回程序program对象 return program; }
// 创建一个缓冲器来存储它的顶点 function initBuffers(gl: any, program: any) { / 创建顶点位置数据数组data,Javascript中小数点前面的0可以省略 / var data=new Float32Array([ .5,.5,.5,-.5,.5,.5,-.5,-.5,.5,.5,.5,.5,-.5,-.5,.5,.5,-.5,.5, //面1 .5,.5,.5,.5,-.5,.5,.5,-.5,-.5,.5,.5,.5,.5,-.5,-.5,.5,.5,-.5, //面2 .5,.5,.5,.5,.5,-.5,-.5,.5,-.5,.5,.5,.5,-.5,.5,-.5,-.5,.5,.5, //面3 -.5,.5,.5,-.5,.5,-.5,-.5,-.5,-.5,-.5,.5,.5,-.5,-.5,-.5,-.5,-.5,.5,//面4 -.5,-.5,-.5,.5,-.5,-.5,.5,-.5,.5,-.5,-.5,-.5,.5,-.5,.5,-.5,-.5,.5,//面5 .5,-.5,-.5,-.5,-.5,-.5,-.5,.5,-.5,.5,-.5,-.5,-.5,.5,-.5,.5,.5,-.5 //面6 ]); / 创建顶点颜色数组colorData / var colorData = new Float32Array([ 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, //红色——面1 .9,0,0, .9,0,0, .9,0,0, .9,0,0, .9,0,0, .9,0,0,//R=0.9——面2 .8,0,0, .8,0,0, .8,0,0, .8,0,0, .8,0,0, .8,0,0,//R=0.8——面3 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, //黄色——面4 .8,0,0, .8,0,0, .8,0,0, .8,0,0, .8,0,0, .8,0,0,//R=0.8——面3 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, //R=1——面6 ]); / 创建缓冲区buffer,传入顶点位置数据data / var buffer = gl.createBuffer(); var aposLocation = gl.getAttribLocation(program,’apos’); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aposLocation); / 创建缓冲区colorBuffer,传入顶点颜色数据colorData / var colorBuffer=gl.createBuffer(); var a_color = gl.getAttribLocation(program,’a_color’); gl.bindBuffer(gl.ARRAY_BUFFER,colorBuffer); gl.bufferData(gl.ARRAY_BUFFER,colorData,gl.STATIC_DRAW); gl.vertexAttribPointer(a_color,3,gl.FLOAT,false,0,0); gl.enableVertexAttribArray(a_color); }
function GLSL(props: { gl: any }) { const { gl } = props;
useEffect(() => { //初始化着色器 var program = initShader(gl, vertexShaderSource, fragShaderSource); initBuffers(gl, program); // 打开GPU深度测试,避免色块覆盖 gl.enable(gl.DEPTH_TEST); // 第2个参数代表从第几个点开始绘制 // 第3个参数代表一共绘制几个点 // gl.drawArrays(gl.LINES, 0, 2); // gl.drawArrays(gl.LINE_LOOP, 0, 3); gl.drawArrays(gl.TRIANGLES, 0, 6); gl.drawArrays(gl.TRIANGLES, 6, 6); gl.drawArrays(gl.TRIANGLES, 12, 6); gl.drawArrays(gl.TRIANGLES, 18, 6); gl.drawArrays(gl.TRIANGLES, 24, 6); gl.drawArrays(gl.TRIANGLES, 30, 6); // LINES 两两相连 // LINE_LOOP 顺序相连并收尾相连成环 // LINE_STRIP 顺序相连,不成环 }, []); return (
); }export default GLSL;
``` 更多实例详见:https://github.com/sdhr27/webGL-in-hook