webgl深入理解视图矩阵

news2024/9/24 12:31:41

文章目录

  • 前言
  • 三角形构成三维物体
  • 视点、目标、正方向
  • 视图矩阵
    • 辅助函数:归一化、向量差、点积、叉积
    • 视图矩阵的数学表示与使用
    • 视图矩阵构建三维世界
  • 注意


前言

在前面的学习中,已经得知了webgl是如何绘制二维图形,并进行仿射变换(矩阵变换)、纹理、交互等操作也适用于绘制三维图形。本节将介绍webgl是如何通过视图矩阵进入三维世界的。


三角形构成三维物体

在这里插入图片描述
三角形是三维物体的基本组成单元,如上图所示的一个立方体,绘制在屏幕上其实是由若干个形状大小不一的三角形依次组装而成的。但是,三维与二维相比多出了Z轴一个维度,这个维度代表的是深度信息,有了这个维度能正确不同三角形的遮挡信息,从而绘制出三维物体。

视点、目标、正方向

为了正确表示webgl坐标系中每个点在观察者(或者相机)中的坐标,首先要构建一个属于观察者的三维坐标系统。为了确定观察者的状态,你需要获取三项信息:视点目标点上方向。有了这三项信息,就可以确定物体的三维坐标了,它们的含义如下:

  • 视点

视线的起点,也就是眼睛所在三维空间中的位置(eyeX,eyeY,eyeZ)。

  • 目标点

被观察目标所在的点,当确立目标点和视点时,视线方向也随着确立。目标点的坐标用(atX,atY,atZ)表示。

  • 上方向

最终绘制在屏幕上的影像中的向上的方向,即正方向。当视线方向确定时,观察者还能够以视线为轴旋转的。当指定上方向时,整个坐标就彻底固定住。上方向是具有3 个分量的矢量(upX,upY,upZ)。

在webgl的默认规则中,视点位于坐标系统原点(0,0,0),视线为Z轴负方向(屏幕内),上方向为Y轴负方向(向下)。

视图矩阵

使用 视点目标点上方向 三个矢量可以创建一个视图矩阵(view matrix),将该矩阵传给顶点着色器,重新构建观察者的状态,最终影响了显示在屏幕上的视图。

辅助函数:归一化、向量差、点积、叉积

为了创建视图矩阵,需要使用几个线性几何函数辅助计算,由于javascript中没有相关的数据类型,因此需要自己构建。

  • 向量归一化

将向量转化为单位向量

function normlized (arr) {
    let sum = 0;
    let _mod;
    
    arr.map((item)=>{
        sum += item * item
    })
    _mod = Math.sqrt(sum);
    const normlizedVecArr = 
    arr.map((item) => 
        item / mod
    )
    return normlizedVecArr;
}
  • 向量减法

两点确定指向

function diff (arr1, arr2) {
    if (arr1.length !==3 || arr2.length !==3) return
    return new Float32Array([
        arr1[0] - arr2[0],
        arr1[1] - arr2[1],
        arr1[2] - arr2[2]
    ]);
}
  • 向量点积

用来投影长度

function dot (arr1, arr2) {
    if (arr1.length !==3 || arr2.length !==3) return
    return arr1[0] * arr2[0] + arr1[1] * arr2[1] + arr1[2] * arr2[2];
}
  • 向量叉积

用来获取法向量

function cross (arr1, arr2) {
    if (arr1.length !==3 || arr2.length !==3) return
    return new Float32Array([
        arr1[1] *arr2[2] -  arr1[2] *arr2[1],
        arr1[2] *arr2[0] -  arr1[0] *arr2[2],
        arr1[0] *arr2[1] -  arr1[1] *arr2[0],
    ]);
}

视图矩阵的数学表示与使用

  • 视图矩阵创建
