webgl如何工作
- WebGL简介:
WebGL(Web Graphics Library)是一种基于JavaScript API的Web图形编程技术,用于在浏览器中实现高性能的3D图形渲染。它是HTML5的一部分,通过使用HTML5的元素和OpenGL ES 2.0的着色语言(GLSL ES语言以字符串形式存在JS中),使得开发者可以利用浏览器的GPU加速能力来创建复杂的交互式3D图形应用程序。
- WebGL特点:
(1)低级别的硬件访问:webgl允许开发者直接访问计算机GPU,图形处理性能更优异;
(2)跨平台支持:webgl基于web标准支持H5,H5的浏览器都可以,无需插件和扩展;
(3)支持2d和3d;
- WebGL应用场景:
数字孪生城市规划(数据可视化、地图操作)、虚拟现实(VR)和增强现实(AR)应用、室内设计模拟和仿真、游戏开发、低代码开发等。
- webgl绘图流程(与canvas对比)
(1)canvas
获取canvas元素 → 获取2d上下文 → 绘图命令队列 → GPU渲染
绘图命令:fillRect、arc、drawImage等
(2)webgl
获取canvas元素 → 获取webgl上下文 → 编写着色器 → 创建缓冲区和顶点数据 → 绘制命令队列 → GPU渲染
绘图命令:gl.drawArrays或gl.drawElements
总言之,canvas是基于2D绘图的,使用简单的绘图api进行绘制,而webgl是基于opengl,需要编写着色器和操作缓冲区来进行高性能的2D和3D图形渲染。webgl绘图流程需要更多的步骤和概念,但它可以实现更复杂和高性能的图形效果。
- GPU渲染过程/渲染管线:
(1)顶点处理(Vertex Processing):将顶点几何信息转化为照相机下的坐标信息,如矩阵变换、投影变换、光照计算、纹理坐标变换等。
(2)图元装配及光栅化(Primitive Assembly and Rasterization):根据顶点和屏幕空间/视口将图元(如点、线段、三角形)装配。光栅化是将几何图元转为图像/位图/片元。
(3)片元处理/片元插值(Fragment Processing)
片元:不是像素,是像素前身,除了rgba之外,还包含深度值、法线、纹理坐标等。
通过片元着色器,计算片元最终颜色和深度,并写入颜色缓冲区。
(3)像素处理(Pixel Processing)
计算其最终的颜色值。这包括对纹理进行采样、应用光照模型、执行像素着色器等操作。像素处理阶段是实现图像真实感和特效的关键阶段。
(4)光栅操作(Raster Operations):此阶段涉及对像素的混合、融合、遮罩、深度测试等操作。这些操作可以控制像素在屏幕上的显示方式,实现遮挡、透明度混合、深度测试等效果。
- 坐标系
webgl默认为右手坐标,物体距离越远越大;相反左手符合人眼,物体距离越远越小;将右手坐标系转换成左手坐标系,z值取反即可,以下是坐标参考图(图1.1,图1.2)。
着色器shader
- 顶点着色器:gl_Position和gl_PointSize
- 片元着色器:gl_FragColor
- 创见着色器过程:
编写opengl着色器源码 → 创建createShader → 指定源码shaderSource → 编译compileShader → 与程序对象关联attachShader → 使用程序linkProgram,useProgram
function initShader(gl, vertexSource, fragmentSource) {
//创建着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
//指定着色器源码
gl.shaderSource(vertexShader, vertexSource);
gl.shaderSource(fragmentShader, fragmentSource);
//编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) || !gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
var info1 = gl.getShaderInfoLog(vertexShader);
var info2 = gl.getShaderInfoLog(fragmentShader);
throw `could not compile webgl shader<br>
vertexShader: ${info1}<br>
fragmentShader: ${info2}
`;
}
//使用着色器,创建一个程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader,);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
return program;
}
GLSLES语言
强类型语言,每一句都需要有分号。它注释语法和 JS 一样,变量名规则也和 JS 一样,不能使用关键字,保留字,不能以数字、 gl_、webgl_ 或 webgl 开头。
①大小写敏感
②强制分号
③通过main函数为入口,无返回值void,等式写在函数里
④注释单行//,多行/**/
⑤条件判断if(){}else{} elseif,循环for while do while,跳出continue,break,discard(该方法只在片元着色器中使用,表示放弃当前片元直接处理下一片元)
- 基本数据类型
GLSL 中主要有三种数据值类型,浮点数、整数和布尔。注意浮点数必须要带小数点。类型转换可以直接使用 float、int 和 bool 函数。
通过int(),float(),bool()转换类型
- 矢量类型,用于处理图像数据
vec2、vec3、vec4具有2、3、4个浮点数的矢量;
ivec2、ivec3、ivec4具有2、3、4个整数的矢量;
bvec2、bvec3、bvec4具有2、3、4个布尔数的矢量;
(1)构造赋值vec4 position=vec4(0.5,0.5,0.0,0.0)
(2)访问矢量里的分量顶点坐标x,y,z,w
纹理坐标s,t,p,q
,如:position.x
,position.xy
- 矩阵类型,用于处理图像数据
mat2(22)、mat3(33)、mat4(4*4)浮点矩阵
//注:矩阵构造入参是列主序
mat4 m =mat4(
1.0, 5.0, 0.0, 1.0,
2.0, 6.0, 0.0, 1.0,
3.0, 7.0, 0.0, 1.0,
4.0, 8.0, 0.0, 1.0,
)
- 取样器 sampler
两种:sampler2D和samplerCube
只能声明成uniform,以下是2D纹理对象使用,webgl是通过纹理单元管理一张纹理图像,可以人为指定纹理编号
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;//精度
uniform sampler2D uSampler;//取样器
varying vec2 vTex;
void main(){
gl_FragColor=texture2D(uSampler,vTex);
}
`;//片元
//创建纹理对象
const texture = gl.createTexture();
//翻转图片Y轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
//开启一个纹理单元
gl.activeTexture(gl.TEXTURE0);//纹理编号
//绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);//(type,texture),type有两种gl.TEXTURE_2D,gl.TEXTURE_CUBE_MAP,texture纹理对象
//处理放大缩小逻辑
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);//(type,pname,param)type同上,pname四种,param两种
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);//gl.TEXTURE_MIN_FILTER缩小
//横向,纵向,平铺
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);//gl.TEXTURE_WRAP_S横向
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);//gl.TEXTURE_WRAP_T纵向
//配置纹理图像
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);//(type,level,internalformat,format,dataType,image),internalformat和format值一致
gl.uniform1i(uSampler, 0);//0为纹理编号
- 内置函数
(1)角度函数:radians角度转弧度,degress弧度转角度
(2)三角函数:sin正弦,cos余弦,tan正切,asin反正弦,acos反余弦,atan反正切
(3)指数函数:pow次方,exp自然质数,log对数,sqrt开平方,inversesqrt开平方的倒数
(4)通用函数:abs绝对值,min最小值,max最大值,mod取余数,sign取符号,floor向下取整,ceil向上取整,clamp限定范围,fract获取小数部分
(5)几何函数:length求长度,distance求两点距离(支持vec2,vec3,vec4),dot点积,cross差积,normalize(x)返回方向同x长度为1的向量
补充:
- 归一化函数normalize:通常用于将图像中的像素值标准化或映射到特定的范围内,使它们符合特定的要求或条件。
归一化函数使用场景:
(1)像素值范围映射;
(2)对比度增强:通过拉伸或映射像素值;
(3)光照校正:比如在设置照射方向时,将图像的亮度范围映射到一个更均匀的分布,从而减少光照影响;
(4)特征提取和分类:图像预处理,使得图像具有相同的尺度和范围;
(5)深度学习中的输入数据处理:通过将输入图像的像素值转换为 [0, 1] 或 [-1, 1] 的范围,可以帮助网络模型更好地收敛和处理数据;
- 存储限定词
声明js与着色器可传递的数据
(1)const常量
(2)atrribute:只在顶点着色器中,只能声明全局变量,存逐个顶点信息,如坐标position
(3)uniform:只读类型,强调一致性,存影响所有顶点的数据,如变换矩阵
(4)varying:从顶点着色器向片元着色器传递数据
精度限定precision,用于提升运行效率,减少内存开销
(1)可单独针对某个变量精度mediump float f;
,存在精度歧义,不利于维护
(2)片元着色器使用浮点类型数据可以设置precision mediump float;
三种枚举:高highp、中mediump、低lowp
在顶点着色器中 int 和 float 都是 highp
在片元着色器中 int 是 mediump,float 没有定义
另外在顶点和片元着色器 sampler2D 和 samplerCube 都是 lowp
缓冲区buffer
解决多个顶点绘制问题
用gl.vertexAttribPointer替代gl.vertexAttrib4f方法
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5
])
//创建buffer缓冲区对象
const buffer = gl.createBuffer();
//绑定给webgl
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);//(target,buffer),target有两种:gl.ARRAY_BUFFER顶点数据,gl.ELEMENT_ARRAY_BUFFER顶点索引值
//const BYTES = points.BYTES_PER_ELEMENT//获取属性字节数
//添加顶点数据,约定类型
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);//(target,data,type)target同上保持一致,type数据类型3种,这里gl.STATIC_DRAW表示写入一次多次绘制
//将缓冲分配给attribute变量
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);//(location,size,type,normalized,stride,offset)size参数数量,normalized是否将数据归一区间,stride两个数据间隔几个字节,offset数据偏移量
//启动attribute
void gl.enableVertexAttribArray(aPosition);
gl.drawArrays(gl.POINTS, 0, 3);
补充说明:
1、缓冲区的存储数据target两种取值:gl.ARRAY_BUFFER顶点数据,gl.ELEMENT_ARRAY_BUFFER顶点索引值,分别对应绘制图形两种方法gl.drawArrays和gl.drawElements
- 动画
requestAnimationFrame解决计时器延迟问题
function animation() {
//更新顶点数据
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);//执行绘制命令
requestAnimationFrame(animation);
}
animation();
矩阵matrix
是一个纵横排列的数据表格m行n列(m*n),作用:转换点坐标,(x,y,z,w)*(m*n矩阵)变成(x`,y`,z`,w`)。
- 平移矩阵,参数是x1,y1,z1方向平移的值,如:x`=x+x1
- 缩放矩阵,参数是x1,y1,z1方向缩放的倍数,如:x`=x*x1
- 旋转矩阵,参数是deg角度,x`=x*cos(deg) - y*sin(deg),
y`=x*sin(deg) + y*cos(deg)
- 投影,包含正射投影和透视投影
(1)正射投影:把可视空间坐标映射到[-1,-1]范围内,参数
(2)透视投影/中心投影:视线和z轴平行,参数有角度、画布宽高比、远距离和近距离 - 视图矩阵:3D图形绘制本质就是webgl通过观察平面创建新的坐标系(映射关系),参数有视点,目标点,上方向/正方向
补充:
(1)3D三要素:视点(眼睛//相机)、目标点(物体)、上方向(正方向)
(2)x轴同时垂直上方向和z轴;上方向垂直z轴;y轴不一定垂直上方向,但同时垂直x轴和z轴;
纹理texture
webgl里使用纹理坐标和图形顶点坐标的映射关系来确定贴图
- 纹理坐标
canvas,img坐标相同x轴正向右,y轴正向下,纹理坐标如下图,
- 纹理单元
webgl通过纹理单元管理一张纹理图像的,图像尺寸尽量是2的整数倍,方便webgl对图形快速采样取值。gl.activeTexture开启纹理单元,可以自定义纹理编号,需要注意图像叠加是有时间顺序的,只有所有图像都加载完才能绘制。以下是实现2D纹理贴图的主要代码:
//片元着色器定义采样器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
uniform sampler2D uSampler;
uniform sampler2D uSampler1;
varying vec2 vTex;
void main(){
vec4 c1=texture2D(uSampler,vTex);
vec4 c2=texture2D(uSampler1,vTex);
gl_FragColor=c1*c2;
}
`;
function getImage(src, location, index) {
return new Promise(resolve => {
const img = new Image();
img.onload = function () {
//创建纹理对象
const texture = gl.createTexture();
//翻转图片Y轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
//开启一个纹理单元
gl.activeTexture(gl[`TEXTURE${index}`]);//纹理编号
//绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);//(type,texture),type有两种gl.TEXTURE_2D,gl.TEXTURE_CUBE_MAP,texture纹理对象
//处理放大缩小逻辑
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);//(type,pname,param)type同上,pname四种,param两种
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);//gl.TEXTURE_MIN_FILTER缩小
//横向,纵向,平铺
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);//gl.TEXTURE_WRAP_S横向
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);//gl.TEXTURE_WRAP_T纵向
//配置纹理图像
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);//(type,level,internalformat,format,dataType,image),internalformat和format值一致
gl.uniform1i(location, index);//0为纹理编号
resolve();
}
//1200*675
img.src = src;
})
}
const tex1 = getImage('./img/sky.jpg', uSampler, 0);
const tex2 = getImage('./img/sky2.jpg', uSampler1, 1);
Promise.all([tex1, tex2]).then(() => {
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
});
几何体案例
功能拆分:
- 绘制2个圆锥(着色器,顶点数据缓冲区)
(1)着色器
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aColor;
uniform mat4 uMvpMatrix;
varying vec4 vColor;
varying vec3 vNormal; // 用于贴图
void main(){
vec4 vertexPosition=uMvpMatrix*aPosition;
gl_Position=vertexPosition;
vColor=aColor;
// 传递法向量。因为位置是以几何中心为原点的
vNormal = normalize(aPosition.xyz);
}
`;
// 片元着色器
const FRAGMENT_SHADER_SOURCE = `
precision mediump float;
varying vec4 vColor;
uniform samplerCube uSampler;
varying vec3 vNormal;
void main(){
gl_FragColor=vColor;
// gl_FragColor=textureCube(uSampler, normalize(vNormal));
}
`;
// 初始化着色器
function initShader(gl, vertexSource, fragmentSource) {
//创建着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
//指定着色器源码
gl.shaderSource(vertexShader, vertexSource);
gl.shaderSource(fragmentShader, fragmentSource);
//编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) || !gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
var info1 = gl.getShaderInfoLog(vertexShader);
var info2 = gl.getShaderInfoLog(fragmentShader);
throw `could not compile webgl shader<br>
vertexShader: ${info1}<br>
fragmentShader: ${info2}
`;
}
//使用着色器,创建一个程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader,);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
return program;
}
(2)复习圆锥几何构造:
从上面看:是一个圆,严格说是一个正N多边形,N值越大,越接近圆。绘制时要用三角函数计算正N多边形的N个顶点坐标。从侧面看是个三角形:最下面是一个顶点,和上面的正N多边形顶点相连构成圆锥网格(图7.1)。
// 定义顶点和颜色缓冲区
function initVertexBuffers(gl) {
let radius = 0.3;
let height = 1;
let divideNum = 60; // 自定义几份数据
let theta = Math.PI * 2 / divideNum; // 一个圆2Π是360°
/**
* 4
* / \
* 3 1
* \ /
* \ 2 /
* \ /
* 0
*/
let vertices = [0, -height / 2, 0];
// let vertices = [];
for (let i = 0; i < divideNum; i++) {
let x = radius * Math.cos(theta * i);
let z = radius * Math.sin(theta * i);
vertices.push(x, height / 2, z);
}
let colors = [];
let baseColor = [[1.0, 0.4, 0.4], [0.4, 1.0, 0.4], [0.4, 0.4, 1.0], [0.2, 0.4, 0.0], [0.4, 0.3, 0.8], [0.8, 1, 0.1]];
colors.push(1.0, 0.4, 0.4);
for (let i = 1; i < vertices.length; i++) {
// let color = baseColor[Math.floor(Math.random()*6)];
// colors = colors.concat(color);
colors.push(0.4, 1.0, 0.4);
}
let indices = [];
for (let i = 1; i <= divideNum; i++) {
if (i === divideNum) {
indices.push(0, i, 1);
} else {
indices.push(0, i, i + 1);
}
}
vertices = Float32Array.from(vertices);
colors = Float32Array.from(colors);
indices = Uint8Array.from(indices);
var indexBuffer = gl.createBuffer();
if (!indexBuffer)
return -1;
if (!initArrayBuffer(gl, program, vertices, 3, gl.FLOAT, 'aPosition'))
return -1;
if (!initArrayBuffer(gl, program, colors, 3, gl.FLOAT, 'aColor'))
return -1;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
return indices.length;
}
function initArrayBuffer(gl, program, data, num, type, attribute) {
var buffer = gl.createBuffer();
if (!buffer) {
console.log('Failed to create the buffer object');
return false;
}
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
var a_attribute = gl.getAttribLocation(program, attribute);
if (a_attribute < 0) {
console.log('Failed to get the storage location of ' + attribute);
return false;
}
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
gl.enableVertexAttribArray(a_attribute);
return true;
}
- 混合矩阵(透视、视图、旋转)
Matrix4库是由<<WebGL编程指南>>作者写的提供WebGL的4*4矩阵操作的方法库
threejs里自带的Matrix4库
import { Matrix4 } from ‘https://unpkg.com/three/build/three.module.js’;
function draw() {
// 获取着色器变量
const uMvpMatrix = gl.getUniformLocation(program, 'uMvpMatrix');
// 设置视觉矩阵,三要素(视点,目标点,上方向)
rotationAngle += 1;
let eyex = 0;
let eyey = 2.0;
let eyez = 5.0;
const eye = [eyex, eyey, eyez];
const lookAt = [0.0, 0.0, 0.0];
const up = [0.0, 1.0, 0.0];
// 设置canvas的背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清理缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
// 开启隐藏面消除
gl.enable(gl.DEPTH_TEST);
// 开启半透明
// gl.enable(gl.BLEND);
// 设置半透明物体
// gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// 贴纹理
// initTexture(gl, program);
var modelMatrix = new Matrix4() //创建模型矩阵
var viewMatrix = new Matrix4() //创建视图矩阵
var projMatrix = new Matrix4() //创建投影矩阵
var mvpMatrix = new Matrix4() //创建模型视图投影矩阵
modelMatrix.rotate(rotationAngle, 1.0, 1.0, 1.0) //设置模型矩阵,旋转
viewMatrix.setLookAt(...eye, ...lookAt, ...up) //设置视点、视线和上方向
projMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100) //设置透视投影矩阵
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix) //将模型矩阵、视图矩阵、投影矩阵相乘赋值给模型视图投影矩阵
gl.uniformMatrix4fv(uMvpMatrix, false, mvpMatrix.elements);
// 绘制
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
// 动画
requestAnimationFrame(draw);
}
- 贴纹理绘制
function initTexture(gl, program) {
const uSampler = gl.getUniformLocation(program, 'uSampler');
let index = 0;
// 创建纹理对象
const texture = gl.createTexture();
// 翻转图片Y轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
// 开启一个纹理单元
gl.activeTexture(gl[`TEXTURE${index}`]);//纹理编号
// 绑定纹理对象
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);//(type,texture),type有两种gl.TEXTURE_2D,gl.TEXTURE_CUBE_MAP,texture纹理对象
// 配置纹理图像
const ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 128;
ctx.canvas.height = 128;
const faceInfos = [
{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_X, faceColor: '#F00', textColor: '#0FF', text: '+X' },
{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, faceColor: '#FF0', textColor: '#00F', text: '-X' },
{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, faceColor: '#0F0', textColor: '#F0F', text: '+Y' },
{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, faceColor: '#0FF', textColor: '#F00', text: '-Y' },
{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, faceColor: '#00F', textColor: '#FF0', text: '+Z' },
{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, faceColor: '#F0F', textColor: '#0F0', text: '-Z' },
];
faceInfos.forEach((faceInfo) => {
const { target, faceColor, textColor, text } = faceInfo;
generateFace(ctx, faceColor, textColor, text);
// 上传画布到立方体贴图的每个面。
const level = 0;
const internalFormat = gl.RGBA;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
// https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/texImage2D
gl.texImage2D(target, level, internalFormat, format, type, ctx.canvas);
});
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.uniform1i(uSampler, index);// 0为纹理编号
}
function generateFace(ctx, faceColor, textColor, text) {
const { width, height } = ctx.canvas;
ctx.fillStyle = faceColor;
ctx.fillRect(0, 0, width, height);
ctx.font = `${width * 0.7}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = textColor;
ctx.fillText(text, width / 2, height / 2);
}
- 主流程
// 获取webgl上下文
const canvas = document.getElementById('webgl');
const gl = canvas.getContext('webgl');// 或getWebGLContext(canvas)
// 着色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);
// 顶点 颜色 索引缓冲区
const n = initVertexBuffers(gl);
// 绘制
draw();
- 效果图(查看)
3d代码细节
- 视图对象前后关系,隐藏面消除与深度冲突处理
开启隐藏面消除gl.enable(gl.DEPTH_TEST);
绘制之前,常清除颜色深度缓冲区gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
- 矩阵乘法,非转换性质,A*B和B*A是不同运算,使用时注意顺序
//复合函数矩阵
function mixMatrix(A,B){
const result=new Float32Array(16);
for (let i = 0; i < 4; i++) {
result[i]=A[i]*B[0]+A[i+4]*B[1]+A[i+8]*B[2]+A[i+12]*B[3];//列乘行
result[i+4]=A[i]*B[4]+A[i+4]*B[5]+A[i+8]*B[6]+A[i+12]*B[7];
result[i+8]=A[i]*B[8]+A[i+4]*B[9]+A[i+8]*B[10]+A[i+12]*B[11];
result[i+12]=A[i]*B[12]+A[i+4]*B[13]+A[i+8]*B[14]+A[i+12]*B[15];
}
return result;
}
- 处理缓冲区内存不足报错GL_INVALID_OPERATION: Insufficient buffer size
图像是一帧一帧绘制的,每一帧图像的状态都被保存到缓冲区叫帧缓冲区,至少包含模板缓冲、深度缓冲、颜色缓冲,使用gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT|gl.STENCIL_CLEAR_VALUE)
学习资料:
《WebGL编程指南》电子书(百度网盘 ,提取码:0619)
知乎-webgl入门知识
WebGL基础文档
绘制圆锥案例
WebGL API查询文档