WebGL圖形編程實戰【3】:矩陣操控 × 從二維到三維的跨越

上一篇文章:WebGL圖形編程實戰【2】:動態著色 × 紋理貼圖技術揭秘
倉庫地址:github…、gitee…

矩陣操控

矩陣變換

回到前面關于平移縮放、旋轉的例子當中,我們是通過改變傳遞進去的xy的值來改變的。

在進行基礎變換的時候,涉及到多個變量且變化頻率高,在實際的webgl應用開發過程中,其復雜程度更令人發指,故引入了數學工具—矩陣。(具有規律性的二維數組)計算機實際上是一個固執的老頑童,它最喜歡有規律性質的東西,所以這樣一拍即合,計算機技術與數學理論達成情人關系(webgl內置了矩陣系統)。本堂課的內容就是將變換過程轉換成矩陣進行表示。

矩陣動畫推演

下面先展示了在數學方面上對xyz軸的數據進行相對應的變化,而后是在矩陣層面上變化的值

平移
x1 = x + Tx;
y1 = y + Ty;
z1 = z + Tz;

在這里插入圖片描述

旋轉
x1 = x * cos(angle) - y * sin(angle);
y1 = y * cos(angle) + x * sin(angle);

在這里插入圖片描述

縮放
x1 = x * Sx;
y1 = y * Sy;
z1 = z * Sz;

在這里插入圖片描述

矩陣運算案例

加(減)法

只有同型矩陣之間才可以進行加(減)法運算,將兩個矩陣相同位置的元相加即可,m行n列的兩個矩陣相加(減)后得到一個新的m行n列矩陣,例如

1,2,3,4  +  3,4,5,6  =  4,6,8,10
2,3,4,5  +  2,3,4,5  =  4,6,8,10
數乘

數乘即將矩陣乘以一個常量,矩陣中的每個元都與這個常量相乘,例如

1,2,3  *  3  =  3,6,9
乘法

兩個矩陣的乘法僅當第一個矩陣的列數和另一個矩陣的行數相等時才能定義

1,2,3  *  3,4  =  1*3+2*2+3*3  1*4+2*3+3*4  = 16 22
2,3,4  *  2,3  =  2*3+3*2+4*3  2*4+3*3+4*4  = 24 333,4  =  

glMatrix 常用API

glMatrix API 官網…

補充:在使用glMatrix-0.9.6.min.js和npm上的glMatrix.js,API是有一定區別的,下面是用最新的glMatrix

創建矩陣

返回類型是Float32Array,矩陣的元素個數是16,也就是一個4x4的矩陣。

const matrix = mat4.create();

投影矩陣

生成具有給定邊界的透視投影矩陣。far傳遞null/undefined/no值將生成無限投影矩陣。

mat4.perspective(out, fovy, aspect, near, far);
mat4.perspective(matrix, 45, 4 / 3, 1, 100);
名稱類型描述
outmat4mat4截頭體矩陣將被寫入
fovynumber垂直視場(弧度)
aspectnumber寬高比。通常視口寬度/高度
nearnumber截頭體的近界
farnumber截頭體的遠邊界,可以為null或Infinity

矩陣相乘

將兩個mat 4相乘,參數一為目標矩陣,參數二三為要相乘的矩陣。

const matrix = mat4.create();
const matrix1 = mat4.create();
let target = [];
mat4.multiply(target, matrix, matrix1);

單位矩陣

將一個矩陣設置為單位矩陣。單位矩陣是一個4x4的矩陣,其元素值都為0,除了主對角線元素值都為1。

mat4.identity(matrix);

矩陣變化(平移、旋轉、縮放)

平移縮放旋轉都傳遞了兩個矩陣參數,其中第一個參數是目標矩陣,第二個參數是變化矩陣(不做變換就和第一個傳一樣的值)。第三個參數是變化參數(平移的xyz值、縮放的xyz值、旋轉的弧度和xyz值)。

mat4.translate(matrix, matrix, [10, 10, 10]);
mat4.scale(matrix, matrix, [1, 2, 1]);
mat4.rotate(matrix, matrix, 45, [0, 0, 1]);

WebGL+矩陣變化

整體邏輯如下mermaid圖

數據組裝
uniformMatrix4fv
變化值
賦值給shader
initWebGL
initShader
initBuffer
render
draw

修改著色器,添加一個中間矩陣,然后把中間矩陣傳遞給著色器。版本為0.9.6

const vertexString = `attribute vec4 a_position;uniform mat4 u_formMatrix;void main(){gl_Position = u_formMatrix * a_position;gl_PointSize = 40.0;}`;