function createViewMatrix(eye_Matrix, look_Matrix, up_Matrix){

    // Z轴
    const Z = normlized(diff( look_Matrix, eye_Matrix));
    // X轴
    const X = normlized(cross(Z, normlized(up_Matrix)));
    // Y轴
    const Y = normlized(cross(Z, X));
    console.log(Z, X, Y)
    
    return new Float32Array([
        X[0],   Y[0],   -Z[0],   0,
        X[1],   Y[1],   -Z[1],   0,
        X[2],   Y[2],   -Z[2],   0,
        -dot(X,eye_Matrix),   -dot(Y,eye_Matrix),  -dot(Z,eye_Matrix),   1,
    ])
}
  • 在webgl中使用
		// 获取canvas元素对象
		let canvas = document.getElementById('canvas');
		// canvas绑定事件
		canvas.onmousedown = function (ev) { click(ev, gl, canvas, aPosition) }
		let pointList = []
		// 获取webgl绘图上下文
		const gl = canvas.getContext('webgl');
		if (!gl) {
			throw new Error('WebGL not supported');
		}

		canvas.width = 500;
		canvas.height = 500;
		gl.viewport(0, 0, canvas.width, canvas.height)

		// 设置背景色
		gl.clearColor(1.0, 1.0, 0.0, 1.0)
		// 清空缓冲区
		gl.clear(gl.COLOR_BUFFER_BIT)

		const vertex = `
			attribute vec4 aPosition;
			uniform mat4 uViewMatrix;
			void main() {
				gl_Position = uViewMatrix * aPosition;
				gl_PointSize = 10.0;
			}
		`
		const fragment = `
			precision highp float;
			void main(){
				gl_FragColor =vec4(1.0,0.0,1.0,1.0);
			}
		`

		// 创建program
		const program = initShader(gl, vertex, fragment)
		// 获取attribute变量的数据存储位置
		const aPosition = gl.getAttribLocation(program, 'aPosition');
		// 获取uniform变量的数据存储位置
		 const uViewMatrix = gl.getUniformLocation(program, 'uViewMatrix');
		// 创建缓冲区对象
		const buffer = gl.createBuffer();
		// 绑定缓冲区对象
		gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
		// 传入的数据
		const vertices = new Float32Array([
			0.0, 0.5, // 顶点着色器补全为(0.0,0.5,0.0,1.0)
			-0.5, -0.5,
			0.5, -0.5
		])
		// 开辟空间并写入数据
		gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
		// 缓冲区对象分配给attribute变量
   		 gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0)
		// 开启attribue变量
    	gl.enableVertexAttribArray(aPosition)

		const viewMatrix = createViewMatrix(
			new Float32Array(), // 视点位置
			new Float32Array(), // 目标点位置
			new Float32Array(), // 上方向
		)
		console.log(viewMatrix)
		gl.uniformMatrix4fv(uViewMatrix, false, viewMatrix)

    	// 开始绘制
    	gl.drawArrays(gl.TRIANGLES, 0, 3)

视图矩阵构建三维世界

在这里插入图片描述
如上图,揭示了视图矩阵与webgl世界坐标之间的关系,下面我们通过几个例子来深入理解视图矩阵。

  • 使用默认规则

前面我们说到,在webgl的默认规则中,视点位于坐标系统原点(0,0,0),视线为Z轴负方向(屏幕内),上方向为Y轴负方向(向下),首先我们以这个规则进行尝试,与默认效果相同。

const viewMatrix = createViewMatrix(
			new Float32Array([0.0, 0.0, 0.0]), // 视点位置
			new Float32Array([0.0, 0.0, 1.0]), // 目标点位置
			new Float32Array([0.0, -1.0, 0.0]), // 上方向
		)

在这里插入图片描述

  • 增加三角形

接下来,我们在缓冲区中存储两个三角形的顶点和颜色信息进行绘制:

let canvas = document.getElementById('canvas');
		// canvas绑定事件
		canvas.onmousedown = function (ev) { click(ev, gl, canvas, aPosition) }
		let pointList = []
		// 获取webgl绘图上下文
		const gl = canvas.getContext('webgl');
		if (!gl) {
			throw new Error('WebGL not supported');
		}

		canvas.width = 500;
		canvas.height = 500;
		gl.viewport(0, 0, canvas.width, canvas.height)

		// 设置背景色
		gl.clearColor(1.0, 1.0, 0.0, 1.0)
		// 清空缓冲区
		gl.clear(gl.COLOR_BUFFER_BIT)

		const vertex = `
			attribute vec4 aPosition;
			uniform mat4 uViewMatrix;
			attribute vec4 aColor;
			varying vec4 v_Color;
			void main() {
				gl_Position = uViewMatrix * aPosition;
				gl_PointSize = 10.0;
				v_Color = aColor;
			}
		`
		const fragment = `
			precision highp float;
			varying vec4 v_Color;
			void main(){
				gl_FragColor = v_Color;
			}
		`

		// 创建program
		const program = initShader(gl, vertex, fragment)
		// 获取attribute变量的数据存储位置
		const aPosition = gl.getAttribLocation(program, 'aPosition');
		const aColor = gl.getAttribLocation(program, 'aColor');
		// 获取uniform变量的数据存储位置
		const uViewMatrix = gl.getUniformLocation(program, 'uViewMatrix');
		// 创建缓冲区对象
		const buffer = gl.createBuffer();
		// 绑定缓冲区对象
		gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
		// 传入的数据
		const vertices = new Float32Array([
			0.0, 0.5, -0.4, 0.4, 1.0, 0.4, // The back green one
			-0.5, -0.5, -0.4, 0.4, 1.0, 0.4,
			0.5, -0.5, -0.4, 1.0, 0.4, 0.4,

			0.5, 0.4, -0.2, 1.0, 0.4, 0.4, // The middle yellow one
			-0.5, 0.4, -0.2, 1.0, 1.0, 0.4,
			0.0, -0.6, -0.2, 1.0, 1.0, 0.4,
		])

		const BYTES = vertices.BYTES_PER_ELEMENT;
		// 开辟空间并写入数据
		gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
		// 缓冲区对象分配给attribute变量
		gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 6 * BYTES, 0)
		gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 6 * BYTES, 3 * BYTES)
		// 开启attribue变量
		gl.enableVertexAttribArray(aPosition)
		gl.enableVertexAttribArray(aColor)

		const viewMatrix = createViewMatrix(
			new Float32Array([0.0, 0.0, 0.0]), // 视点位置
			new Float32Array([0.0, 0.0, 1.0]), // 目标点位置
			new Float32Array([0.0, 1.0, 0.0]), // 上方向
		)
		gl.uniformMatrix4fv(uViewMatrix, false, viewMatrix)

		// 开始绘制
		gl.drawArrays(gl.TRIANGLES, 0, 6)

