WebGL基礎(一): 從一個滑鼠畫點開始瞭解原生webGL | IT人
文章推薦指數: 80 %
面向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畫任意物體都需要一個頂點著色器和片元著色器,頂點著色器:描述頂點的特性(位置、顏色等)的程式.片元著色器:進行著片元處理過程的程式。
也許你會很懵,一大堆官方理論又要望而卻步了,所以我直接展示下最簡單的展示一個點的程式碼,相信你會馬上明白。<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<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
*@paramcanvas
延伸文章資訊
- 1WebGL基礎(一): 從一個滑鼠畫點開始瞭解原生webGL | IT人
面向web前端的WebGL教程,網路上的教程均是假設有計算機圖形學基礎,對web開發者來說不是很 ... webGL如何展示一個點首先得知道webGL如何展示出一個點?
- 2现在为什么很少用webgl - 头条搜索
Canvas、 SVG 和WebGL三者之间的区别- 游侠舒迟- 博客园 · WebGL 3D位图,是基于Canvas 的3D 框架。 · WebGL 入门必看(一)_凯小默的博客-CSDN博客...
- 3[WebGL - Day05] 學習經驗1/2 - 有點不得其門而入的碰壁期
這篇講的是我一開始學WebGL 時覺得抓不到施力點, ... 我目前學習與運用WebGL 的方式 ... three.js 處理掉一些WebGL 必須要寫的寫法但也同時需要學習three.js ...
- 4WebGL 是否需要以OpenGL 为学习基础? - 知乎
你要想真正学会WebGL,必须懂OpenGL 和图形学,以及shader 语言。 ... 无论怎么分很不幸,这个都不是他们擅长的领域。很多程序员老鸟,可能各种刷数据结构,但是毕业 ...
- 5如何学习webgl,需要那些基础知识,或者数学知识
然后找个现代GL教程看(不一定要webgl,你知道API是做什么的就可以自己写了). 再深入可以去读 3D游戏与计算机图形学中的数学方法 之类书.