WebGL基礎(一): 從一個滑鼠畫點開始瞭解原生webGL | IT人

文章推薦指數: 80 %
投票人數:10人

面向web前端的WebGL教程,網路上的教程均是假設有計算機圖形學基礎,對web開發者來說不是很 ... webGL如何展示一個點首先得知道webGL如何展示出一個點? Togglenavigation IT人 IT人 WebGL基礎(一):從一個滑鼠畫點開始瞭解原生webGL undefined發表於 2022-03-14 WebGL 面向web前端的WebGL教程,網路上的教程均是假設有計算機圖形學基礎,對web開發者來說不是很友好,故開闢此坑最終效果https://codepen.io/chendonmin...滑鼠點選畫一個點。

webGL如何展示一個點首先得知道webGL如何展示出一個點?webGL畫任意物體都需要一個頂點著色器和片元著色器,頂點著色器:描述頂點的特性(位置、顏色等)的程式.片元著色器:進行著片元處理過程的程式。

也許你會很懵,一大堆官方理論又要望而卻步了,所以我直接展示下最簡單的展示一個點的程式碼,相信你會馬上明白。

你的瀏覽器似乎不支援或者禁用了HTML5<canvas>元素. 首先,我需要一些簡單的封裝函式:functioninitShaders(gl,vshader,fshader){ varprogram=createProgram(gl,vshader,fshader); if(!program){ console.log('Failedtocreateprogram'); returnfalse; } gl.useProgram(program); gl.program=program; returntrue; } /** *Createthelinkedprogramobject *@paramglGLcontext *@paramvshaderavertexshaderprogram(string) *@paramfshaderafragmentshaderprogram(string) *@returncreatedprogramobject,ornullifthecreationhasfailed */ functioncreateProgram(gl,vshader,fshader){ //Createshaderobject varvertexShader=loadShader(gl,gl.VERTEX_SHADER,vshader); varfragmentShader=loadShader(gl,gl.FRAGMENT_SHADER,fshader); if(!vertexShader||!fragmentShader){ returnnull; } //Createaprogramobject varprogram=gl.createProgram(); if(!program){ returnnull; } //Attachtheshaderobjects gl.attachShader(program,vertexShader); gl.attachShader(program,fragmentShader); //Linktheprogramobject gl.linkProgram(program); //Checktheresultoflinking varlinked=gl.getProgramParameter(program,gl.LINK_STATUS); if(!linked){ varerror=gl.getProgramInfoLog(program); console.log('Failedtolinkprogram:'+error); gl.deleteProgram(program); gl.deleteShader(fragmentShader); gl.deleteShader(vertexShader); returnnull; } returnprogram; } /** *Createashaderobject *@paramglGLcontext *@paramtypethetypeoftheshaderobjecttobecreated *@paramsourceshaderprogram(string) *@returncreatedshaderobject,ornullifthecreationhasfailed. */ functionloadShader(gl,type,source){ //Createshaderobject varshader=gl.createShader(type); if(shader==null){ console.log('unabletocreateshader'); returnnull; } //Settheshaderprogram gl.shaderSource(shader,source); //Compiletheshader gl.compileShader(shader); //Checktheresultofcompilation varcompiled=gl.getShaderParameter(shader,gl.COMPILE_STATUS); if(!compiled){ varerror=gl.getShaderInfoLog(shader); console.log('Failedtocompileshader:'+error); gl.deleteShader(shader); returnnull; } returnshader; }這是一段初始化著色器的函式初始化webgl:constcanvas=document.querySelector("#glcanvas"); //初始化WebGL上下文 constgl=canvas.getContext("webgl"); //確認WebGL支援性 if(!gl){ alert("無法初始化WebGL,你的瀏覽器、作業系統或硬體等可能不支援WebGL。

"); return; } //使用完全不透明的黑色清除所有影像 gl.clearColor(0.0,0.0,0.0,1.0); //用上面指定的顏色清除緩衝區 gl.clear(gl.COLOR_BUFFER_BIT);呼叫初始化著色器函式。

constVSHADER_SOURCE=` voidmain(){ gl_Position=vec4(0.0,0.0,0.0,1.0); gl_PointSize=10.0; } `; constFSHADER_SOURCE=` voidmain(){ gl_FragColor=vec4(1.0,0.0,0.0,1.0); } `; initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE); gl.drawArrays(gl.POINTS,0,1);OK,目前為止,你應該能看到黑色canvas中間有個紅色的點了。