效果如下:
在这里插入图片描述
修改视点和目标点的位置,会产生不一样的效果。

const viewMatrix = createViewMatrix(
			new Float32Array([0.20, 0.25, 0.25]), // 视点位置
			new Float32Array([0.0, 0.0, 0.0]), // 目标点位置
			new Float32Array([0.0, 1.0, 0.0]), // 上方向
		)

在这里插入图片描述

注意

  • 根据两个三角形的遮挡关系,容易让人以为webgl是右手坐标系,实际上它是左手坐标系,当开启深度检测时,前后关系就会反转。

  • GPU 在绘制过程会经过视口变换后,将所有在 [-1, 1] 之间的坐标映射到屏幕空间中,所以将视点位置设置在两个三角形之间时会看到它们仍然都被绘制。

  • 经过视图矩阵变换后并不能直接转化为屏幕上的坐标,还要经过投影变换(正射/透视)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/340768.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql的架构图

Mysql逻辑架构图主要分三层:1) 第一层负责连接处理,授权认证,安全等等每个客户端连接都会在服务器进程中拥有一个线程,服务器维护了一个线程池,因此不需要为每一个新建的连接创建或者销毁线程。当客户端连接到Mysql服务…

基于JavaEE的“三味”书屋网上售书系统

技术:Java、JSP等摘要:美国最先提出Internet的概念,如今,全球各地的人们纷纷加入到这个网络行列, 使 Internet 真正走向全球化。随着用户、网民越来越多,网络的范围也愈来愈大,领域也慢慢走向了多元化,一体化 。“三味”书屋设计就是网络浪潮…

GORM设计原理和实践(七)——GORM

文章目录一、重点内容:1、GROM设计原理2、GROM配置3、GROM使用即CRUD二、详细知识点介绍:1、GORM设计原理图2、SQL是怎么生成的3、GROM配置开启go model设置go model输入代理:导入依赖:4、GROM操作初体验代码:5、模型定…

【Shell1】shell语法,ssh/build/scp/upgrade,环境变量,自动升级bmc

文章目录1.shell语法:shell是用C语言编写的程序,是用户使用Linux的桥梁,硬件>内核(os)>shell>文件系统1.1 变量:readonly定义只读变量,unset删除变量1.2 函数:shell脚本传递的参数中包含空格&…

app逆向篇之安卓模拟器环境搭建

前言 本教程适配:安卓7以上的安卓模拟器(包括雷电9等安卓9的模拟器) 准备工具 雷电模拟器面具debug版LSPosed 正式步骤 安装雷电模拟器,这里怎么安装就不需要说了吧安装好雷电模拟器之后应该能够在桌面上看到两个图标,如下: …

分页和mmap

文章目录一、内存分页1、基本概念2、分页机制下,虚拟地址和物理地址是如何映射的?3、快表(TLB)二、mmap基本原理和分类一、内存分页 1、基本概念 CPU并不是直接访问物理内存地址,而是通过虚拟地址空间来间接的访问物理内存地址。 页&#x…

CSS ~ 从入门到入坑。

CSS ~ 从入门到入坑。 文章目录CSS ~ 从入门到入坑。what。css 三种实现方式。选择器。id 选择器 > class 选择器 > 标签选择器。标签选择器。类选择器。id 选择器。层次选择器。后代选择器。子选择器。相邻兄弟选择器。通用选择器。结构伪类选择器。属性选择器。字体风格…

OpenAI ChatGPT模型训练

打开VSCode 新建一个工作目录 使用pip install --upgrade openai 配置环境变量&#xff1a; OPENAI_API_KEY<你的open ai key> windows配置:(需要重启) setx OPENAI_API_KEY "你的open ai key" 准备训练数据集文件&#xff1a; 格式如下&#xff1a; 放到工作目…

