要把Cesium,three.js 这些玩明白还是要有WebGL的知识的,不然只是官方demo的ctrl-cv侠,本笔记参考的教程 : 2022年WebGL入门教程(完结)_哔哩哔哩_bilibili
一、初级(二维)
1.1. 坐标系
WebGL的工作区在canvas标签中运行,并与canvas有个边距,其内部是一个空间直角坐标系,x,y,z的范围都是∈[ -1 , 1 ] ,z轴是从屏幕里往外延申,最里面是-1
▲ 图:webGL坐标系
1.2. 生命周期
顶点缓冲区:功能大概像画画一样,先把图形边框绘制出来,绘制出点位
uniform数据:把顶点缓冲区的数据传给顶点着色器
顶点着色器:给顶点画颜色
图元装配:把顶点数据转换为矢量数据,如:存在3个顶点,这3个点是要绘制成一个面,还是三条线,还是一个点一条直线,还是三个点,等等。
光栅器:矢量转栅格
片元着色器:给栅格数据附上纹理和颜色
归属测试:暂定
模板测试:暂定
深度测试:暂定
融合:融合各方面的数据
抖动:动态效果
颜色缓冲区:用户可以看到的区域
1.3. 基础绘制
1.3.1 绘制点 (输入固定位置)
流程:初始化webgl控件、初始化着色器、初始化缓冲区、开始绘制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="glMatrix-0.9.6.min.js"></script>
// 第三方库,集成了一些坐标投影函数
<script>
// 顶点着色器的配置
let vertexstring = `
attribute vec4 a_position;
uniform mat4 proj;
void main(void){
gl_Position =proj * a_position;
gl_PointSize=60.0;
}
`;
// 片元着色器的配置
let fragmentstring = `
void main(void){
gl_FragColor = vec4(0,0,1.0,1.0);
}
`;
var projMat4 = mat4.create(); // 第三方库:投影影矩阵转换功能
var webgl;
function init() {
initWebgl();
initShader();
initBuffer();
draw();
}
// canvas中初始化webgl上下文
function initWebgl() {
let webglDiv = document.getElementById("myCanvas");
webgl = webglDiv.getContext("webgl");
webgl.viewport(0, 0, webglDiv.clientWidth, webglDiv.clientHeight);
mat4.ortho(
0,
webglDiv.clientWidth,
webglDiv.clientHeight,
0,
-1.0,
1.0,
projMat4
);
}
// 配置顶点vertex_shader、片元着色器fragment_shader,建立渲染项目program
function initShader() {
let vsshader = webgl.createShader(webgl.VERTEX_SHADER);
let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);
webgl.shaderSource(vsshader, vertexstring);
webgl.shaderSource(fsshader, fragmentstring);
webgl.compileShader(vsshader);
webgl.compileShader(fsshader);
if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {
var err = webgl.getShaderInfoLog(vsshader);
alert(err);
return;
}
if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {
var err = webgl.getShaderInfoLog(fsshader);
alert(err);
return;
}
let program = webgl.createProgram();
webgl.attachShader(program, vsshader);
webgl.attachShader(program, fsshader);
webgl.linkProgram(program);
webgl.useProgram(program);
webgl.program = program;
}
// 设置点并加入顶点缓冲区:使用attribute和uniform传递数据(这里没有用缓冲区、因为只有一个点)
function initBuffer() {
let pointPosition = new Float32Array([100.0, 100.0, 0.0, 1.0]);
let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");
webgl.vertexAttrib4fv(aPsotion, pointPosition);
let uniformProj = webgl.getUniformLocation(webgl.program, "proj");
webgl.uniformMatrix4fv(uniformProj, false, projMat4);
}
// 开始绘制
function draw() {
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.drawArrays(webgl.POINTS, 0, 1);
}
</script>
</head>
<body onload="init()">
<canvas id="myCanvas" width="1024" height="768"></canvas>
</body>
</html>
1.3.2 绘制点(鼠标动态点击绘制)
相较于固定输入的一个点位,新增的难点:获取点击时候的显示器坐标、显示器坐标转换为WebGL坐标(像素坐标转归一化zuo)
/** 其他省略,和上一个代码区域里面一样
.....
*/
// 使用顶点缓冲区
function initBuffer() {
let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");
document.addEventListener("mousedown", function (e) {
debugger;
let x = e.clientX;
let y = e.clientY;
let rect = e.target.getBoundingClientRect();
let pointx = (x - rect.left - 512) / 512; // 屏幕转WebGL坐标
let pointy = (350 - (y - rect.top)) / 350;
points.push(pointx);
points.push(pointy);
points.push(0);
let pointPosition = new Float32Array(points);
let pointBuffer = webgl.createBuffer(); // 创建缓冲区
webgl.bindBuffer(webgl.ARRAY_BUFFER, pointBuffer); // 绑定缓冲区
webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW); // 缓冲区输入数据
webgl.enableVertexAttribArray(aPsotion); // 启动:开始向GPU传输数据
webgl.vertexAttribPointer(aPsotion, 3, webgl.FLOAT, false, 0, 0); // 配置项:位置、一次从顶点数组取几个数、顶点数组数据类型、是否归一化、间隔字节、偏移字节
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.drawArrays(webgl.POINTS, 0, points.length / 3); // 绘制点
});
let uniformProj = webgl.getUniformLocation(webgl.program, "proj");
webgl.uniformMatrix4fv(uniformProj, false, projMat4);
}
function draw() {
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
}
1.3.3 绘制线
var points = [];
var colors=[];
function initBuffer() {
let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");
let arr = [100.0,100.0,0, 200.0,200.0,0, 300.0,200.0,0 ,400,600,0];
let vertexArr = new Float32Array(arr);
let trangleBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER,trangleBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER,vertexArr,webgl.STATIC_DRAW);
webgl.enableVertexAttribArray(aPsotion);
webgl.vertexAttribPointer(aPsotion,3, webgl.FLOAT, false, 0, 0);
let uniformProj = webgl.getUniformLocation(webgl.program, "proj");
webgl.uniformMatrix4fv(uniformProj, false, projMat4);
}
function draw() {
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.drawArrays(webgl.LINES,0,4); // 绘制线
//webgl.drawArrays(webgl.LINE_STRIP,0,4) // 绘制折现
//webgl.drawArrays(webgl.LINE_LOOP,0,4) // 绘制闭合线
}
1.3.4 绘制三角形
var points = [];
var colors=[];
function initBuffer() {
let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");
let arr = [100.0,100.0,0, 200.0,200.0,0, 300.0,200.0,0 ,400,600,0];
let vertexArr = new Float32Array(arr);
let trangleBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER,trangleBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER,vertexArr,webgl.STATIC_DRAW);
webgl.enableVertexAttribArray(aPsotion);
webgl.vertexAttribPointer(aPsotion,3, webgl.FLOAT, false, 0, 0);
let uniformProj = webgl.getUniformLocation(webgl.program, "proj");
webgl.uniformMatrix4fv(uniformProj, false, projMat4);
}
function draw() {
webgl.clearColor(0.0, 0.0, 0.0, 1.0);
webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
webgl.drawArrays(webgl.TRIANGLES,0,3);
// webgl.drawArrays(webgl.TRIANGLE_STRIP,0,4);
// webgl.drawArrays(webgl.TRIANGLE_FAN,0,4);
}
图 三角形的绘制类型
1.3.5 索引缓冲区
使用索引实现点位复用,以达到不用传入多个相同的点
1.4. 纹理
1.4.1 纹理映射
理论: 在片元着色器中运行。原理:图片四个点作为控制点,然后插值映射到接受区域。
1.4.2 多重纹理
(1)理论:一次性绘制多个纹理
(2) 图像坐标系与纹理坐标系
▲ 图:图像坐标转纹理坐标
1.4.3 动态变换与动画理论
(1)基本理论
Matrix4进行矩阵运算,一秒内快速变换数十次位置,形成动画。
(2)数学运算
▲图:平移、缩放、旋转的矩阵运算
二、 中级(三维)
2.1 视点、目标点、视线
意义:camera视角控制
图:视点、目标点、视线
2.2 可视域
意义:提升渲染效率
(1)正视投影
图:正视投影
(2)透视投影
图:透视投影
2.3 深度缓冲区与多边形偏移
深度缓冲区:三维空间中,保存对象z值的缓冲区
深度冲突:z值相同或十分相近的两个物体对象,会产生重影,解决方案就是深度检测算法
多边形偏移:进行z值的offset偏移设置,使得z值稍微有差距,解决重影问题。
2.4 绘制正方体
略
知识点:三维场景中,逆时针绘画为正面,顺时针为背面
2.5 光照
1. 基本概念
光源:平行光,点光源,环境光(各个方向光照一样)
反射光:镜面反射,漫反射,环境反射
法线:垂直于表面的向量
未完待续