LayaBox引擎源码阅读笔记(三、WebGL学习(1))

5 篇文章 0 订阅
4 篇文章 0 订阅

在阅读laya.webgl.js中的源代码的时候,先来重温下webgl编程方面的知识。

以下内容均为我个人对学习的一个总结,不能完全正确,只是我对于知识的一个理解,如果哪天我发现我理解错了,我会尽快回来修改。

什么是WebGL

  在我来看WebGL是对于canvas的一次升级,它们两者都是一个绘图指令集,但是WebGL功能更加强大,并且生来就是为了更好的支持3D在Web上而开发的,因为存在着色器的特性,让我们可以编辑我们想要的效果。

  canvas只是跑在cpu上由js操作的指令集,而WebGL是需要编译成二进制由浏览器交给显卡进行渲染。性能会比canvas高很多,也会降低cpu计算压力,让cpu着力去处理我们自己的逻辑。

  在我们使用WebGL的时候着色器一开始就编译完成,我们只是通过着色器暴露的属性进行修改值达到我们想要的效果。

顶点着色器,片元着色器

  WebGL着色器语言为GLSL,WebGL相当于OpenGL在Web上面的一个版本,因此很多API基本与opengl一样。

  其中着色器程序包含顶点着色器与片元着色器

  顶点着色器控制顶点位置,颜色等特性,片元着色器则逐片(像素)处理光照颜色等。我把它他们的关系立即为html与css的关系,一个是架构,另一个则是美化。

  两个着色器中都包含一个main函数,这个函数在我们执行时会自动调用。另外就包含我们自己需要的属性以及方法。

  这里值得注意的是,所有我们声明的方法,属性都必须在main函数之前,下面的a_Color并不会被编译

var vs = `
    void main(){
        do something
    }
attribute vec4 a_Color;
`;

定义顶点着色器与片元着色器

  如果想要创建一个shader程序,我们首先需要定义一个顶点着色器vs,片元着色器fs,然后通过WebGL提供的API接口初始化shader

    第一步:分别定义两个着色器,这次我们绘制一个立方体,所以需要定义顶点数据及颜色数据。

   顶点着色器中的v_Color会在片元着色器编译时使用并最终赋值到gl_FragColor

var vs = `
    attribute vec4 a_Position;
    attribute vec4 a_Color;
    uniform mat4 u_MvpMatrix;
    varying vec4 v_Color;
    void main(){
        gl_Position = u_MvpMatrix * a_Position;;
        v_Color = a_Color;
    }
`;

var fs = `
    #ifdef GL_ES
    precision mediump float;
    #endif
    varying vec4 v_Color;
    void main(){
        gl_FragColor = v_Color;
    }
`;

 获取gl对象

var canvas = document.getElementById("webgl");
var gl = canvas.getContext("webgl");

   gl对象是WebGLRenderingContext,这个对象提供基于OpenGL ES 2.0的绘图上下文,我们就可以拿这个对象在canvas上绘图。

创建Shader程序并编译vs,fs

function initShader(gl,vs,fs){
    var program = createProgram(gl,vs,fs);
    if(!program){
        return false
    }
    gl.useProgram(program);
    gl.program = program;
    return true;
}

function createProgram(gl,vs,fs){
    var vertexShader = loadShader(gl,gl.VERTEX_SHADER,vs);
    var fragmentShader = loadShader(gl,gl.FRAGMENT_SHADER,fs);
    if(!vertexShader || !fragmentShader){
        return null;
    }
    var program = gl.createProgram();
    gl.attachShader(program,vertexShader);
    gl.attachShader(program,vertexShader);
    gl.linkProgram(program);

    var linked = gl.getProgramParameter(program,gl.LINK_STATUS);
    if(!linked){
        var error = gl.getProgramInfoLog(program);
        gl.deleteProgram(program);
        gl.deleteShader(fragmentShader);
        gl.deleteShader(vertexShader);
        return null;
    }
    return program;
}

function loadShader(gl,type,source){
    var shader = gl.createShader(type);
    gl.shaderSource(shader,source);
    gl.compileShader(shader);
    var compiled = gl.getShaderParameter(shader,gl.COMPILE_STATUS);
    if(!compiled){
        var error = gl.getShaderInfoLog(shader);
        console.log("Failed to compile shader:" + error);
        gl.deleteShader(shader);
        return null;
    }
    return shader;
}