在js當中通過glMatrix.js進行矩陣變換,然后用webGL的uniformMatrix4fv方法傳遞給著色器。

uniformMatrix4fv 為 uniform 變量指定矩陣值

  • 參數一:是指定待修改 uniform 變量的存儲位置
  • 參數二:指定是否轉置矩陣
  • 參數三:序列值
function animate() {const middleMat4 = mat4.create();mat4.identity(middleMat4);mat4.translate(middleMat4, [0, 0.5, 0]);mat4.rotate(middleMat4, 0.5 * Math.PI, [0, 0, 1]);mat4.scale(middleMat4, [0.5, 0.5, 0.5]);let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');webGL.uniformMatrix4fv(uniformMatrix, false, middleMat4);
}

案例:WebGL時鐘效果

和上面webGL+矩陣變化的代碼一樣,在頂點著色器當中傳入一個u_formMatrix用來計算,隨后在initBuffer當中重新設置頂點坐標用來繪制三角帶,如下

let triangleArray = [0, -0.1, 0, 1.0,0, 0.4, 0, 1.0,0.01, 0.4, 0, 1.0,0.01, -0.1, 0, 1.0
];
webGL.drawArrays(webGL.TRIANGLE_FAN, 0, 4);

之后就是矩陣變換的代碼,用rotate選擇的方法去改變矩陣,以秒鐘為例,那就是一秒鐘走2*Math.PI弧度除以60,這樣一分鐘60秒剛好一圈,那么代碼就是這樣實現的。

得到當前秒
計算弧度
初始化矩陣
單元化
旋轉矩陣
傳遞矩陣
const second = new Date().getSeconds();
const rotate = 2 * Math.PI / 60 * second;
const middleMat4 = mat4.create();
mat4.identity(middleMat4);
mat4.rotate(middleMat4, -rotate, [0, 0, 1]);
let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
webGL.uniformMatrix4fv(uniformMatrix, false, middleMat4);

隨后分鐘小時的代碼就一樣了,分鐘和秒鐘的計算是一樣的,時針就是將60換成12即可。最后就是添加一個setInterval每隔一秒調用一次。注意:在這里繪制的時候,只需要在秒針繪制的時機先clear一遍,分針和時針的時候直接調用drawArray繪制即可,不用再次clear

在這里插入圖片描述

完整代碼地址:https://github.com/lizuoqun/visualThree/blob/main/webGL/animate/clockTriangle.html

三維世界

視點 & 視線

觀察者的位置就是視點,從視點出發,觀察者能看到的就是視線。

調整視口觀察三維對象

三角形
分層級
設置顏色
lookAt
改變視口觀察

前面的案例研究了xy軸的二維平面對象,而三維就是給z設置了對應的值,而改變觀察的方向就能看到不同的結果。這里以三角形繪制為例,參考前面的代碼實現,先繪制三個三角形,并且修改其z軸的值,在三個不同的平面上

let triangleArray = [0.0, 0.5, -0.4, 1.0,-0.5, -0.5, -0.4, 1.0,0.5, -0.5, -0.4, 1.0,0.5, 0.4, -0.2, 1.0,-0.5, 0.4, -0.2, 1.0,0.0, -0.6, -0.2, 1.0,0.0, 0.4, 0.0, 1,-0.4, -0.4, 0.0, 1,0.4, -0.4, 0.0, 1
];

給每一個層級的三角形設置一下不同的顏色,上面可以區分有三個層級,分別位于z的-0.4、-0.2、0.0三個位置上,修改這個數組對象,就代表著一個點對象有八個數值,分別是x、y、z、1、r、g、b、a,將顏色值和點綁定在一起

let triangleArray = [0.0, 0.5, -0.4, 1.0, 0.4, 1.0, 0.4, 1,-0.5, -0.5, -0.4, 1.0, 0.4, 1.0, 0.4, 1,0.5, -0.5, -0.4, 1.0, 0.4, 1.0, 0.4, 1,0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.4, 1,-0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.4, 1,0.0, -0.6, -0.2, 1.0, 1.0, 0.4, 0.4, 1,0.0, 0.4, 0.0, 1, 0.4, 0.4, 1.0, 1,-0.4, -0.4, 0.0, 1, 0.4, 0.4, 1.0, 1,0.4, -0.4, 0.0, 1, 0.4, 0.4, 1.0, 1
];

修改著色器代碼,這里使用varying變量,先將顏色值傳遞給頂點著色器,再透傳給片元著色器中,根據varying變量的值,設置顏色。

