OpenGL4.x中,TransformFeedback用來分流頂點著色器或幾何著色器的輸出,并可以將它回傳計算機的記憶體做一些操作,如此,我們就可以使用顯卡的并行計算能力來進行一些計算加速,比如深度學習或區塊鏈,這里記錄如何使用WebGL2在瀏覽器中使用TransformFeedback,
1、原生TransformFeedback的基本流程:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>原生WebGL例子-反寫記憶體</title> 6 <style> 7 html, body { 8 overflow: hidden; 9 width: 100%; 10 height: 100%; 11 margin: 0; 12 padding: 0; 13 } 14 #renderCanvas { 15 width: 100%; 16 height: 100%; 17 touch-action: none; 18 } 19 </style> 20 </head> 21 <body> 22 <canvas id="renderCanvas" touch-action="none"></canvas> 23 </body> 24 <script> 25 window.onload=webGLStart; 26 var canvas,gl; 27 28 function webGLStart() 29 { 30 canvas = document.getElementById("renderCanvas");//canvas是html5下的繪圖示簽,可以支持3D繪圖 31 gl=initGL(canvas);//初始化“繪制背景關系”,以后的繪制都要通過它進行 32 var vertexShaderSrc="#version 300 es\n" +//這個版本標志必須有!這里必須有換行符 33 "precision highp float;" +//如果你要在程式中書寫浮點數,則這個精度設定是必須的,但這里其實不需要 34 "in float inValue;\n" +//這個換行符不是必須的,輸入值《-注意!瀏覽器端輸入的資料單元可能是一個浮點數或2到4元向量,但glsl只會按這里設定的資料型別處理輸入,比如輸入3元向量型的資料,但這里只取向量的第一個分量處理, 35 "out float outValue;\n" +//一方面輸出到片元著色器,一方面輸出到TransformFeedback 36 "void main()\n" + 37 "{\n" + 38 " outValue=https://www.cnblogs.com/ljzc002/p/sqrt(inValue);/n" +//取平方根 39 "}\n" 40 var vertexShader=gl.createShader(gl.VERTEX_SHADER);//建立頂點著色器 41 gl.shaderSource(vertexShader, vertexShaderSrc); 42 gl.compileShader(vertexShader); 43 //先不建立片元著色器 44 var shaderProgram = gl.createProgram();//建立著色器程式物件, 45 gl.attachShader(shaderProgram, vertexShader); 46 //注意!對于OpenGL和OpenGL ES來說,允許只建立頂點著色器,然后鏈接即可計算,但WebGL必須有片元著色器才可鏈接 47 48 var fragmentShaderSrc="#version 300 es\n" + 49 "precision highp float;\n" + 50 "precision highp int;" + 51 52 "out vec4 outValue2;" +//這個光柵化后輸出到螢屏,和TransformFeedback沒關系 53 "void main(){" + 54 " outValue2=vec4(1.0,0.5,0.5,1.0);" + 55 "}" 56 var fragmentShader=gl.createShader(gl.FRAGMENT_SHADER); 57 gl.shaderSource(fragmentShader, fragmentShaderSrc); 58 gl.compileShader(fragmentShader); 59 gl.attachShader(shaderProgram, fragmentShader); 60 61 //也先不連接程式 62 gl.transformFeedbackVaryings(shaderProgram,["outValue"], gl.SEPARATE_ATTRIBS);//gl.SEPARATE_ATTRIBS,//gl.INTERLEAVED_ATTRIBS 63 //console.log(activeInfo);//上一行設定“變換反饋”與頂點著色器中的哪個out變數關聯,這里只有一個 64 65 gl.linkProgram(shaderProgram); 66 console.log(gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)); 67 console.log(gl.getShaderInfoLog(vertexShader)); 68 console.log(gl.getShaderInfoLog(fragmentShader)); 69 console.log(gl.getProgramInfoLog(shaderProgram));//The program must contain objects to form both a vertex and fragment shader. 70 71 72 var verts = [2,0,0,4,0,0,6,0,0];//準備一點資料 73 var vertexBuffer=gl.createBuffer(); 74 gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); 75 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);//準備瀏覽器端的快取 76 var inputAttrib =gl.getAttribLocation(shaderProgram, "inValue");//找到顯卡中變數的位置(此時尚未傳遞資料到顯卡) 77 78 79 80 var transformFeedbackBuffer=gl.createBuffer();//用于讀取變換反饋結果的快取 81 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER,transformFeedbackBuffer); 82 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 3* Float32Array.BYTES_PER_ELEMENT, gl.STREAM_READ);//STREAM_READ,DYNAMIC_READ,STATIC_READ,靜態則只讀一次? 83 84 var transformFeedback=gl.createTransformFeedback();//建立變換反饋物件 85 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); 86 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, transformFeedbackBuffer);//把變換反饋物件和快取關聯起來 87 88 gl.useProgram(shaderProgram);//啟用鏈接好的著色器程式 89 gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); 90 gl.enableVertexAttribArray(inputAttrib); 91 gl.vertexAttribPointer(inputAttrib,3,gl.FLOAT,false,0,0);//3是vertSize,這里3個數一組的發送到顯卡 92 93 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); 94 gl.beginTransformFeedback(gl.POINTS);//這兩個必須一樣 95 gl.drawArrays(gl.POINTS,0,3);//TRIANGLE_STRIP,TRIANGLES 96 //var activeInfo = gl.getTransformFeedbackVarying(shaderProgram, 0); 97 //console.log(activeInfo); 98 gl.endTransformFeedback(); 99 //讀取時控制臺出現警告:performance warning: READ-usage buffer was read back without waiting on a fence. This caused a graphics pipeline stall. 100 //但是添加了延時后仍然有警告,不知是什么原因 101 102 // requestAnimFrame( 103 // function(){ 104 // gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER,transformFeedbackBuffer); 105 // //var arr=new Float32Array(3); 106 // var arrBuffer = new ArrayBuffer(3 * Float32Array.BYTES_PER_ELEMENT); 107 // var arr=new Float32Array(arrBuffer); 108 // gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER,0,arr) 109 // console.log(arr); 110 // //console.log(arr.values()); 111 // } 112 // ) 113 //gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER,transformFeedbackBuffer); 114 setTimeout(function(){ 115 116 //var arr=new Float32Array(3); 117 var arrBuffer = new ArrayBuffer(3 * Float32Array.BYTES_PER_ELEMENT); 118 var arr=new Float32Array(arrBuffer); 119 gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER,0,arr)//ARRAY_BUFFER,TRANSFORM_FEEDBACK_BUFFER 120 console.log(arr);//讀取到了變換反饋資訊, 121 //console.log(arr.values()); 122 },1000) 123 124 //var activeInfo = gl.getTransformFeedbackVarying(shaderProgram, 0); 125 //console.log(activeInfo); 126 } 127 function initGL(canvas){ 128 gl; 129 try 130 { 131 gl = canvas.getContext("webgl2",{antialias:true});//從canvas中獲取webgl背景關系 132 //https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/getContext 133 gl.viewport(0,0,canvas.width,canvas.height);//設定視口 134 } 135 catch(e) 136 { 137 var msg="Error creating WebGL Context!: "+ e.toString(); 138 alert(msg); //彈出錯誤資訊 139 } 140 return gl; 141 } 142 window.requestAnimFrame = (function() { 143 return window.requestAnimationFrame || 144 window.webkitRequestAnimationFrame || 145 window.mozRequestAnimationFrame || 146 window.oRequestAnimationFrame || 147 window.msRequestAnimationFrame || 148 function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { 149 window.setTimeout(callback, 1000/60); 150 }; 151 })(); 152 </script> 153 </html>
在js的語法里,我們就是飛行在天空的上帝,我們可以俯瞰大地上的所有角落,并隨時降臨在任意位置;但在操作顯卡時,我們是軌道上的宇航員,我們要在移動到特定坐標時才能看到相應位置(bind),并且要在做好充足準備后(buffer)才能在有限的區域登陸,
2、Babylon.js訓練場中的一個略微復雜一些的例子:
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <title>01Babylon里的的原生例子</title> 6 <style> 7 html, body { 8 overflow: hidden; 9 width: 100%; 10 height: 100%; 11 margin: 0; 12 padding: 0; 13 } 14 #renderCanvas { 15 width: 100%; 16 height: 100%; 17 touch-action: none; 18 } 19 </style> 20 <script src="../../JS/LIB/babylon50.min.js"></script> 21 <!--<script src="https://www.cnblogs.com/JS/LIB/recast.js"></script><!–基于wasm編譯而來的導航庫–>--> 22 <!--<script src="https://www.cnblogs.com/JS/LIB/dat.gui.min.js"></script>--> 23 </head> 24 <body> 25 <canvas id="renderCanvas" touch-action="none"></canvas> <!-- touch-action="none" for best results from PEP --> 26 <script> 27 const canvas = document.getElementById("renderCanvas"); // Get the canvas element 獲取畫布標簽 28 const engine = new BABYLON.Engine(canvas, true, 29 { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false}); // Generate the BABYLON 3D engine 建立BABYLON 3D引擎 30 //var navmeshdebug; 31 32 const createScene = () => { 33 // This creates a basic Babylon Scene object (non-mesh) 34 var scene = new BABYLON.Scene(engine); 35 var babylonE = scene.getEngine();//未使用 36 37 // This creates and positions a free camera (non-mesh) 38 var camera = new BABYLON.ArcRotateCamera("camera", BABYLON.Tools.ToRadians(90), BABYLON.Tools.ToRadians(65), 2000, BABYLON.Vector3.Zero(), scene); 39 //var camera = new BABYLON.FreeCamera("camera", BABYLON.Vector3.Zero(), scene); 40 // This targets the camera to scene origin 41 camera.setTarget(BABYLON.Vector3.Zero()); 42 camera.maxZ = 1000000;//因為操作的是同一個gl,所以這個相機也能生效! 43 44 // This attaches the camera to the canvas 45 camera.attachControl(canvas, true); 46 47 // This creates a light, aiming 0,1,0 - to the sky (non-mesh) 48 var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene); 49 50 // Default intensity is 1. Let's dim the light a small amount 51 light.intensity = 0.7;//光照也生效 52 53 54 var time = 0; 55 56 // Our built-in 'ground' shape. 57 var ground = BABYLON.MeshBuilder.CreateGround("ground", { 58 width: 1000, 59 height: 1000, 60 subdivisions: 500 61 }, scene 62 );//建立了一個“地面網格”,它有很多個頂點 63 let data = ground.getVertexBuffer(BABYLON.VertexBuffer.PositionKind).getData();//取出頂點資料 64 65 var newData = new Float32Array(data.length);//把頂點資料放在一個快取物件里 66 67 const gl = canvas.getContext('webgl2') || canvas.getContext('experimental-webgl2'); 68 console.log(gl);//取原生gl物件 69 setupCustomGLProgram(gl, new Float32Array(data), newData);//使用顯卡并行處理大量頂點,應該會比cpu更快 70 71 console.log(newData); 72 73 ground.setVerticesData(BABYLON.VertexBuffer.PositionKind, newData);//把處理完的頂點放回地面網格里 74 ground.createNormals(false); 75 76 77 78 scene.registerBeforeRender(function(){ 79 time += 0.1;//也可以讓頂點們隨時間變化!! 80 }); 81 82 return scene; 83 } 84 85 86 87 function setupCustomGLProgram(gl, dataIn, dataOut)//自定義gpu計算 88 { 89 const VERTEX_COUNT = dataIn.length; 90 91 gl.enable(gl.RASTERIZER_DISCARD);//這個有什么用?《-讓這個gpu計算中的draw不生效!只用Babylon.js的! 92 //使用GL_RASTERIZER_DISCARD標志作為引數呼叫glEnable()函式,告訴渲染管線在transform feedback可選階段之后和到達光柵器前拋棄所有的圖元, 93 const program = gl.createProgram();//和上個例子類似的操作 94 gl.attachShader(program, getShader(gl, voronoiVertex, gl.VERTEX_SHADER)); 95 gl.attachShader(program, getShader(gl, voronoiFragment, gl.FRAGMENT_SHADER)); 96 gl.transformFeedbackVaryings(program, ['outPosition'], gl.SEPARATE_ATTRIBS); 97 98 gl.linkProgram(program); 99 console.log('program:', gl.getProgramInfoLog(program)); 100 gl.useProgram(program); 101 102 103 //buffers 104 //input 105 let inputBuffer = gl.createBuffer(); 106 gl.bindBuffer(gl.ARRAY_BUFFER, inputBuffer); 107 gl.bufferData(gl.ARRAY_BUFFER, dataIn, gl.STATIC_DRAW); 108 109 //output 110 let resultBuffer = gl.createBuffer(); 111 112 // Create a TransformFeedback object 113 var transformFeedback = gl.createTransformFeedback(); 114 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); 115 116 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, resultBuffer); 117 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, VERTEX_COUNT * Float32Array.BYTES_PER_ELEMENT, gl.STATIC_DRAW); 118 119 120 // Attribute position 121 const inputAttribLocation = gl.getAttribLocation(program, 'position'); 122 123 gl.enableVertexAttribArray(inputAttribLocation); 124 gl.bindBuffer(gl.ARRAY_BUFFER, inputBuffer); 125 gl.vertexAttribPointer( 126 inputAttribLocation, // index 127 3, // size 128 gl.FLOAT, // type 129 gl.FALSE, // normalized? 130 0, // stride 131 0 // offset 132 ); 133 134 135 // Activate the transform feedback 136 gl.beginTransformFeedback(gl.POINTS); 137 gl.drawArrays(gl.POINTS, 0, Math.floor(VERTEX_COUNT/3)); 138 gl.endTransformFeedback(); 139 140 141 // Read back 142 gl.getBufferSubData( 143 gl.TRANSFORM_FEEDBACK_BUFFER, // target 144 0, // srcByteOffset 145 dataOut, // dstData 146 ); 147 148 gl.disable(gl.RASTERIZER_DISCARD); 149 } 150 151 152 153 function getShader(gl, source, type){ 154 let sh = gl.createShader(type); 155 gl.shaderSource(sh, source); 156 gl.compileShader(sh); 157 console.log('Shader:', gl.getShaderInfoLog(sh)); 158 return sh; 159 } 160 161 162 163 164 165 166 167 //用模板字串,更好換行 168 const voronoiVertex = 169 `#version 300 es 170 precision highp float; 171 172 173 174 175 in vec3 position; 176 out vec3 outPosition; 177 178 void main(void) { 179 vec3 finalPos = position; 180 181 182 183 outPosition = finalPos; 184 if(finalPos[0]<0.0) 185 { 186 outPosition[1]=10.0; 187 } 188 } 189 `; 190 191 192 const voronoiFragment = 193 `#version 300 es 194 precision highp float;//這一句可省 195 196 void main(void) { 197 198 } 199 ` 200 201 202 203 204 BABYLON.Effect.ShadersStore["customVertexShader"] = voronoiVertex 205 206 BABYLON.Effect.ShadersStore["customFragmentShader"] = voronoiFragment; 207 208 // Add your code here matching the playground format 按照訓練場的格式添加你自己的代碼 209 const scene = createScene(); //Call the createScene function 呼叫createScene方法 210 211 scene.debugLayer.show(); 212 // Register a render loop to repeatedly render the scene 注冊一個渲染回圈,來重復地渲染場景 213 engine.runRenderLoop(function () { 214 scene.render(); 215 }); 216 // Watch for browser/canvas resize events 監聽瀏覽器或畫布的尺寸改變事件 217 window.addEventListener("resize", function () { 218 engine.resize(); 219 }); 220 221 </script> 222 </body> 223 </html>
以上就是一個通過并行計算提高計算效率的例子,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/296163.html
標籤:其他
上一篇:[譯] 如何使用 WebGL 技術進行風力地圖可視化
下一篇:OpenCV 之 自定義濾波