NSSROUND#8[Basic]

文章目录一、[NSSRound#8 Basic]MyDoor二、[NSSRound#8 Basic]Upload_gogoggo三、[NSSRound#8 Basic]MyPage四、[NSSRound#8 Basic]ez_node一、[NSSRound#8 Basic]MyDoor <?php error_reporting(0);if (isset($_GET[N_S.S])) {eval($_GET[N_S.S]); }if(!isset($_GET[file])…

如何备考系统集成项目管理工程师?

首先了解考试信息&#xff0c;做好备考计划&#xff0c;准备好备考资料等~现在网上也有很多的视频教程&#xff0c;跟着一起学习&#xff0c;大概一周的时间就能过完。如果时间紧张&#xff0c;可以直接抓重点章节真题的复习。考试重点诺&#xff0c;重点章节标红了的&#xff…

NSACE高级WEB安全专家

目录信息收集基本信息收集域名注册信息收集子域名信息收集IP查询系统鉴定端口扫描搜索引擎信息收集 信息收集包括基本信息收集和搜索引擎收集 基本信息收集 基本信息收集方式如下&#xff1a; 域名注册信息查询&#xff1a;例如www.baidu.com 的注册信息&#xff0c;包含注…

vue3的核心知识点合集

Vue3 中文文档(新) Vue.js - 渐进式 JavaScript 框架 | Vue.js vue3的优点&#xff1a; 首次渲染更快diff 算法更快内存占用更少打包体积更小更好的 Typescript 支持Composition API 组合 API首先理解一下vite 构建工具&#xff1a; vite是一种新型前端构建工具&#xff0c;…

解答vue组件中一级组件可不可以作二级组件这个疑惑

引子 大家请看如下情况&#xff0c;我需要做一个项目&#xff0c;首页上的“产品介绍”部分同样也是导航导向的一部分&#xff1a; 首页中的部分&#xff1a; 导航栏导向的部分&#xff1a; 这里我要表达的是&#xff0c;组件Case在导航栏中属于一级路由&#xff0c;而在首…

解决idea出现的java.lang.OutOfMemoryError: Java heap space的问题

文章目录1. 复现问题2. 分析问题3. 解决问题4. 补充解决java.lang.OutOfMemoryError: PermGen space问题1. 复现问题 今天使用idea开发时&#xff0c;突然报出如下错误&#xff1a; Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat org.…

【测试开发】web 自动化测试 --- selenium4

目录1. 什么是自动化为什么要做自动化2. 为什么选择selenium作为我使用的web自动化工具3. 什么是驱动&#xff1f;驱动的工作原理是什么5. 第一个自动化程序演示6. selenium基本语法6.1 定位元素的方法6.2 操作页面元素6.3 等待6.4 信息打印获取当前页面句柄&#xff0c;窗口切…

vue脚手架安装详细

一、vue脚手架安装命令npm i -g vue/cli 或 yarn global add vue/cli安装上面的工具&#xff0c;安装后运行 vue --version &#xff0c;如果看到版本号&#xff0c;说明安装成功或 vue -V工具安装好之后&#xff0c;就可以安装带有webpack配置的vue项目了。创建项目之前&#…

基于java的学生成绩管理系统 完整代码 毕业设计

完整项目代码&#xff1a;https://download.csdn.net/download/qq_38735017/87382417下面是程序界面的详情&#xff1a;分享不易 谢谢&#xff01;主界面首先是开始界面&#xff0c;功能选择窗口&#xff0c;包含 5 个功能按钮清除数据功能清空数据库数据&#xff08;若出现 bu…

问题状态(Conditioning of a problem)

Conditioning of a problem该博客是学习《Numerical Linear Algebra with Applications Using MATLAB》的一些总结&#xff0c;仅供学习使用。 通常即使使用一个稳定的算法来解决一个问题&#xff0c;该问题对数据中小的改变或扰动仍然是敏感的。这些扰动可能来自舍入误差(roun…

Python与Arcgis 获取图像信息

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 这里实现一个很简单的功能,批量的获取图像的相关信息,如影像格式、y分辨率等等,并将这些信息写入到一个csv文件中,同时也会为每个图像生成相应的矩形掩模面。 二、实现代码 在真正执行代码之前,我们首先配置一…

PAT——1111 对称日

央视新闻发了一条微博&#xff0c;指出 2020 年有个罕见的“对称日”&#xff0c;即 2020 年 2 月 2 日&#xff0c;按照 年年年年月月日日 格式组成的字符串 20200202 是完全对称的。 给定任意一个日期&#xff0c;本题就请你写程序判断一下&#xff0c;这是不是一个对称日&a…