// 頂點著色器
const vertexString = `attribute vec4 a_position;attribute vec4 a_color;varying vec4 color;void main(){gl_Position =  a_position;color = a_color;}`;// 片元著色器
const fragmentString = `precision mediump float;varying vec4 color;void main(){gl_FragColor = color;}`;

進行賦值,在js當中通過vertexAttribPointer將顏色值傳遞給著色器當中的a_color變量,同時因為數組的內容改了,之前是4個數據一個點現在是8個數據一個點,所以設置點的代碼也要調整

// 設置點坐標
webGL.vertexAttribPointer(aPosition, 4, webGL.FLOAT, false, 8 * 4, 0);
// 調整不同層級三角形的顏色
let aColor = webGL.getAttribLocation(program, 'a_color');
webGL.enableVertexAttribArray(aColor);
webGL.vertexAttribPointer(aColor, 4, webGL.FLOAT, false, 8 * 4, 4 * 4);

改變視角:在頂點著色器當中添加u_formMatrix,使用lookAt方法設置視點,并傳遞給著色器中

let modelView = mat4.create();
mat4.identity(modelView);
modelView = mat4.lookAt(modelView, [0, -0.5, 0.2], [0, 0, 0], [0, 1, 0]);
let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
webGL.uniformMatrix4fv(uniformMatrix, false, modelView);

lookAt(out, eye, center, up): 使用給定的眼睛位置、焦點和上方向軸生成注視矩陣

參數說明

名稱類型描述
outmat4截頭體矩陣將被寫入
eyeReadonlyVec3視口位置
centerReadonlyVec3觀看者正在觀看的點
upReadonlyVec3指定上方向

疊加矩陣變化

可以再創建一個新的矩陣進行旋轉90度,之后將視口矩陣和旋轉矩陣乘積重新賦值也就完成了疊加矩陣變化。

let ModelMatrix = mat4.create();
mat4.identity(ModelMatrix);
mat4.rotate(ModelMatrix, ModelMatrix, Math.PI / 2, [0, 0, 1]);let ViewMatrix = mat4.create();
mat4.identity(ViewMatrix);
ViewMatrix = mat4.lookAt(ViewMatrix, [0, 0, 0.3], [0, 0, 0], [0, 1, 0]);
let mvMatrix = mat4.create();
mat4.multiply(mvMatrix, ViewMatrix, ModelMatrix);
// 最后將這個進行賦值
let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
webGL.uniformMatrix4fv(uniformMatrix, false, mvMatrix);

原本都是正向上的三角形就變成了橫的了

在這里插入圖片描述

可視范圍(正射投影)

在上一個案例當中,當視點在極右或極左的位置時,三角形會缺少一部分。原因是沒有指定可視范圍,即實際觀察得到的區域邊界

兩類常用的可視空間:

  • 長方體可視空間,也稱盒狀空間,由正射投影產生
  • 四棱錐/金字塔可視空間,由透視投影產生

可視空間由前后兩個矩形表面確定,分別稱近裁剪面(near)和遠裁剪面(far)

改變視口可視域:ortho(out, left, right, bottom, top, near, far):生成具有給定邊界的正交投影矩陣

名稱類型描述
outmat4輸出矩陣
leftnumber截頭體的左邊界
rightnumber右邊界
bottomnumber底邊界
topnumber上邊界
nearnumber
farnumber

改變視口可視域,通過ortho方法設置可視域范圍,這樣他的坐標系取值就變成了canvas的坐標系,繪制圖形的坐標值也要進行相對應的調整

let ProjMatrix = mat4.create();
mat4.identity(ProjMatrix);
mat4.ortho(ProjMatrix, -100, 100, -100, 100, near, far);    //修改可視域范圍let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
webGL.uniformMatrix4fv(uniformMatrix, false, ProjMatrix);

可視空間(透視投影)

在正射投影的可視空間中,不管三角形與視點的距離是遠是近,它有多大,那么畫出來就有多大。為了打破這條限制,使用透視投影可視空間,它將使場景具有深度性

設置透視投影:perspective(out, fovy, aspect, near, far):生成具有給定邊界的透視投影矩陣

名稱類型描述
outmat4輸出矩陣
fovynumber垂直方向的視野角度(上截面與下截面的角度)
aspectnumber縱橫比(寬高比)
nearnumber
farnumber

創建一個透視投影矩陣,并賦值給uniformMatrix,去修改傳入的角度的時候可以觀察到變化

