• 3D网页小实验-WebGL2中的TransformFeedback


    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
    " +//这个版本标志必须有!这里必须有换行符
     33             "precision highp float;" +//如果你要在程序中书写浮点数,则这个精度设置是必须的,但这里其实不需要
     34             "in float inValue;
    " +//这个换行符不是必须的,输入值《-注意!浏览器端输入的数据单元可能是一个浮点数或2到4元向量,但glsl只会按这里设置的数据类型处理输入,比如输入3元向量型的数据,但这里只取向量的第一个分量处理。
     35             "out float outValue;
    " +//一方面输出到片元着色器,一方面输出到TransformFeedback
     36             "void main()
    " +
     37             "{
    " +
     38             "   outValue=sqrt(inValue);
    " +//取平方根
     39             "}
    "
     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
    " +
     49             "precision highp float;
    " +
     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="../../JS/LIB/recast.js"></script>&lt;!&ndash;基于wasm编译而来的导航库&ndash;&gt;-->
     22     <!--<script src="../../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                  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>

    以上就是一个通过并行计算提高计算效率的例子。

  • 相关阅读:
    POJ 2752 Seek the Name, Seek the Fame
    POJ 2406 Power Strings
    KMP 算法总结
    SGU 275 To xor or not to xor
    hihocoder 1196 高斯消元.二
    hihoCoder 1195 高斯消元.一
    UvaLive 5026 Building Roads
    HDU 2196 computer
    Notions of Flow Networks and Flows
    C/C++代码中的笔误
  • 原文地址:https://www.cnblogs.com/ljzc002/p/15148836.html
Copyright © 2020-2023  润新知