zoukankan      html  css  js  c++  java
  • 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>

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

  • 相关阅读:
    vmwear 及docker
    vue相关
    vue demo
    线程的死锁
    让出和守护线程
    join加入线程
    线程的优先级
    线程间的通信
    synchronized关键字的使用
    线程的并发
  • 原文地址:https://www.cnblogs.com/ljzc002/p/15148836.html
Copyright © 2011-2022 走看看