let ProjMatrix = mat4.create();
mat4.identity(ProjMatrix);
//角度小,看到的物體大,角度大,看到的物體小。
mat4.perspective(ProjMatrix, 160 * Math.PI / 180, 1, 1, 100); //修改可視域范圍

在這里插入圖片描述

正射投影和透視投影的區別
  • 在透視投影下,產生的三維場景看上去更是有深度感,更加自然,因為我們平時觀察真實世界用的也是透視投影。在大多數情況下,比如三維射擊類游戲中,我們都應當采用透視投影。
  • 正射投影的好處是用戶可以方便地比較場景中物體( 比如兩個原子的模型)
    的大小,這是因為物體看上去的大小與其所在的位置沒有關系。在建筑平面圖等技術繪圖的相關場合,應當使用這種投影。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/75905.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/75905.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/75905.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

并發編程--共享內存SHM

共享內存SHM 文章目錄 共享內存SHM1. 基本概念2. 函數接口2.1 創建或打開SHM對象2.2 映射 / 解除映射SHM對象2.3 其余操作2.4示例代碼 1. 基本概念 共享內存,顧名思義,就是通過不同進程共享一段相同的內存來達到通信的目的,由于SHM對象不再交…

Redis中的數據類型與適用場景

目錄 前言1. 字符串 (String)1.1 特點1.2 適用場景 2. 哈希 (Hash)2.1 特點2.2 適用場景 3. 列表 (List)3.1 特點3.2 適用場景 4. 集合 (Set)4.1 特點4.2 適用場景 5. 有序集合 (Sorted Set)5.1 特點5.2 適用場景 6. Redis 數據類型的選型建議結語 前言 Redis 作為一款高性能的…

科技賦能建筑業變革:中建海龍創新引領高質量發展新路徑