解析最關鍵的部分,其實就是VSHADER_SOURCE和FSHADER_SOURCE兩個字串,分別表示了點的座標和點的顏色。

VSHADER_SOURCE和FSHADER_SOURCE是屬於glsl程式碼,VSHADER_SOURCE中的gl_Position代表的就是點的位置,gl_Position是glsl的內建變數。

你會發現gl_Position的值是個vec4型別,座標居然有4個值?其實這個是齊次座標.對於vec4(x,y,z,w),真實的世界座標是(x/w,y/w,z/w),所以一般vec4第四個引數我們設定為1。

為什麼需要齊次座標呢,因為三維世界中,向量也是三個座標表示的,所以為了區分向量和真實位置引入了第四個引數,向量的第四個引數是0.js和GLSL通訊上面程式碼確實畫出一個點,但是是寫在一個字串中的,這肯定不方便我們進行操作啊,所以操作glsl中的變數就很有必要了。

constVSHADER_SOURCE=` attributevec4a_Position; voidmain(){ gl_Position=a_Position; gl_PointSize=10.0; } `;如上圖,我們對頂點著色器程式碼加入了一個attribute,然後attributea_Position賦值給glsl內建變數gl_Position,這是否意味著我改動a_Position的值,gl_Position也會改變呢?js獲取並修改attribute需要的API:gl.getAttribLocation(gl.program,attribute); gl.vertexAttrib3f(index,x,y,z);getAttribLocation方法返回了給定WebGLProgram物件中某屬性的下標指向位置vertexAttrib3f可以為頂點attibute變數賦值現在只需要在gl.drawArrays(gl.POINTS,0,1);之前修改attribute即可vara_Position=gl.getAttribLocation(gl.program,"a_Position"); gl.vertexAttrib3f(a_Position,0.0,0.0,0.0);目前為止,完整程式碼如下:constcanvas=document.querySelector("#glcanvas"); //初始化WebGL上下文 constgl=canvas.getContext("webgl"); //確認WebGL支援性 if(!gl){ alert("無法初始化WebGL,你的瀏覽器、作業系統或硬體等可能不支援WebGL。

"); return; } //使用完全不透明的黑色清除所有影像 gl.clearColor(0.0,0.0,0.0,1.0); //用上面指定的顏色清除緩衝區 gl.clear(gl.COLOR_BUFFER_BIT); constVSHADER_SOURCE=` attributevec4a_Position; voidmain(){ gl_Position=a_Position; gl_PointSize=10.0; } `; constFSHADER_SOURCE=` voidmain(){ gl_FragColor=vec4(1.0,0.0,0.0,1.0); } `; //初始化著色器 initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE); vara_Position=gl.getAttribLocation(gl.program,"a_Position"); gl.vertexAttrib3f(a_Position,0.0,0.0,0.0); //畫點 gl.drawArrays(gl.POINTS,0,1);畫多個點gl.vertexAttrib3f(a_Position,0.0,0.0,0.0); gl.drawArrays(gl.POINTS,0,1); gl.vertexAttrib3f(a_Position,0.5,0.5,0.0); gl.drawArrays(gl.POINTS,0,1);你會發現螢幕上存在了兩個紅點。

drawArrays方法用於從向量陣列中繪製圖元,每執行一次就要通知GPU渲染圖元。

現在兩個點還好,如果是成千上萬個點呢?我們需要一次性畫多個點,這樣才能保持效能。

型別化陣列TypedArray對於多個點,我們需要把點的位置存在變數中,我們選擇了TypedArray,它相比普通Array有幾個好處:效能效能還tm是效能。

對於typedArray介紹看如下程式碼://下面程式碼是語法格式,不能直接執行, //TypedArray關鍵字需要替換為底部列出的建構函式。

