区块链技术博客
www.b2bchain.cn

webgl实现发光线框(glow wireframe)效果求职学习资料

本文介绍了webgl实现发光线框(glow wireframe)效果求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

在之前这篇文章,
WebGL 单通道wireframe渲染
我们介绍了webgl如何实现单通道wireframe的效果。

本篇文章就是在此技术原理基础之上,来实现发光的wireframe效果。

要实现发光的效果

所谓的发光的效果,就是颜色的渐变。 渐变越慢,发光的效果越明显,渐变越快,发光效果越不明显。

其实wireframe本身就是在两种颜色之间进行渐变,从代码也可以看出:

gl_FragColor.rgb = mix(vec3(.0,.0,.0), vec3(1.0,1.0,1.0),edgeFactor3());

其中edgeFactor3() 就是通过重心坐标的变换计算出来的一个渐变过度的参数。
但是由于这种渐变的效果不够慢,所以 发光的效果不是很明显,因此我们可以改进如下效果,把渐变的参数通过pow函数进行处理,代码如下:

 float interopter = edgeFactor3();  interopter = pow(interopter, uPower); gl_FragColor.rgb = mix(vec3(1.0,.0,.0), vec3(1.0,1.0,1.0),interopter);

其中uPower表示pow函数的次方,此处取值范围0~1,通过uniform变量传递该变量的数值,最终的效果如下:
webgl实现发光线框(glow wireframe)效果

上面是既有线框部分,也有面的部分。如果想实现只有线框的效果,可以启用透明的机制,并对颜色的透明度也进行渐变插值运算,透明设置代码如下:

    // 启用混合功能       gl.enable(gl.DEPTH_TEST);       gl.enable(gl.BLEND);       gl.disable(gl.DEPTH_WRITEMASK);       // 设置混合函数       gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

shader代码增加以下的这行代码:

gl_FragColor.a = mix(1.0, .0,interopter);

效果如下图所示:
webgl实现发光线框(glow wireframe)效果

发光的线框

如果模型替换成球形,效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-球体

加载模型的效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-模型

如果修改shader中的edgeFactor3函数,把计算最小值,改为计算平均值,代码如下:

float edgeFactor3(){         vec3 d = fwidth(vBarycentric);         vec3 a3 = smoothstep(vec3(0.0), d * 30.0 , vBarycentric);       //return min(min(a3.x, a3.y), a3.z);        return (a3.x + a3.y + a3.z) / 3.0;   }

得到最终的效果如下图所示(立方体):

webgl实现发光线框(glow wireframe)效果

发光的线框-立方体

替换成模型,效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-模型

如果结合混合模式中的相加混合,加上多个模型的叠加,可以得到更明显的发光叠加的效果,此种效果经常用于智慧园区,智慧楼宇中楼宇的发光效果呈现。
首先把混合模式改成相加混合,代码如下:

     // 设置混合函数       // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);       gl.blendFunc(gl.SRC_ALPHA, gl.ONE); //相加混合模式

然后同时绘制多个模型,代码如下:

 for (var i = 0;i < 10;i ++){             gl.uniform1f(normalProgram.uScale, 1 - i/10)             gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);            }

最终的效果如下所示:

webgl实现发光线框(glow wireframe)效果

叠加效果

webgl实现发光线框(glow wireframe)效果

叠加效果

webgl实现发光线框(glow wireframe)效果

叠加效果

全部代码:

<!DOCTYPE html> <html>     <head>         <meta charset="utf-8">         <title>单通道 solid wireframe</title>         <script type="text/javascript" src="js/utils.js"></script>         <script type="text/javascript" src="libs/gl-matrix.js"></script>         <!-- <script type="text/javascript" src="js/shaders.js"></script> -->         <script type="text/javascript" src="js/shaders3.js"></script>         <!-- <script type="text/javascript" src="js/shaders3.js"></script> -->     <script type="text/javascript" src="js/SolidWireframe.js"></script>     <script type="text/javascript" src="js/Sphere.js"></script>      </head>   <body onload= 'load();'>      <div>        power:<input type="range" id="power"/>      </div>       <canvas id = "webgl" width = "800" height = "800" />      </body> </html>
var utils = {};  utils.getCanvas= function (id) {     if(id instanceof HTMLCanvasElement){         return id;     }     var canvas =  document.getElementById(id);     if(canvas instanceof HTMLCanvasElement){         return canvas;     }     return null; } utils.getWebGLContext = function(id){     var canvas = utils.getCanvas(id);     var options = {         antialias:true,     }     if(canvas != null){         return canvas.getContext('webgl',options) || canvas.getContext('experimental-webgl',options);     }     throw 'expect canvas but find none'; }  utils.buildProgram = function(gl,vertexSource,fragmentSource){     var vertexShader = utils.createShader(gl,gl.VERTEX_SHADER,vertexSource);     var fragmentShader = utils.createShader(gl,gl.FRAGMENT_SHADER,fragmentSource);     var program = gl.createProgram(); // 创建程序     // 程序绑定着色器       gl.attachShader(program,vertexShader);       gl.attachShader(program,fragmentShader);       // 链接程序     gl.linkProgram(program)     if ( !gl.getProgramParameter( program, gl.LINK_STATUS) ) {           var info = gl.getProgramInfoLog(program);           console.log("Could not initialise shadern" + "VALIDATE_STATUS: " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + ", gl error [" + gl.getError() + "]");           throw 'Could not compile WebGL program. nn' + info;     }     // 使用程序     gl.useProgram(program)     return program; }  utils.createShader = function(gl,type,source){     var shader = gl.createShader(type); // 创建着色器对象        gl.shaderSource(shader,source); // 将着色器源码写入对象        gl.compileShader(shader); // 编译着色器        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS) && !gl.isContextLost()) {         var info = gl.getShaderInfoLog(shader);         console.error(info);         throw 'Could not compile shader program. ' + info;     }        return shader; }  utils.createVertexBufferObject = function(gl,vertices){     var vertexBuffer = gl.createBuffer(); // 创建缓冲区     gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //绑定缓冲区     gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); //给缓冲区填充数据     return vertexBuffer; }  utils.initVertexBufferObject = function(gl,vertices,size,location){     var vertexBuffer = gl.createBuffer(); // 创建缓冲区     gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //绑定缓冲区     gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); //给缓冲区填充数据     if(size && location != null){            gl.vertexAttribPointer(location,size,gl.FLOAT,false,0,0); // 把缓冲区分配给attribute变量     }      return vertexBuffer; }  utils.getVariableLocation = function(gl,program,name,attribute){     var location;     if(attribute){ // 是attribute变量         location = gl.getAttribLocation(program,name);          if(location < 0){                  throw "Get attribute variable "+name+"'s location fail";               }     }else{ // 表示是uniform 变量         location = gl.getUniformLocation(program,name);         if(location == null){             throw "Failed get unifrom variable "+name+"'s location"         }     }     return location; }  utils.getProgramVariableLocations2 = function (gl,program,variableNames,isAttribute) {       var len = variableNames.length;       for (var i = 0; i < len; i++) {         var variableName = variableNames[i];           if (isAttribute) { // attribute 变量             var location = program[variableName] = utils.getVariableLocation(gl, program, variableName, true);           } else { // uniform 变量             program[variableName] = utils.getVariableLocation(gl, program, variableName, false);           }       } }  utils.getProgramVariableLocations = function(gl,program,variableNames){        var len = variableNames.length;        for(var i = 0; i < len;i ++){            var variableName = variableNames[i];            if(variableName.startsWith('a')){ // attribute 变量                var location = program[variableName] =  utils.getVariableLocation(gl,program,variableName,true);            }else{// uniform 变量                program[variableName] = utils.getVariableLocation(gl,program,variableName,false);            }           } }

“`
function load(){
var gl = window.gl= utils.getWebGLContext(‘webgl’,{
antialias: true,
});
if(gl == null){
console.log(“Get WebGL Context fail”);
return;
}

//  gl.getExtension("OES_standard_derivatives");   // 创建正常场景着色器程序  var normalProgram = utils.buildProgram(gl,normalVS,normalFS);   utils.getProgramVariableLocations(gl, normalProgram, [    'aPosition', 'aColor', 'aNormal', 'aBarycentric', 'uMVPMatrix',     'uLightColor', 'uLightPosition', 'uAmbientLightColor', 'uPower',    "uScale",   ]);  // var sphere = new Sphere(10,20,20); // //创建立方体数据 var verticesColors = new Float32Array([        // 前面四个顶点          -5,  5,  5, 1,0,0,//v0  红色 法线向量 0,0,1         -5, -5,  5, 1,0,0,// v1 红色           5,  5,  5,  1,0,0,// v2 红色           5,  -5, 5,  1,0,0,// v3 红色        // 右面四个顶点          5,5,5, 0,1,0, //v4 绿色 法线向量 1,0,0          5,-5,5, 0,1,0,//v5 绿色          5,5,-5, 0,1,0,//v6 绿色          5,-5,-5, 0,1,0,//v7 绿色        // 上面四个顶点          -5,5,-5,0,1,1, //v8 青色 法线向量 0,1,0          -5,5,5,0,1,1, //v9 青色           5,5,-5,0,1,1,//v10 青色           5,5,5,0,1,1,//v11 青色         // 下面四个顶点           -5,-5,5,1,1,1,//v12 白色 法线向量 0,-1,0          -5,-5,-5,1,1,1,//v13 白色          5,-5,5,1,1,1,//v14 白色           5,-5,-5,1,1,1,//v15 白色        // 左面的四个顶点         -5,5,-5,1,1,0,//v16 黄色  法线向量 -1,0,0         -5,-5,-5,1,1,0,//v17 黄色         -5,5,5,1,1,0,//v18黄色         -5,-5,5,1,1,0,//v19 黄色        // 后面的四个顶点         5,5,-5,1,0,1,//v20 红蓝 法线向量 0,0,-1         5,-5,-5,1,0,1,//v21 红蓝         -5,5,-5,1,0,1,//v22 红蓝         -5,-5,-5,1,0,1,//v23 红蓝   ]);    // var verticesColors = sphere.getVerticesArray();      var normals = new Float32Array([      // 前面四个顶点      0, 0, 1, //v0  红色 法线向量 0,0,1      0, 0, 1, // v1 红色      0, 0, 1, // v2 红色      0, 0, 1, // v3 红色      // 右面四个顶点      1, 0, 0, //v4 绿色 法线向量 1,0,0      1, 0, 0, //v5 绿色      1, 0, 0, //v6 绿色      1, 0, 0, //v7 绿色      // 上面四个顶点      0, 1, 0, //v8 青色 法线向量 0,1,0      0, 1, 0, //v9 青色       0, 1, 0, //v10 青色       0, 1, 0, //v11 青色       // 下面四个顶点      0, -1, 0, //v12 白色 法线向量 0,-1,0      0, -1, 0, //v13 白色      0, -1, 0, //v14 白色       0, -1, 0, //v15 白色      // 左面的四个顶点      -1, 0, 0, //v16 黄色  法线向量 -1,0,0      -1, 0, 0, //v17 黄色      -1, 0, 0, //v18黄色      -1, 0, 0, //v19 黄色      // 后面的四个顶点      0, 0, -1, //v20 红蓝 法线向量 0,0,-1      0, 0, -1, //v21 红蓝      0, 0, -1, //v22 红蓝      0, 0, -1, //v23 红蓝    ]);    // var normals = sphere.getNormals();   var indices = new Uint16Array([         0,1,2 ,2,1,3, // 前表面索引         4,5,6,6,5,7, //右         8,9,10,10,9,11, //上         12,13,14,14,13,15, // 下         16,17,18,18,17,19,//左          20,21,22,22,21,23,//后   ]);   // var indices = sphere.getIndicesArray();   // 启用混合功能   gl.enable(gl.DEPTH_TEST);   gl.enable(gl.BLEND);   // gl.disable(gl.DEPTH_WRITEMASK);   // 设置混合函数   // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);   gl.blendFunc(gl.SRC_ALPHA, gl.ONE);  var barycentrics =new Float32Array(computeBarycentric(indices));    //  var barycentrics = sphere.getBarycentric();     var floatSize = verticesColors.BYTES_PER_ELEMENT;   var verticesColorsBuffer = utils.initVertexBufferObject(gl, verticesColors);   var normalsBuffer = utils.initVertexBufferObject(gl,normals)    var barycentricBuffer = utils.initVertexBufferObject(gl,barycentrics);     var indexBuffer = gl.createBuffer(); // 创建缓冲区    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer); //绑定缓冲区   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW); //给缓冲区填充数据     var radius = 20,angle = 1.5;   var lightColor = [1.0,1.0,1.0],lightPosition = [0.01,20,0],ambientLightColor = [0.3,0.3,0.3];    var viewMatrix = mat4.create(), projectMatrix = mat4.create(),         mvpMatrix = mat4.create();   setInterval(draw,100);   function draw(){         var cameraPos = [radius * Math.sin(angle),10 ,radius * Math.cos(angle)],         center = [0.0,0,0],up = [0,1,0],near = 1,far = 100;         mat4.lookAt(viewMatrix,cameraPos,center,up);         mat4.perspective(projectMatrix,Math.PI/2,1,near,far);         mat4.mul(mvpMatrix,projectMatrix,viewMatrix);                gl.viewport(0,0,800,800);// 设置视口大小           gl.clearColor(0.0,0.0,1.0,1.0);//设置背景色为蓝色           gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);//清空             drawNormal(); // 绘制正常场景         angle += 0.04;          }    function drawNormal(){       gl.useProgram(normalProgram);// 使用正常着色器程序       // 设置着色器程序uniform变量       gl.uniform3fv(normalProgram.uLightColor,lightColor);       gl.uniform3fv(normalProgram.uLightPosition,lightPosition);       gl.uniform3fv(normalProgram.uAmbientLightColor,ambientLightColor)       gl.uniform1f(normalProgram.uPower, 0.05 + document.querySelector('#power').value / 500)        gl.uniform1f(normalProgram.uScale, 1.0)       gl.uniformMatrix4fv(normalProgram.uMVPMatrix,false,mvpMatrix);        gl.enableVertexAttribArray(normalProgram.aPosition);

在之前这篇文章,
WebGL 单通道wireframe渲染
我们介绍了webgl如何实现单通道wireframe的效果。

本篇文章就是在此技术原理基础之上,来实现发光的wireframe效果。

要实现发光的效果

所谓的发光的效果,就是颜色的渐变。 渐变越慢,发光的效果越明显,渐变越快,发光效果越不明显。

其实wireframe本身就是在两种颜色之间进行渐变,从代码也可以看出:

gl_FragColor.rgb = mix(vec3(.0,.0,.0), vec3(1.0,1.0,1.0),edgeFactor3());

其中edgeFactor3() 就是通过重心坐标的变换计算出来的一个渐变过度的参数。
但是由于这种渐变的效果不够慢,所以 发光的效果不是很明显,因此我们可以改进如下效果,把渐变的参数通过pow函数进行处理,代码如下:

 float interopter = edgeFactor3();  interopter = pow(interopter, uPower); gl_FragColor.rgb = mix(vec3(1.0,.0,.0), vec3(1.0,1.0,1.0),interopter);

其中uPower表示pow函数的次方,此处取值范围0~1,通过uniform变量传递该变量的数值,最终的效果如下:
webgl实现发光线框(glow wireframe)效果

上面是既有线框部分,也有面的部分。如果想实现只有线框的效果,可以启用透明的机制,并对颜色的透明度也进行渐变插值运算,透明设置代码如下:

    // 启用混合功能       gl.enable(gl.DEPTH_TEST);       gl.enable(gl.BLEND);       gl.disable(gl.DEPTH_WRITEMASK);       // 设置混合函数       gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

shader代码增加以下的这行代码:

gl_FragColor.a = mix(1.0, .0,interopter);

效果如下图所示:
webgl实现发光线框(glow wireframe)效果

发光的线框

如果模型替换成球形,效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-球体

加载模型的效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-模型

如果修改shader中的edgeFactor3函数,把计算最小值,改为计算平均值,代码如下:

float edgeFactor3(){         vec3 d = fwidth(vBarycentric);         vec3 a3 = smoothstep(vec3(0.0), d * 30.0 , vBarycentric);       //return min(min(a3.x, a3.y), a3.z);        return (a3.x + a3.y + a3.z) / 3.0;   }

得到最终的效果如下图所示(立方体):

webgl实现发光线框(glow wireframe)效果

发光的线框-立方体

替换成模型,效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-模型

如果结合混合模式中的相加混合,加上多个模型的叠加,可以得到更明显的发光叠加的效果,此种效果经常用于智慧园区,智慧楼宇中楼宇的发光效果呈现。
首先把混合模式改成相加混合,代码如下:

     // 设置混合函数       // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);       gl.blendFunc(gl.SRC_ALPHA, gl.ONE); //相加混合模式

然后同时绘制多个模型,代码如下:

 for (var i = 0;i < 10;i ++){             gl.uniform1f(normalProgram.uScale, 1 - i/10)             gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);            }

最终的效果如下所示:

webgl实现发光线框(glow wireframe)效果

叠加效果

webgl实现发光线框(glow wireframe)效果

叠加效果

webgl实现发光线框(glow wireframe)效果

叠加效果

全部代码:

<!DOCTYPE html> <html>     <head>         <meta charset="utf-8">         <title>单通道 solid wireframe</title>         <script type="text/javascript" src="js/utils.js"></script>         <script type="text/javascript" src="libs/gl-matrix.js"></script>         <!-- <script type="text/javascript" src="js/shaders.js"></script> -->         <script type="text/javascript" src="js/shaders3.js"></script>         <!-- <script type="text/javascript" src="js/shaders3.js"></script> -->     <script type="text/javascript" src="js/SolidWireframe.js"></script>     <script type="text/javascript" src="js/Sphere.js"></script>      </head>   <body onload= 'load();'>      <div>        power:<input type="range" id="power"/>      </div>       <canvas id = "webgl" width = "800" height = "800" />      </body> </html>
var utils = {};  utils.getCanvas= function (id) {     if(id instanceof HTMLCanvasElement){         return id;     }     var canvas =  document.getElementById(id);     if(canvas instanceof HTMLCanvasElement){         return canvas;     }     return null; } utils.getWebGLContext = function(id){     var canvas = utils.getCanvas(id);     var options = {         antialias:true,     }     if(canvas != null){         return canvas.getContext('webgl',options) || canvas.getContext('experimental-webgl',options);     }     throw 'expect canvas but find none'; }  utils.buildProgram = function(gl,vertexSource,fragmentSource){     var vertexShader = utils.createShader(gl,gl.VERTEX_SHADER,vertexSource);     var fragmentShader = utils.createShader(gl,gl.FRAGMENT_SHADER,fragmentSource);     var program = gl.createProgram(); // 创建程序     // 程序绑定着色器       gl.attachShader(program,vertexShader);       gl.attachShader(program,fragmentShader);       // 链接程序     gl.linkProgram(program)     if ( !gl.getProgramParameter( program, gl.LINK_STATUS) ) {           var info = gl.getProgramInfoLog(program);           console.log("Could not initialise shadern" + "VALIDATE_STATUS: " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + ", gl error [" + gl.getError() + "]");           throw 'Could not compile WebGL program. nn' + info;     }     // 使用程序     gl.useProgram(program)     return program; }  utils.createShader = function(gl,type,source){     var shader = gl.createShader(type); // 创建着色器对象        gl.shaderSource(shader,source); // 将着色器源码写入对象        gl.compileShader(shader); // 编译着色器        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS) && !gl.isContextLost()) {         var info = gl.getShaderInfoLog(shader);         console.error(info);         throw 'Could not compile shader program. ' + info;     }        return shader; }  utils.createVertexBufferObject = function(gl,vertices){     var vertexBuffer = gl.createBuffer(); // 创建缓冲区     gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //绑定缓冲区     gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); //给缓冲区填充数据     return vertexBuffer; }  utils.initVertexBufferObject = function(gl,vertices,size,location){     var vertexBuffer = gl.createBuffer(); // 创建缓冲区     gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //绑定缓冲区     gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); //给缓冲区填充数据     if(size && location != null){            gl.vertexAttribPointer(location,size,gl.FLOAT,false,0,0); // 把缓冲区分配给attribute变量     }      return vertexBuffer; }  utils.getVariableLocation = function(gl,program,name,attribute){     var location;     if(attribute){ // 是attribute变量         location = gl.getAttribLocation(program,name);          if(location < 0){                  throw "Get attribute variable "+name+"'s location fail";               }     }else{ // 表示是uniform 变量         location = gl.getUniformLocation(program,name);         if(location == null){             throw "Failed get unifrom variable "+name+"'s location"         }     }     return location; }  utils.getProgramVariableLocations2 = function (gl,program,variableNames,isAttribute) {       var len = variableNames.length;       for (var i = 0; i < len; i++) {         var variableName = variableNames[i];           if (isAttribute) { // attribute 变量             var location = program[variableName] = utils.getVariableLocation(gl, program, variableName, true);           } else { // uniform 变量             program[variableName] = utils.getVariableLocation(gl, program, variableName, false);           }       } }  utils.getProgramVariableLocations = function(gl,program,variableNames){        var len = variableNames.length;        for(var i = 0; i < len;i ++){            var variableName = variableNames[i];            if(variableName.startsWith('a')){ // attribute 变量                var location = program[variableName] =  utils.getVariableLocation(gl,program,variableName,true);            }else{// uniform 变量                program[variableName] = utils.getVariableLocation(gl,program,variableName,false);            }           } }

“`
function load(){
var gl = window.gl= utils.getWebGLContext(‘webgl’,{
antialias: true,
});
if(gl == null){
console.log(“Get WebGL Context fail”);
return;
}

//  gl.getExtension("OES_standard_derivatives");   // 创建正常场景着色器程序  var normalProgram = utils.buildProgram(gl,normalVS,normalFS);   utils.getProgramVariableLocations(gl, normalProgram, [    'aPosition', 'aColor', 'aNormal', 'aBarycentric', 'uMVPMatrix',     'uLightColor', 'uLightPosition', 'uAmbientLightColor', 'uPower',    "uScale",   ]);  // var sphere = new Sphere(10,20,20); // //创建立方体数据 var verticesColors = new Float32Array([        // 前面四个顶点          -5,  5,  5, 1,0,0,//v0  红色 法线向量 0,0,1         -5, -5,  5, 1,0,0,// v1 红色           5,  5,  5,  1,0,0,// v2 红色           5,  -5, 5,  1,0,0,// v3 红色        // 右面四个顶点          5,5,5, 0,1,0, //v4 绿色 法线向量 1,0,0          5,-5,5, 0,1,0,//v5 绿色          5,5,-5, 0,1,0,//v6 绿色          5,-5,-5, 0,1,0,//v7 绿色        // 上面四个顶点          -5,5,-5,0,1,1, //v8 青色 法线向量 0,1,0          -5,5,5,0,1,1, //v9 青色           5,5,-5,0,1,1,//v10 青色           5,5,5,0,1,1,//v11 青色         // 下面四个顶点           -5,-5,5,1,1,1,//v12 白色 法线向量 0,-1,0          -5,-5,-5,1,1,1,//v13 白色          5,-5,5,1,1,1,//v14 白色           5,-5,-5,1,1,1,//v15 白色        // 左面的四个顶点         -5,5,-5,1,1,0,//v16 黄色  法线向量 -1,0,0         -5,-5,-5,1,1,0,//v17 黄色         -5,5,5,1,1,0,//v18黄色         -5,-5,5,1,1,0,//v19 黄色        // 后面的四个顶点         5,5,-5,1,0,1,//v20 红蓝 法线向量 0,0,-1         5,-5,-5,1,0,1,//v21 红蓝         -5,5,-5,1,0,1,//v22 红蓝         -5,-5,-5,1,0,1,//v23 红蓝   ]);    // var verticesColors = sphere.getVerticesArray();      var normals = new Float32Array([      // 前面四个顶点      0, 0, 1, //v0  红色 法线向量 0,0,1      0, 0, 1, // v1 红色      0, 0, 1, // v2 红色      0, 0, 1, // v3 红色      // 右面四个顶点      1, 0, 0, //v4 绿色 法线向量 1,0,0      1, 0, 0, //v5 绿色      1, 0, 0, //v6 绿色      1, 0, 0, //v7 绿色      // 上面四个顶点      0, 1, 0, //v8 青色 法线向量 0,1,0      0, 1, 0, //v9 青色       0, 1, 0, //v10 青色       0, 1, 0, //v11 青色       // 下面四个顶点      0, -1, 0, //v12 白色 法线向量 0,-1,0      0, -1, 0, //v13 白色      0, -1, 0, //v14 白色       0, -1, 0, //v15 白色      // 左面的四个顶点      -1, 0, 0, //v16 黄色  法线向量 -1,0,0      -1, 0, 0, //v17 黄色      -1, 0, 0, //v18黄色      -1, 0, 0, //v19 黄色      // 后面的四个顶点      0, 0, -1, //v20 红蓝 法线向量 0,0,-1      0, 0, -1, //v21 红蓝      0, 0, -1, //v22 红蓝      0, 0, -1, //v23 红蓝    ]);    // var normals = sphere.getNormals();   var indices = new Uint16Array([         0,1,2 ,2,1,3, // 前表面索引         4,5,6,6,5,7, //右         8,9,10,10,9,11, //上         12,13,14,14,13,15, // 下         16,17,18,18,17,19,//左          20,21,22,22,21,23,//后   ]);   // var indices = sphere.getIndicesArray();   // 启用混合功能   gl.enable(gl.DEPTH_TEST);   gl.enable(gl.BLEND);   // gl.disable(gl.DEPTH_WRITEMASK);   // 设置混合函数   // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);   gl.blendFunc(gl.SRC_ALPHA, gl.ONE);  var barycentrics =new Float32Array(computeBarycentric(indices));    //  var barycentrics = sphere.getBarycentric();     var floatSize = verticesColors.BYTES_PER_ELEMENT;   var verticesColorsBuffer = utils.initVertexBufferObject(gl, verticesColors);   var normalsBuffer = utils.initVertexBufferObject(gl,normals)    var barycentricBuffer = utils.initVertexBufferObject(gl,barycentrics);     var indexBuffer = gl.createBuffer(); // 创建缓冲区    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer); //绑定缓冲区   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW); //给缓冲区填充数据     var radius = 20,angle = 1.5;   var lightColor = [1.0,1.0,1.0],lightPosition = [0.01,20,0],ambientLightColor = [0.3,0.3,0.3];    var viewMatrix = mat4.create(), projectMatrix = mat4.create(),         mvpMatrix = mat4.create();   setInterval(draw,100);   function draw(){         var cameraPos = [radius * Math.sin(angle),10 ,radius * Math.cos(angle)],         center = [0.0,0,0],up = [0,1,0],near = 1,far = 100;         mat4.lookAt(viewMatrix,cameraPos,center,up);         mat4.perspective(projectMatrix,Math.PI/2,1,near,far);         mat4.mul(mvpMatrix,projectMatrix,viewMatrix);                gl.viewport(0,0,800,800);// 设置视口大小           gl.clearColor(0.0,0.0,1.0,1.0);//设置背景色为蓝色           gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);//清空             drawNormal(); // 绘制正常场景         angle += 0.04;          }    function drawNormal(){       gl.useProgram(normalProgram);// 使用正常着色器程序       // 设置着色器程序uniform变量       gl.uniform3fv(normalProgram.uLightColor,lightColor);       gl.uniform3fv(normalProgram.uLightPosition,lightPosition);       gl.uniform3fv(normalProgram.uAmbientLightColor,ambientLightColor)       gl.uniform1f(normalProgram.uPower, 0.05 + document.querySelector('#power').value / 500)        gl.uniform1f(normalProgram.uScale, 1.0)       gl.uniformMatrix4fv(normalProgram.uMVPMatrix,false,mvpMatrix);        gl.enableVertexAttribArray(normalProgram.aPosition);

在之前这篇文章,
WebGL 单通道wireframe渲染
我们介绍了webgl如何实现单通道wireframe的效果。

本篇文章就是在此技术原理基础之上,来实现发光的wireframe效果。

要实现发光的效果

所谓的发光的效果,就是颜色的渐变。 渐变越慢,发光的效果越明显,渐变越快,发光效果越不明显。

其实wireframe本身就是在两种颜色之间进行渐变,从代码也可以看出:

gl_FragColor.rgb = mix(vec3(.0,.0,.0), vec3(1.0,1.0,1.0),edgeFactor3());

其中edgeFactor3() 就是通过重心坐标的变换计算出来的一个渐变过度的参数。
但是由于这种渐变的效果不够慢,所以 发光的效果不是很明显,因此我们可以改进如下效果,把渐变的参数通过pow函数进行处理,代码如下:

 float interopter = edgeFactor3();  interopter = pow(interopter, uPower); gl_FragColor.rgb = mix(vec3(1.0,.0,.0), vec3(1.0,1.0,1.0),interopter);

其中uPower表示pow函数的次方,此处取值范围0~1,通过uniform变量传递该变量的数值,最终的效果如下:
webgl实现发光线框(glow wireframe)效果

上面是既有线框部分,也有面的部分。如果想实现只有线框的效果,可以启用透明的机制,并对颜色的透明度也进行渐变插值运算,透明设置代码如下:

    // 启用混合功能       gl.enable(gl.DEPTH_TEST);       gl.enable(gl.BLEND);       gl.disable(gl.DEPTH_WRITEMASK);       // 设置混合函数       gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

shader代码增加以下的这行代码:

gl_FragColor.a = mix(1.0, .0,interopter);

效果如下图所示:
webgl实现发光线框(glow wireframe)效果

发光的线框

如果模型替换成球形,效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-球体

加载模型的效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-模型

如果修改shader中的edgeFactor3函数,把计算最小值,改为计算平均值,代码如下:

float edgeFactor3(){         vec3 d = fwidth(vBarycentric);         vec3 a3 = smoothstep(vec3(0.0), d * 30.0 , vBarycentric);       //return min(min(a3.x, a3.y), a3.z);        return (a3.x + a3.y + a3.z) / 3.0;   }

得到最终的效果如下图所示(立方体):

webgl实现发光线框(glow wireframe)效果

发光的线框-立方体

替换成模型,效果如下:

webgl实现发光线框(glow wireframe)效果

发光的线框-模型

如果结合混合模式中的相加混合,加上多个模型的叠加,可以得到更明显的发光叠加的效果,此种效果经常用于智慧园区,智慧楼宇中楼宇的发光效果呈现。
首先把混合模式改成相加混合,代码如下:

     // 设置混合函数       // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);       gl.blendFunc(gl.SRC_ALPHA, gl.ONE); //相加混合模式

然后同时绘制多个模型,代码如下:

 for (var i = 0;i < 10;i ++){             gl.uniform1f(normalProgram.uScale, 1 - i/10)             gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);            }

最终的效果如下所示:

webgl实现发光线框(glow wireframe)效果

叠加效果

webgl实现发光线框(glow wireframe)效果

叠加效果

webgl实现发光线框(glow wireframe)效果

叠加效果

全部代码:

<!DOCTYPE html> <html>     <head>         <meta charset="utf-8">         <title>单通道 solid wireframe</title>         <script type="text/javascript" src="js/utils.js"></script>         <script type="text/javascript" src="libs/gl-matrix.js"></script>         <!-- <script type="text/javascript" src="js/shaders.js"></script> -->         <script type="text/javascript" src="js/shaders3.js"></script>         <!-- <script type="text/javascript" src="js/shaders3.js"></script> -->     <script type="text/javascript" src="js/SolidWireframe.js"></script>     <script type="text/javascript" src="js/Sphere.js"></script>      </head>   <body onload= 'load();'>      <div>        power:<input type="range" id="power"/>      </div>       <canvas id = "webgl" width = "800" height = "800" />      </body> </html>
var utils = {};  utils.getCanvas= function (id) {     if(id instanceof HTMLCanvasElement){         return id;     }     var canvas =  document.getElementById(id);     if(canvas instanceof HTMLCanvasElement){         return canvas;     }     return null; } utils.getWebGLContext = function(id){     var canvas = utils.getCanvas(id);     var options = {         antialias:true,     }     if(canvas != null){         return canvas.getContext('webgl',options) || canvas.getContext('experimental-webgl',options);     }     throw 'expect canvas but find none'; }  utils.buildProgram = function(gl,vertexSource,fragmentSource){     var vertexShader = utils.createShader(gl,gl.VERTEX_SHADER,vertexSource);     var fragmentShader = utils.createShader(gl,gl.FRAGMENT_SHADER,fragmentSource);     var program = gl.createProgram(); // 创建程序     // 程序绑定着色器       gl.attachShader(program,vertexShader);       gl.attachShader(program,fragmentShader);       // 链接程序     gl.linkProgram(program)     if ( !gl.getProgramParameter( program, gl.LINK_STATUS) ) {           var info = gl.getProgramInfoLog(program);           console.log("Could not initialise shadern" + "VALIDATE_STATUS: " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + ", gl error [" + gl.getError() + "]");           throw 'Could not compile WebGL program. nn' + info;     }     // 使用程序     gl.useProgram(program)     return program; }  utils.createShader = function(gl,type,source){     var shader = gl.createShader(type); // 创建着色器对象        gl.shaderSource(shader,source); // 将着色器源码写入对象        gl.compileShader(shader); // 编译着色器        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS) && !gl.isContextLost()) {         var info = gl.getShaderInfoLog(shader);         console.error(info);         throw 'Could not compile shader program. ' + info;     }        return shader; }  utils.createVertexBufferObject = function(gl,vertices){     var vertexBuffer = gl.createBuffer(); // 创建缓冲区     gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //绑定缓冲区     gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); //给缓冲区填充数据     return vertexBuffer; }  utils.initVertexBufferObject = function(gl,vertices,size,location){     var vertexBuffer = gl.createBuffer(); // 创建缓冲区     gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer); //绑定缓冲区     gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW); //给缓冲区填充数据     if(size && location != null){            gl.vertexAttribPointer(location,size,gl.FLOAT,false,0,0); // 把缓冲区分配给attribute变量     }      return vertexBuffer; }  utils.getVariableLocation = function(gl,program,name,attribute){     var location;     if(attribute){ // 是attribute变量         location = gl.getAttribLocation(program,name);          if(location < 0){                  throw "Get attribute variable "+name+"'s location fail";               }     }else{ // 表示是uniform 变量         location = gl.getUniformLocation(program,name);         if(location == null){             throw "Failed get unifrom variable "+name+"'s location"         }     }     return location; }  utils.getProgramVariableLocations2 = function (gl,program,variableNames,isAttribute) {       var len = variableNames.length;       for (var i = 0; i < len; i++) {         var variableName = variableNames[i];           if (isAttribute) { // attribute 变量             var location = program[variableName] = utils.getVariableLocation(gl, program, variableName, true);           } else { // uniform 变量             program[variableName] = utils.getVariableLocation(gl, program, variableName, false);           }       } }  utils.getProgramVariableLocations = function(gl,program,variableNames){        var len = variableNames.length;        for(var i = 0; i < len;i ++){            var variableName = variableNames[i];            if(variableName.startsWith('a')){ // attribute 变量                var location = program[variableName] =  utils.getVariableLocation(gl,program,variableName,true);            }else{// uniform 变量                program[variableName] = utils.getVariableLocation(gl,program,variableName,false);            }           } }

“`
function load(){
var gl = window.gl= utils.getWebGLContext(‘webgl’,{
antialias: true,
});
if(gl == null){
console.log(“Get WebGL Context fail”);
return;
}

//  gl.getExtension("OES_standard_derivatives");   // 创建正常场景着色器程序  var normalProgram = utils.buildProgram(gl,normalVS,normalFS);   utils.getProgramVariableLocations(gl, normalProgram, [    'aPosition', 'aColor', 'aNormal', 'aBarycentric', 'uMVPMatrix',     'uLightColor', 'uLightPosition', 'uAmbientLightColor', 'uPower',    "uScale",   ]);  // var sphere = new Sphere(10,20,20); // //创建立方体数据 var verticesColors = new Float32Array([        // 前面四个顶点          -5,  5,  5, 1,0,0,//v0  红色 法线向量 0,0,1         -5, -5,  5, 1,0,0,// v1 红色           5,  5,  5,  1,0,0,// v2 红色           5,  -5, 5,  1,0,0,// v3 红色        // 右面四个顶点          5,5,5, 0,1,0, //v4 绿色 法线向量 1,0,0          5,-5,5, 0,1,0,//v5 绿色          5,5,-5, 0,1,0,//v6 绿色          5,-5,-5, 0,1,0,//v7 绿色        // 上面四个顶点          -5,5,-5,0,1,1, //v8 青色 法线向量 0,1,0          -5,5,5,0,1,1, //v9 青色           5,5,-5,0,1,1,//v10 青色           5,5,5,0,1,1,//v11 青色         // 下面四个顶点           -5,-5,5,1,1,1,//v12 白色 法线向量 0,-1,0          -5,-5,-5,1,1,1,//v13 白色          5,-5,5,1,1,1,//v14 白色           5,-5,-5,1,1,1,//v15 白色        // 左面的四个顶点         -5,5,-5,1,1,0,//v16 黄色  法线向量 -1,0,0         -5,-5,-5,1,1,0,//v17 黄色         -5,5,5,1,1,0,//v18黄色         -5,-5,5,1,1,0,//v19 黄色        // 后面的四个顶点         5,5,-5,1,0,1,//v20 红蓝 法线向量 0,0,-1         5,-5,-5,1,0,1,//v21 红蓝         -5,5,-5,1,0,1,//v22 红蓝         -5,-5,-5,1,0,1,//v23 红蓝   ]);    // var verticesColors = sphere.getVerticesArray();      var normals = new Float32Array([      // 前面四个顶点      0, 0, 1, //v0  红色 法线向量 0,0,1      0, 0, 1, // v1 红色      0, 0, 1, // v2 红色      0, 0, 1, // v3 红色      // 右面四个顶点      1, 0, 0, //v4 绿色 法线向量 1,0,0      1, 0, 0, //v5 绿色      1, 0, 0, //v6 绿色      1, 0, 0, //v7 绿色      // 上面四个顶点      0, 1, 0, //v8 青色 法线向量 0,1,0      0, 1, 0, //v9 青色       0, 1, 0, //v10 青色       0, 1, 0, //v11 青色       // 下面四个顶点      0, -1, 0, //v12 白色 法线向量 0,-1,0      0, -1, 0, //v13 白色      0, -1, 0, //v14 白色       0, -1, 0, //v15 白色      // 左面的四个顶点      -1, 0, 0, //v16 黄色  法线向量 -1,0,0      -1, 0, 0, //v17 黄色      -1, 0, 0, //v18黄色      -1, 0, 0, //v19 黄色      // 后面的四个顶点      0, 0, -1, //v20 红蓝 法线向量 0,0,-1      0, 0, -1, //v21 红蓝      0, 0, -1, //v22 红蓝      0, 0, -1, //v23 红蓝    ]);    // var normals = sphere.getNormals();   var indices = new Uint16Array([         0,1,2 ,2,1,3, // 前表面索引         4,5,6,6,5,7, //右         8,9,10,10,9,11, //上         12,13,14,14,13,15, // 下         16,17,18,18,17,19,//左          20,21,22,22,21,23,//后   ]);   // var indices = sphere.getIndicesArray();   // 启用混合功能   gl.enable(gl.DEPTH_TEST);   gl.enable(gl.BLEND);   // gl.disable(gl.DEPTH_WRITEMASK);   // 设置混合函数   // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);   gl.blendFunc(gl.SRC_ALPHA, gl.ONE);  var barycentrics =new Float32Array(computeBarycentric(indices));    //  var barycentrics = sphere.getBarycentric();     var floatSize = verticesColors.BYTES_PER_ELEMENT;   var verticesColorsBuffer = utils.initVertexBufferObject(gl, verticesColors);   var normalsBuffer = utils.initVertexBufferObject(gl,normals)    var barycentricBuffer = utils.initVertexBufferObject(gl,barycentrics);     var indexBuffer = gl.createBuffer(); // 创建缓冲区    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer); //绑定缓冲区   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW); //给缓冲区填充数据     var radius = 20,angle = 1.5;   var lightColor = [1.0,1.0,1.0],lightPosition = [0.01,20,0],ambientLightColor = [0.3,0.3,0.3];    var viewMatrix = mat4.create(), projectMatrix = mat4.create(),         mvpMatrix = mat4.create();   setInterval(draw,100);   function draw(){         var cameraPos = [radius * Math.sin(angle),10 ,radius * Math.cos(angle)],         center = [0.0,0,0],up = [0,1,0],near = 1,far = 100;         mat4.lookAt(viewMatrix,cameraPos,center,up);         mat4.perspective(projectMatrix,Math.PI/2,1,near,far);         mat4.mul(mvpMatrix,projectMatrix,viewMatrix);                gl.viewport(0,0,800,800);// 设置视口大小           gl.clearColor(0.0,0.0,1.0,1.0);//设置背景色为蓝色           gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);//清空             drawNormal(); // 绘制正常场景         angle += 0.04;          }    function drawNormal(){       gl.useProgram(normalProgram);// 使用正常着色器程序       // 设置着色器程序uniform变量       gl.uniform3fv(normalProgram.uLightColor,lightColor);       gl.uniform3fv(normalProgram.uLightPosition,lightPosition);       gl.uniform3fv(normalProgram.uAmbientLightColor,ambientLightColor)       gl.uniform1f(normalProgram.uPower, 0.05 + document.querySelector('#power').value / 500)        gl.uniform1f(normalProgram.uScale, 1.0)       gl.uniformMatrix4fv(normalProgram.uMVPMatrix,false,mvpMatrix);        gl.enableVertexAttribArray(normalProgram.aPosition);

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » webgl实现发光线框(glow wireframe)效果求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们