嗨,大家好呀,我是你们的前端西瓜哥啊。上一节我们学习了 视图矩阵,通过它我们可以像一个自由的摄像机一样,可以在三维世界的任意位置观察目标模型,但也遇到了一些问题。
创新互联公司专注于肃南裕固族自治企业网站建设,成都响应式网站建设公司,成都商城网站开发。肃南裕固族自治网站建设公司,为肃南裕固族自治等地区提供建站服务。全流程按需设计网站,专业设计,全程项目跟踪,创新互联公司专业和态度为您提供的服务
其中一个问题就是,在超过某个临界值时。三角形会出现残缺的现象,甚至直接不见了,见下图。这是为什么呢?
对应源码:
上面残缺的情况会在 z 变得比较大时出现。所谓视图矩阵,其实利用的是一种相对关系。
我们没有移动,坐标系的原点还是在画布的中心位置,但视图矩阵可以计算出模型新的位置,给一个更小的 z,其实就是这个模型的所有顶点在远离我们。
但是,部分像素超出了某个范围,就被 WebGL 直接忽略掉了。
这个范围是多少呢?是 [-1, 1] 区间(大于等于 -1,小于等于 1),x、y、z 维都是这个范围。超出这个范围的像素,一律不画。
利用这个性质,我们可以将一些不需要出现在画面中的物体删除。比如一些遥远的远景,或是跑到视口外的模型,不绘制它们能提高绘制效率。
长方体可视空间是一种常用的可视空间,它由 正射投影(Orthographic Projection) 产生。此外还有更常用的 透视投影,我们后面再聊。
正视投影的作用是:将一个指定尺寸的盒子,映射压缩成 [-1, 1] 范围的盒子,使其中的像素点全部可以绘制出来,之外的像素点则被抛弃。
正射投影的矩阵公式为:
其中 r、l 等值,对应 left、right、top、bottom、near、far 的缩写。
JavaScript 代码实现为:
function createOrthoMatrix(left, right, bottom, top, near, far) {
const width = right - left;
const height = top - bottom;
const depth = far - near;
// prettier-ignore
return new Float32Array([
2 / width, 0, 0, 0,
0, 2 / height, 0, 0,
0, 0, -2 / depth, 0,
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1
]);
}
这里不得不感谢 Github Colipot 提供的支持了。
我们来写个 demo,来体验一下透视矩阵的效果。
为了不引入过多的复杂度,我们只绘制两个三角形,且不引入视图矩阵。然后可以通过上下方向键改变可视空间的 near 和 far 值。此时我们就能看到三角形消失的现象,因为它跑出我们的可视空间了。
两个三角形都平行于 xy 屏幕,只是 z 不同,分别为 0.2 和 -0.2。红色在上边,黄色在下边。
两个三角形的缓冲区数据为:
const verticesColors = new Float32Array([
// 底下的黄色三角形
0, 0.5, -0.1, 1, 1, 0, // 点 1 的位置和颜色信息
-0.5, -0.5, -0.1, 1, 1, 0, // 点 2
0.5, -0.5, -0.1, 1, 1, 0, // 点 3
// 上边的红色三角形
-0.5, 0.25, 0.1, 1, 0, 0,
0.5, 0.25, 0.1, 1, 0, 0,
0, -0.75, 0.1, 1, 0, 0,
]);
效果如下:
这些点都在 -1 到 1 的范围内。所以能全部渲染出来。
顶点着色器要加一个保存正射投影矩阵的 mat4 矩阵类型变量,因为不需要改变,所以使用 uniform 关键字。
const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_OrthoMatrix; // 正射投影矩阵
varying vec4 v_Color;
void main() {
gl_Position = u_OrthoMatrix * a_Position;
v_Color = a_Color;
}
`;
然后绑定键盘按下事件:
window.addEventListener('keydown', (event) => {
switch (event.key) {
case 'ArrowUp':
near += 0.1;
far += 0.1;
break;
case 'ArrowDown':
near -= 0.1;
far -= 0.1;
break;
}
near = Math.round(near * 10) / 10; // 消除浮点数误差
far = Math.round(far * 10) / 10;
render();
});
渲染函数为:
function render() {
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, SIZE * 6, 0);
gl.enableVertexAttribArray(a_Position);
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 6, SIZE * 3);
gl.enableVertexAttribArray(a_Color);
/****** 正射投影 ******/
// prettier-ignore
const orthoMatrix = createOrthoMatrix(
// 左右上下近远
-1, 1, -1, 1, near, far
)
gl.uniformMatrix4fv(u_OrthoMatrix, false, orthoMatrix);
/*** 绘制 ***/
// 清空画布,并指定颜色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
不断地按下方向键,near 和 far 越来越小,即可视空间离我们越来越远。
可以看到,当 near 从 0.1 变成 0 后,红色三角形不见了,黄色的还在。因为我们一开始给红色三角形设置的 z 为 0.1,已经不在可视范围内了。
同理,当 far 越来越大,从 -0.1 变成 0 后,黄色三角形则不见了。
/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");
const infoDiv = document.createElement("div");
document.body.appendChild(infoDiv);
const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_OrthoMatrix; // 正射投影矩阵
varying vec4 v_Color;
void main() {
gl_Position = u_OrthoMatrix * a_Position;
v_Color = a_Color;
}
`;
const fragmentShaderSrc = `
precision highp float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
`;
/**** 渲染器生成处理 ****/
// 创建顶点渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 创建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;
// prettier-ignore
const verticesColors = new Float32Array([
// 底下的黄色三角形
0, 0.5, -0.1, 1, 1, 0, // 点 1 的位置和颜色信息
-0.5, -0.5, -0.1, 1, 1, 0, // 点 2
0.5, -0.5, -0.1, 1, 1, 0, // 点 3
// 上边的红色三角形
-0.5, 0.25, 0.1, 1, 0, 0,
0.5, 0.25, 0.1, 1, 0, 0,
0, -0.75, 0.1, 1, 0, 0,
]);
// 每个数组元素的字节数
const SIZE = verticesColors.BYTES_PER_ELEMENT;
const orthoMatrix = createOrthoMatrix(-1, 1, -1, 1, 1.05, -1);
// 创建缓存对象
const vertexColorBuffer = gl.createBuffer();
// 绑定缓存对象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
// 向缓存区写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
const a_Color = gl.getAttribLocation(gl.program, "a_Color");
const u_OrthoMatrix = gl.getUniformLocation(gl.program, "u_OrthoMatrix");
let near = 1;
let far = -1;
// 初次渲染
render();
window.addEventListener("keydown", (event) => {
switch (event.key) {
case "ArrowUp":
near += 0.1;
far += 0.1;
break;
case "ArrowDown":
near -= 0.1;
far -= 0.1;
break;
}
near = Math.round(near * 10) / 10; // 消除浮点数误差
far = Math.round(far * 10) / 10;
render();
});
function render() {
infoDiv.innerHTML = `Near: ${near}, Far: ${far}`;
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, SIZE * 6, 0);
gl.enableVertexAttribArray(a_Position);
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 6, SIZE * 3);
gl.enableVertexAttribArray(a_Color);
/****** 正射投影 ******/
// prettier-ignore
const orthoMatrix = createOrthoMatrix(
// 左右上下近远
-1, 1, -1, 1, near, far
)
gl.uniformMatrix4fv(u_OrthoMatrix, false, orthoMatrix);
/*** 绘制 ***/
// 清空画布,并指定颜色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
/********* 构造正射投影 *********/
function createOrthoMatrix(left, right, bottom, top, near, far) {
const width = right - left;
const height = top - bottom;
const depth = far - near;
// prettier-ignore
return new Float32Array([
2 / width, 0, 0, 0,
0, 2 / height, 0, 0,
0, 0, -2 / depth, 0,
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1
]);
}
线上体验 demo:
回到开头,我们在使用视图矩阵的时候,修改了模型的顶点坐标,这个坐标容易跑到可视空间外,这就是上面三角形出现残缺的问题。
我们来看看到底发生了什么事。
从表现上看,根据前面我们学到的知识,应该是左下角的点超出了 -1 到 1 的范围。
我们确认一下,用计算出来的视图矩阵乘以左下角点 (-0.2, -0.2, 0, 1):
矩阵运算示意图用 Matrix Calculator 网页工具生成。
我们只要加个正视投影矩阵,并将 near 设置为大于 1.04946 的值即可绘制一个完整的三角形啦。
const orthoMatrix = createOrthoMatrix(
-1, 1, -1, 1, 1.05, -1
)
这个 near 再小一丢丢就会导致残缺,读者可以去 demo 中修改代码验证。
线上体验 demo:
https://codesandbox.io/s/6i9n2y。
分享标题:一起学WebGL:可视空间之正射投影
文章URL:http://www.shufengxianlan.com/qtweb/news10/406060.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联