newTypedArray();//ES2017中新增 newTypedArray(length); newTypedArray(typedArray); newTypedArray(object); newTypedArray(buffer[,byteOffset[,length]]); //TypedArray指的是以下的其中之一: Int8Array(); Uint8Array(); Uint8ClampedArray(); Int16Array(); Uint16Array(); Int32Array(); Uint32Array(); Float32Array(); Float64Array();那怎麼選擇呢,看如下列表:型別單個元素值的範圍大小(bytes)描述WebIDL型別C語言中的等價型別Int8Array-128to12718位二進位制有符號整數byteint8_tUint8Array0to25518位無符號整數(超出範圍後從另一邊界迴圈)octetuint8_tUint8ClampedArray0to25518位無符號整數(超出範圍後為邊界值)octetuint8_tInt16Array-32768to32767216位二進位制有符號整數shortint16_tUint16Array0to65535216位無符號整數unsignedshortuint16_tInt32Array-2147483648to2147483647432位二進位制有符號整數longint32_tUint32Array0to4294967295432位無符號整數unsignedlonguint32_tFloat32Array1.2×10^-38to3.4×10^38432位IEEE浮點數(7位有效數字,如1.1234567)unrestrictedfloatfloatFloat64Array5.0×10^-324to1.8×10^308864位IEEE浮點數(16有效數字,如1.123...15)unrestricteddoubledoubleBigInt64Array-2^63to2^63-1864位二進位制有符號整數bigintint64_t(signedlonglong)BigUint64Array0to2^64-1864位無符號整數bigintuint64_t(unsignedlonglong)對於這個教程,因為等會我們會使用浮點數,又因為資料不大,所以選擇Float32Array.嘗試繪製兩個點將兩個點的座標儲存到變數中constverties=newFloat32Array([0.0,0.5,-0.5,-0.5]);把資料掛到緩衝區某個記憶體位置,寫入資料constvertexBuffer=gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //verties就是我們自己建立的Float32Array資料 gl.bufferData(gl.ARRAY_BUFFER,verties,gl.STATIC_DRAW);讀取資料並修改attributeconsta_Position=gl.getAttribLocation(gl.program,"a_Position"); gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0); gl.enableVertexAttribArray(a_Position);理解vertexAttribPointer函式可以看我的這篇筆記https://note.youdao.com/s/c5E...修改了attribute,下一步呼叫繪製命令//因為是繪製兩個點,第三個引數輸入2 gl.drawArrays(gl.POINTS,0,2);完整程式碼除去初始化webGL和工具函式initShaders,因為每次都寫沒有變化...initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE); constverties=newFloat32Array([0.0,0.5,-0.5,-0.5]); constvertexBuffer=gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,verties,gl.STATIC_DRAW); consta_Position=gl.getAttribLocation(gl.program,"a_Position"); gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0); gl.enableVertexAttribArray(a_Position); gl.drawArrays(gl.POINTS,0,2);滑鼠監聽座標並寫入如果上面的都理解了話,第三步反而是最簡單的了(對於web開發人員來說).具體功能上面程式碼都是實現了,只需要:點選的時候把螢幕座標轉成webGL座標把座標存入Float32Array資料修改attribute,渲染。