在建筑工業化浪潮中,中建海龍科技有限公司(以下簡稱“中建海龍”)憑借深厚的技術積累與持續創新,成為推動行業轉型升級的標桿企業。作為中國建筑國際集團旗下核心科技力量,中建海龍深耕模塊化集成建筑(MiC&…

Vue下 Sortable 實現 table 列表字段可拖拽排序,顯示隱藏組件開發

vue 開發table 列表時&#xff0c;需要動態調整列字段的順序和顯示隱藏 實現效果如圖所示&#xff1a; vue 組件代碼 <template><div style"width: 90%; margin: 0 auto;"><el-table :data"tableData" border"" ref"table…

故障扭曲棱鏡反射照片效果ps特效濾鏡樣機 Distorted Mirror Poster Effect

只需單擊幾下即可執行令人著迷的高質量圖像和攝影&#xff01;此照片效果包含智能對象圖層&#xff0c;提供完全自定義、易用性和多功能性的工作流程。只需雙擊其縮略圖打開所需的圖層&#xff0c;刪除占位符鏡頭&#xff0c;添加圖形&#xff0c;保存它&#xff0c;然后觀看 P…

基于dify平臺批量分析excel格式信息

如何以表格形式批量輸入一些信息&#xff0c;然后讓大模型以對話應用形式逐條進行推理分析&#xff1f; 這里提供一個分步解決方案&#xff0c;結合 Dify平臺功能 和 API調用優化 的思路&#xff0c;既保證效率又降低復雜度&#xff1a; 1. 優先檢查 Dify 的「數據集」功能 Di…

CARLA常見技術問題集錦(一)地圖與場景構建篇

編者薦語&#xff1a; 在自動駕駛技術加速落地的今天&#xff0c;CARLA 仿真引擎憑借其開源生態與高保真仿真能力&#xff0c;已成為全球開發者構建智能駕駛算法的核心工具之一。隨著虛幻引擎 5.5 的全面升級&#xff0c;CARLA 0.10.0 版本實現了視覺革命&#xff1a;Lumen 全…

vue+webpack5(高級配置)

項目地址 基礎配置可查看文檔 1、devtool 配置 (找到報錯位置)2、優化打包速度3、oneOf 每個文件只被一個loader處理4、 include/exclude 處理某些文件或者排除某些文件5、 cache 緩存 &#xff08;提升后面幾次的打包速度&#xff09;6、 多進程打包7、減少代碼體積 Tree Shak…

JavaWeb——事務管理、AOP

目錄 一、事管理 1.開啟事務管理日志 2.開啟事務管理 3.傳播行為 二、AOP 1.通知類型 2.通知順序 3.切入點表達式 4.連接點 一、事務管理 1.開啟事務管理日志 2.開啟事務管理 3.傳播行為 當一個事務方法被另一個事務方法調用時&#xff0c;這個事物方法應該如何進行事…

okhttp3網絡請求

一、使用okhttp3和gson build.gradle ... dependencied {...implementation com.squareup.okhttp3:okhttp:3.9.0implementation com.google.code.gson:gson:2.10.1 }二、響應模型 可根據實際情況進行調整&#xff0c;目前我所需的就是這三個變量 HttpResponseData.java im…

【藍橋杯每日一題】3.28

&#x1f3dd;?專欄&#xff1a; 【藍橋杯備篇】 &#x1f305;主頁&#xff1a; f狐o貍x "今天熬的夜&#xff0c;會變成明天獎狀的閃光點&#xff01;" 目錄 一、唯一的雪花 題目鏈接 題目描述 解題思路 解題代碼 二、逛畫展 題目鏈接 題目描述 解題思路 解題代…

【MinIO】Bucket的生命周期管理

&#x1f47b;創作者&#xff1a;丶重明 &#x1f47b;創作時間&#xff1a;2025年3月7日 &#x1f47b;擅長領域&#xff1a;運維 目錄 1.ILM使用介紹2.生命周期配置實例 1.ILM使用介紹 對象生命周期管理&#xff08;ILM&#xff09;是現代對象存儲系統的核心功能之一&#x…

Android 中隱藏標題欄和狀態欄的方法

在Android開發中&#xff0c;隱藏標題欄和狀態欄是實現全屏顯示的常見需求。 一、隱藏標題欄 1、通過代碼隱藏 對于繼承自 AppCompatActivity 的 Activty&#xff0c;可在 onCreate() 方法中調用supportRequestWindowFeature 或 getSupportActionBar 方法來隱藏標題欄。 ove…

進程間通信——信號量

進程間通信——信號量 目錄 一、基本概念 1.1 概念 1.2 基本操作 1.3 相關函數 1.3.1 semget創建/獲取 1.3.2 semop操作信號量 1.3.3 semctl初始化/刪除 二、代碼操作 2.1 不用PV的 2.2 用PV 的 2.2.1 a.c 2.2.2 b.c 2.2.3 sem.h 2.2.4 sem.c 一、基本概念 1.1…

Linux內核2-TFTP與NFS環境搭建

Uboot&#xff1a;引導程序 初始化硬件設備&#xff0c;初始化c語言環境&#xff0c;為內核加載做準備 zImage:內核文件 rootfs:文件系統&#xff0c;為用戶提供一個與硬件設備數據交互的系統 1.TFTP和NFS功能 TFTP:簡單文件傳輸協議網絡配置 pc可以下載 2.minicom bootargs…

TDengine 中的命名與邊界

簡介 本章主要介紹命名的合法字符集和限制規則&#xff0c;這對于正確使用 TDengine&#xff0c;減小報錯很重要&#xff0c;這些規則在 SQL 語句中都生效&#xff0c;在使用過程中要注意&#xff0c;避免不必要的錯誤。 名稱命名規則 合法字符&#xff1a;英文字符、數字和…

C++ 中將函數作為參數傳遞

C 中將函數作為參數傳遞 1. 通過指針傳遞函數 函數可以通過傳遞函數的地址來作為參數傳遞&#xff1b;簡而言之&#xff0c;就是通過指針實現這一點。 示例代碼 #include <iostream> using namespace std;// 定義加法和減法函數 #include <iostream> #include …

Vala 編程語言教程-繼承

繼承? 在 Vala 中&#xff0c;一個類可以繼承自 ?一個或零個? 其他類。盡管實際開發中通常繼承一個類&#xff08;不同于 Java 等語言的隱式繼承機制&#xff09;&#xff0c;但 Vala 并不強制要求必須繼承。 當定義繼承自其他類的子類時&#xff0c;子類的實例與父…

Crypto Architecture Kit簡介

HarmonyOS 5.0.3(15) 版本的配套文檔&#xff0c;該版本API能力級別為API 15 Release 文章目錄 約束與限制能力范圍基本概念與相關Kit的關系 Crypto Architecture Kit屏蔽了第三方密碼學算法庫實現差異的算法框架&#xff0c;提供加解密、簽名驗簽、消息驗證碼、哈希、安全隨機…

交流電機類型及其控制技術

交流電機可分為同步電機和異步電機兩大種類&#xff0c;如果電機轉子的轉速與定子旋轉磁場的轉速相等&#xff0c;轉子與定子旋轉磁場在空間同步地旋轉&#xff0c;這種電機就稱為同步電機。如果電機轉子的轉速不等于定子旋轉磁場的轉速&#xff0c;轉子與定子旋轉磁場在空間旋…