須要瞭解一下的前置知識(下面是推薦閱讀的連接)html
GLSL github.com/wshxbqq/GLS…git
Shader thebookofshaders.com/01/github
矩陣 bk.tw.lvfukeji.com/wiki/%E7%9F…web
齊次座標 bk.tw.lvfukeji.com/wiki/%E9%BD…canvas
這個就是簡單的獲取dom而後獲取上下文 (注意下這裏由於是畫3d因此要開啓深度檢測)api
const canvasDom = document.getElementById('canvas') gl = canvasDom.getContext('webgl') //開啓深度檢測 gl.enable(gl.DEPTH_TEST)
關於着色器shader是一個超級大的話題(推薦看TheBooksOfShader,尷尬的是做者沒寫完)。dom
大體能夠這麼理解:webgl
• 頂點着色器肯定了畫布上點的位置spa
• 3d世界中基礎的幾何圖形是三角形,片元着色器表明了區域的表現形式3d
先看一下webgl的座標系,z+軸是面向咱們的視角:
下面這段是頂點着色器:
const Vertex = ` attribute vec3 vPosition; void main() { gl_PointSize = 1.0; gl_Position = mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)*mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)*vec4(vPosition, 1); } `
attribute : 只能存在於vertex shader中,通常用於保存頂點或法線數據,它能夠在數據緩衝區中讀取數據。
vec3 vPosition 定義了一個3維向量
由於3d空間一個點(x,y,z)
mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)是一個齊次矩陣 表示繞y軸旋轉45度
mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)表示繞z軸旋轉45度
這樣咱們才能看到3d的效果。
下面這個就是編譯shader,固定套路記住就行了 (開發中不大會用原生手寫webgl)
const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, Vertex); gl.compileShader(vertexShader);
下面這段是片元着色器:
const Fragment = ` #ifdef GL_ES precision highp float; #endif void main() { gl_FragColor = vec4(1.0,0,0,1.0); } `
表示的意思是畫布上的顏色是紅色 vec4(1.0,0,0,1.0) 而後也是固定套路:
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, Fragment); gl.compileShader(fragmentShader);
記住就好,就是調用api
const program = gl.createProgram();
記住就好,就是調用api
gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program);
cube是用來獲取頂點座標
剩下的都是套路,api就不展開了,能夠去mdn上查閱
//cube是用來獲取頂點座標 function cube(size = 1.0) { const h = 0.5 * size; const vertices = [ [-h, -h, -h], [-h, h, -h], [h, h, -h], [h, -h, -h], [-h, -h, h], [-h, h, h], [h, h, h], [h, -h, h], ]; const positions = []; function quad(a, b, c, d, e, f, g, h) { [a, b, c, d, e, f, g, h].forEach((i) => { positions.push(vertices[i]); }); } quad(0, 1, 1, 2, 2, 3, 3, 0); quad(4, 5, 5, 6, 6, 7, 7, 4); quad(1, 2, 2, 6, 6, 5, 5, 1); quad(0, 3, 3, 7, 7, 4, 4, 0); quad(0, 1, 1, 5, 5, 4, 4, 0); quad(3, 7, 7, 6, 6, 2, 2, 3); return { positions}; } const geometry = cube(1.0); console.log(geometry) const points = new Float32Array(geometry.positions.flat()); const bufferId = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, bufferId); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
const Position = gl.getAttribLocation(program, 'vPosition');//獲取頂點着色器中的position變量的地址 gl.vertexAttribPointer(Position, 3, gl.FLOAT, false, 0, 0);//給變量設置長度和類型 gl.enableVertexAttribArray(Position);//激活這個變量 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.drawArrays(gl.LINES, 0, 48)
gl.drawArrays(gl.LINES, 0, 48)就是渲染的api
webgl中有7種圖元 (表示怎麼畫)
gl.POINTS 孤立點 繪製一系列點
gl.LINES 繪製一系列單獨線段。每兩個點做爲端點,線段之間不鏈接
gl.LINE_STRIP 連續線段 繪製一個線條。即,繪製一系列線段,上一點鏈接下一點
gl.LINE_LOOP 連續線圈 繪製一個線圈。即,繪製一系列線段,上一點鏈接下一點,而且最後一點與第一個點相連
gl.TRIANGLES 孤立三角形
gl.TRIANGLE_STRIP 三角帶
gl.TRIANGLE_FAN 三角扇
0,48表示從0取48個點繪製
上述過程就是一個完整的webgl繪畫流程。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D立方體</title> </head> <body> <canvas id='canvas' width="800" height="800"></canvas> </body> <script> // 第一步 建立webgl上下文 const canvasDom = document.getElementById('canvas') gl = canvasDom.getContext('webgl') //開啓深度檢測 gl.enable(gl.DEPTH_TEST) console.log(gl) // 第二步 建立頂點着色器與片元着色器 const Vertex = ` attribute vec3 vPosition; void main() { gl_PointSize = 1.0; gl_Position = mat4(1,0, 0, 0 ,0, 0.52, -0.85, 0, 0, 0.85, 0.52, 0, 0, 0, 0,1)*mat4(0.52,0, 0.85, 0 ,0, 1, 0, 0, -0.85, 0, 0.52, 0, 0, 0, 0,1)*vec4(vPosition, 1); } ` const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, Vertex); gl.compileShader(vertexShader); const Fragment = ` #ifdef GL_ES precision highp float; #endif void main() { gl_FragColor = vec4(1.0,0,0,1.0); } ` const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, Fragment); gl.compileShader(fragmentShader); //第三步 建立程序對象 const program = gl.createProgram(); // 第四步 連接程序與着色器 gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); //第五步 創建緩衝數據 function cube(size = 1.0) { const h = 0.5 * size; const vertices = [ [-h, -h, -h], [-h, h, -h], [h, h, -h], [h, -h, -h], [-h, -h, h], [-h, h, h], [h, h, h], [h, -h, h], ]; const positions = []; function quad(a, b, c, d, e, f, g, h) { [a, b, c, d, e, f, g, h].forEach((i) => { positions.push(vertices[i]); }); } quad(0, 1, 1, 2, 2, 3, 3, 0); quad(4, 5, 5, 6, 6, 7, 7, 4); quad(1, 2, 2, 6, 6, 5, 5, 1); quad(0, 3, 3, 7, 7, 4, 4, 0); quad(0, 1, 1, 5, 5, 4, 4, 0); quad(3, 7, 7, 6, 6, 2, 2, 3); return { positions}; } const geometry = cube(1.0); console.log(geometry) const points = new Float32Array(geometry.positions.flat()); const bufferId = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, bufferId); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); // 第六步 渲染 const Position = gl.getAttribLocation(program, 'vPosition');//獲取頂點着色器中的position變量的地址 gl.vertexAttribPointer(Position, 3, gl.FLOAT, false, 0, 0);//給變量設置長度和類型 gl.enableVertexAttribArray(Position);//激活這個變量 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.drawArrays(gl.LINES, 0, 48) </script> </html>
文|alex 關注得物技術,攜手走向技術的雲端