轉成webGl座標constx=(e.offsetX-320)/320; consty=-(e.offsetY-240)/240;其中320=640/2240=480/2320代表canvas元素的寬,240代表canvas元素高存入Float32Array資料首先Float32Array是固定長度的,無法動態修改,所以需要新建一個Float32ArrayconstnewArr=newFloat32Array(length+2) for(leti=0;i test 你的瀏覽器似乎不支援或者禁用了HTML5<canvas>元素.

其中cuon-utils.js是封裝的一個小工具函式//cuon-utils.js(c)2012kandaandmatsuda /** *Createaprogramobjectandmakecurrent *@paramglGLcontext *@paramvshaderavertexshaderprogram(string) *@paramfshaderafragmentshaderprogram(string) *@returntrue,iftheprogramobjectwascreatedandsuccessfullymadecurrent */ functioninitShaders(gl,vshader,fshader){ varprogram=createProgram(gl,vshader,fshader); if(!program){ console.log('Failedtocreateprogram'); returnfalse; } gl.useProgram(program); gl.program=program; returntrue; } /** *Createthelinkedprogramobject *@paramglGLcontext *@paramvshaderavertexshaderprogram(string) *@paramfshaderafragmentshaderprogram(string) *@returncreatedprogramobject,ornullifthecreationhasfailed */ functioncreateProgram(gl,vshader,fshader){ //Createshaderobject varvertexShader=loadShader(gl,gl.VERTEX_SHADER,vshader); varfragmentShader=loadShader(gl,gl.FRAGMENT_SHADER,fshader); if(!vertexShader||!fragmentShader){ returnnull; } //Createaprogramobject varprogram=gl.createProgram(); if(!program){ returnnull; } //Attachtheshaderobjects gl.attachShader(program,vertexShader); gl.attachShader(program,fragmentShader); //Linktheprogramobject gl.linkProgram(program); //Checktheresultoflinking varlinked=gl.getProgramParameter(program,gl.LINK_STATUS); if(!linked){ varerror=gl.getProgramInfoLog(program); console.log('Failedtolinkprogram:'+error); gl.deleteProgram(program); gl.deleteShader(fragmentShader); gl.deleteShader(vertexShader); returnnull; } returnprogram; } /** *Createashaderobject *@paramglGLcontext *@paramtypethetypeoftheshaderobjecttobecreated *@paramsourceshaderprogram(string) *@returncreatedshaderobject,ornullifthecreationhasfailed. */ functionloadShader(gl,type,source){ //Createshaderobject varshader=gl.createShader(type); if(shader==null){ console.log('unabletocreateshader'); returnnull; } //Settheshaderprogram gl.shaderSource(shader,source); //Compiletheshader gl.compileShader(shader); //Checktheresultofcompilation varcompiled=gl.getShaderParameter(shader,gl.COMPILE_STATUS); if(!compiled){ varerror=gl.getShaderInfoLog(shader); console.log('Failedtocompileshader:'+error); gl.deleteShader(shader); returnnull; } returnshader; } /** *InitializeandgettherenderingforWebGL *@paramcanvaselement *@paramopt_debugflagtoinitializethecontextfordebugging *@returntherenderingcontextforWebGL */ functiongetWebGLContext(canvas,opt_debug){ //GettherenderingcontextforWebGL vargl=WebGLUtils.setupWebGL(canvas); if(!gl)returnnull; //ifopt_debugisexplicitlyfalse,createthecontextfordebugging if(arguments.length<2||opt_debug){ gl=WebGLDebugUtils.makeDebugContext(gl); } returngl; }happy 相關文章 高手過招不用滑鼠,一款超好用的跨平臺命令列介面庫 2022-03-11 『現學現忘』Docker基礎—12、通過RPM軟體包方式安裝Docker 2022-03-11 Docker 銀行業保險業數字化轉型指導意見下發,IT基礎架構團隊如何應對挑戰? 2022-03-11 『現學現忘』Docker基礎—13、通過指令碼安裝Docker 2022-03-11 Docker 『現學現忘』Docker基礎—16、Docker中的基本概念和底層原理 2022-03-11 Docker 八個網路基礎知識 2022-03-11 Zookeeper(1)-安裝與基礎使用 2022-03-11 ZooKeeper 雲原生愛好者週刊:美國國家安全域性釋出網路安全指南 2022-03-12 線性代數在前端中的應用(二):實現滑鼠拖拽旋轉元素、Canvas圖形 2022-03-12 前端 salesforce零基礎學習(一百一十二)專案中的零碎知識點小總結(四) 2022-03-12 Mock平臺2-JavaSpringBoot框架基礎知識 2022-03-12 Java框架Spring “創新雷神號”衛星成功發射,華為雲分散式雲原生“天地一體”首次組網成功 2022-03-13 javascript訂閱模式淺析和基礎例項 2022-03-13 JavaScript 【Go進階—基礎特性】介面 2022-03-13 Go 夯實基礎,共創數字化新格局JY 2022-03-14 webgl未使用獨立顯示卡報告 2022-03-14 WebGLGPU Linux-本地日誌服務管理(rsyslog基礎) 2022-03-14 Linux 【網路安全基礎】如何區分等保備案和等保測評? 2022-03-14 最新文章 CMR:印度手機遊戲調查報告 大咖說·圖書分享|HaaS物聯網裝置雲端一體開發框架 話實踐,行實幹,成實事:“巡禮”數字化的中國大地 構建安全程式碼防止供應鏈攻擊 【推薦閱讀】超有用的漏洞掃描工具合集! 國密SM演算法有哪些? 為什麼伺服器選擇Linux系統 ApacheFlinkML2.1.0釋出公告 乾貨|作為前端開發者如何邁向獨立開發者 助力開發者,全方位解讀APISIX測試案例 得物資料庫中介軟體平臺“彩虹橋”演進之路 KubeSphere3.3.0離線安裝教程



請為這篇文章評分?