初始化顶点数据信息

  在创建了shader并编译之后,需要我们把自己数据传到顶点着色器,并重绘,这次我们绘制一个立方体,一个立方体存在6个面,每个面4个点,总共24个点,其中存在重复点。我们使用一个Float32Array来存储这些顶点数据

  

//24个顶点坐标数据
var vertices = new Float32Array([
    1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0,  
     1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0,  
     1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0, 
    -1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0, 
    -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0, 
     1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0  
]);

//24个顶点颜色数据
var colors = new Float32Array([     // Colors
    0.4, 0.4, 1.0,  0.4, 0.4, 1.0,  0.4, 0.4, 1.0,  0.4, 0.4, 1.0, 
    0.4, 1.0, 0.4,  0.4, 1.0, 0.4,  0.4, 1.0, 0.4,  0.4, 1.0, 0.4, 
    1.0, 0.4, 0.4,  1.0, 0.4, 0.4,  1.0, 0.4, 0.4,  1.0, 0.4, 0.4, 
    1.0, 1.0, 0.4,  1.0, 1.0, 0.4,  1.0, 1.0, 0.4,  1.0, 1.0, 0.4, 
    1.0, 1.0, 1.0,  1.0, 1.0, 1.0,  1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 
    0.4, 1.0, 1.0,  0.4, 1.0, 1.0,  0.4, 1.0, 1.0,  0.4, 1.0, 1.0  
  ]);

//每个面需要2个三角形组成,indices表示顶点索引 并不表示值
//使用顶点索引的方式绘制画布,需要使用drawElement函数
var indices = new Uint8Array([       
     0, 1, 2,   0, 2, 3,    // front
     4, 5, 6,   4, 6, 7,    // right
     8, 9,10,   8,10,11,    // up
    12,13,14,  12,14,15,    // left
    16,17,18,  16,18,19,    // down
    20,21,22,  20,22,23     // back
  ]);

  在创建了顶点数据之后就可以将这些数据绑定到缓冲区之中,最终供WebGL使用,在这之前就需要先创建缓冲区,WebGL中可以共同存在32个缓冲区。

var indexBuffer = gl.createBuffer();

 接着分别将数据绑定到a_Position与a_Color之中

initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position');
initArrayBuffer(gl, colors, 3, gl.FLOAT, 'a_Color');

function initArrayBuffer(gl, data, num, type, attribute) {
  var buffer = gl.createBuffer();   // Create a buffer object
  if (!buffer) {
    console.log('Failed to create the buffer object');
    return false;
  }
  //绑定buffer并挂载数据
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  
//获取着色器属性
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log('Failed to get the storage location of ' + attribute);
    return false;
  }
//赋值着色器数据
  gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
  //这一步一定不能少,不然即使赋值也不会被着色器使用,开启数据
  gl.enableVertexAttribArray(a_attribute);

  return true;
}

在分别绑定了数据之后,再把顶点索引数据也绑定要之前创建的buffer之中,就完成了数据的初始化

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

绘制立方体

在初始数据之后,首先要清空画布,在重新调用draw*函数,就完成了所有绘制工作

gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);

//因为是3d 所以存在一个视野  不然我们看过去还是一个2维图形
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
if (!u_MvpMatrix) {
console.log('Failed to get the storage location of u_MvpMatrix');
return;
}

var mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(30, 1, 1, 100);
mvpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0);

//写入数据
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

// Clear color and depth buffer
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

// Draw the cube
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

 总结:

个人感觉学习WebGL的难点主要在于矩阵的运算,很多时候是不知道为什么两个矩阵相乘就是平移一个物体,为什么一个矩阵点乘另一个矩阵能得到它的一个角度,如果搞清楚了这些,其它的跟我们普通的程序编码一样,大都是面向API编程,只要了解了API,多多练习,还是很容易学习的。因此,接下来我会努力学习矩阵方面知识。

  关于WebGL的API,大家直接看WebGL MDN即可,里面讲得很详细,遇到什么API忘记了,一查就很快知道了。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值