webGL学习随笔

风流意气都作罢 提交于 2020-02-21 23:55:10

 这是对threejs初步的一个理解

 

 

 

EleVR  elevr是基于webGL的原生API来实现的一个全景播放平台

首先elevr是根据绘制适口来进行双屏VR渲染的

 

if (eye === 0) { // left eye
    webGL.gl.viewport(0, 0, canvas.width / 2, canvas.height);
} else { // right eye
    webGL.gl.viewport(canvas.width / 2, 0, canvas.width / 2, canvas.height);
}

 

因为本身对webGLAPI不熟悉,所以改写是存在很大难度的

通过层层样式的修改,通过用localstorage本地存储的方式写入一个变量值,每次点击去修改其存储的值,然后根据存入值的一个状态来决定走哪个绘制vierport,最后reload页面,基本实现单双屏切换的状态,

但是用户体验肯定不是很乐观,稍后会对webGL进入深层次的一个学习,暂时用此方法替代单双屏切换的一个状态

具体实现代码

var btn = document.getElementById("btn");//获取点击按钮的dom元素
btn.onclick = function(){//注册一个点击事件
    if (window.localStorage) {//判断浏览器是否支持localstorage
        if(localStorage.VR && localStorage.VR == "on"){

//判断当前存入的VR是否有值或者值是什么
            localStorage.VR = "off";
        }else{
            localStorage.VR = "on";
        }
    } else {//如果浏览器不支持localstorage,不支持当前存入的一个切换
        alert("sorry,换个浏览器试试")
       //setcookie
    }

    location.reload();//reload页面
};

 

绘制vierport的代码

  if (window.localStorage) {//判断浏览器是否支持localstorage
      if (localStorage && localStorage.VR == "on") {
          if (eye === 0) { // left eye
              webGL.gl.viewport(0, 0, canvas.width / 2, canvas.height);
          } else { // right eye
              webGL.gl.viewport(canvas.width / 2, 0, canvas.width / 2, canvas.height);
          }
      }
      else {
          if (eye === 0) { // left eye
              webGL.gl.viewport(0, 0, canvas.width, canvas.height);
          } else { // right eye
              webGL.gl.viewport(0, 0, canvas.width, canvas.height);
          }
      }
  }else{
      if (eye === 0) { // left eye
          webGL.gl.viewport(0, 0, canvas.width / 2, canvas.height);
      } else { // right eye
          webGL.gl.viewport(canvas.width / 2, 0, canvas.width / 2, canvas.height);
      }
  }
    // Draw
    webGL.gl.bindBuffer(webGL.gl.ELEMENT_ARRAY_BUFFER, verticesIndexBuffer);
    webGL.gl.drawElements(webGL.gl.TRIANGLES, 6, webGL.gl.UNSIGNED_SHORT, 0);
},

 

 

Threejs

Threejs的具体切换原理是基于camare来切换绘制渲染,

 

 

 

 

webGL

webGL的纹理对象

纹理映射的四个步骤:

 1.准备好映射的图像   可以是任意图片png  jpg

 2.为几何图形配置纹理映射方式

   1》纹理坐标

     

纹理坐标值与图像尺寸无关,其坐标值是通用的

 

 

 

 

 3.加载纹理图像,对其进行一些配置,方便在webGL中使用它

 4.在片元着色器中将相应的纹素从纹理中抽取出来,并将纹素的颜色赋给片元

 

var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec2 a_TexCoord;\n' +
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  v_TexCoord = a_TexCoord;\n' +
  '}\n';

// Fragment shader program
var FSHADER_SOURCE =
  '#ifdef GL_ES\n' +
  'precision mediump float;\n' +
  '#endif\n' +
  'uniform sampler2D u_Sampler;\n' +
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
  '}\n';

function main() {   //主入口函数
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // S设置顶点信息
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // 配置纹理
  if (!initTextures(gl, n)) {
    console.log('Failed to intialize the texture.');
    return;
  }
}

function initVertexBuffers(gl) {
  var verticesTexCoords = new Float32Array([
    // 顶点坐标  纹理坐标
    -0.5,  0.5,   0.0, 1.0,
    -0.5, -0.5,   0.0, 0.0,
     0.5,  0.5,   1.0, 1.0,
     0.5, -0.5,   1.0, 0.0,
  ]);
  var n = 4; // 顶点的数量
  // 创建缓冲区对象
  var vertexTexCoordBuffer = gl.createBuffer();
  if (!vertexTexCoordBuffer) {
    console.log('Failed to create the buffer object');
    return -1;
  }

  // 将顶点坐标和纹理坐标写入缓冲区对象
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

  var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
  //Get the storage location of a_Position, assign and enable buffer
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return -1;
  }
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
  gl.enableVertexAttribArray(a_Position);  // Enable the assignment of the buffer object

  // 将纹理坐标分配给a_TexCoord 并且开启它
  var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
  if (a_TexCoord < 0) {
    console.log('Failed to get the storage location of a_TexCoord');
    return -1;
  }
  // Assign the buffer object to a_TexCoord variable
  gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
  gl.enableVertexAttribArray(a_TexCoord);  // Enable the assignment of the buffer object

  return n;
}

function initTextures(gl, n) {
  var texture = gl.createTexture();   // 创建纹理对象
  if (!texture) {
    console.log('Failed to create the texture object');
    return false;
  }

  // 获取u_Sampler的存储位置
  var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
  if (!u_Sampler) {
    console.log('Failed to get the storage location of u_Sampler');
    return false;
  }
  var image = new Image();  // 创建一个image对象
  if (!image) {
    console.log('Failed to create the image object');
    return false;
  }
  // 注册图像加载事件的相应函数
  image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); };
  // 把路径添加到src让浏览器加载
  image.src = '../resources/sky.jpg';

  return true;
}

function loadTexture(gl, n, texture, u_Sampler, image) {
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 对纹理图像进行Y轴反转,是因为webGL的坐标跟图像的y轴正好是相反的,所以需要y轴反转,或者在着色器中进行纹理的t轴反转


  // 开启0号纹理单元
  gl.activeTexture(gl.TEXTURE0);


  // 向target绑定纹理对象
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // 配置纹理参数
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  // 配置纹理图像
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
  
  // 将0号纹理传递给着色器
  gl.uniform1i(u_Sampler, 0);
  
  gl.clear(gl.COLOR_BUFFER_BIT);   // Clear <canvas>

  gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // D绘制矩形
}

 

 

1.顶点着色器中接收顶点的纹理坐标,光栅化后传递给片元着色器

2.片元着色器根据片元的纹理坐标,从纹理图像中抽取纹素颜色,赋给当前片元

3.设置顶点的纹理坐标initVertexBuffers()

4.准备待加载的纹理图像,令浏览器读取它initTextures()

5.监听纹理图像的加载事件,一旦加载完成,就在webGL系统中使用纹理loadTexture()

 

Gl_TEXTURE0-------GL_TEXTURE7八个纹理单元都与gl_TEXTURE_2D相关联

 

Gl_deleteTexture  删除一个纹理对象

LoadTexture(gl,n,texture,u_Sampler,image),loadtexture有五个参数,第一个参数为绘制上下文,第二个为绘制的顶点的个数,第三个为之前创建的纹理对象,第四个是从uniform中取得的变量u_Sampler的存储位置,第五个是我们需要渲染的图片

webGL不允许跨域的纹理图像

从浏览器加载图像到loadTexture函数调用图像

 

 

Gl.pixelStorei()方法的规范

 

Gl.activeTexture(gl.texTure0)激活纹理单元,参数为八个纹理单元

 

在对纹理对象进行操作之前,还需要绑定纹理单元,跟绑定缓冲区对象很类似

开启纹理对象并绑定纹理到target

Gl.bindTexTure(gl.texTure2D,texTure);绑定二维纹理对象

Gl.bindTexTure(gl.texTure_CUBE_MAP,texTure);绑定立方体纹理对象

 

 

注意:在webGL中没办法直接操控纹理对象,只能把纹理对象绑定到纹理单元上,然后通过操控纹理单元来操控纹理对象;

配置纹理对象的参数gl.texParameteri()

 

通过paname可以指定四个纹理参数

放大方法:gl.texTure_MAG_FILTER 当纹理的绘制本身比纹理更大时

缩小方法:gl.texTure_MIN_FILTER  当绘制的纹理比纹理更小时

水平填充方法:gl.texTure_wrap_s

垂直填充方法:gl.texTure_wrap_t

其图示如下:

 

 

 

 

将纹理图像分配给纹理对象gl.texImage2D()

 

 

如果为png格式的图片就用gl.rgba  jpg之类的就用gl.rgb

 

 

GLSL ES 编写着色器的编程语言

必须有且仅有一个main函数做为主函数

Void声明一个函数没有返回值

支持整型数和浮点数,支持布尔类型,不支持字符串

变量命名规范和js相似,以下是关键字

 

GLSL ES 是一种强类型语言,必须声明数据类型

float()将整形转换为浮点数

Int()转换为整型数

Booln()转换为布尔值,0false

 

Mat4矩阵函数

Vec4

 

Attribute  顶点着色器里声明的全局变量,

Varying   从顶点着色器向片元着色器传输数据,也是一个全局变量

Uniform

Const  表示声明一个变量且这个变量是不可改变的,并且不能重新赋值,否则会导致报错

 

 

 

片元着色器中要对float类型的精度做限定,不然就会编译报错

 

指令必须在最顶部

 

SetlokAt

 

GLSL 变量的使用

var VSHADER_SOURCE =  'attribute vec4 a_Position;\n' +  'void main() {\n' +  '  gl_Position = a_Position;\n' +  '  gl_PointSize = 10.0;\n' +  '}\n';

首先看顶点着色器,我们声明了a_Position变量,接下来我们通过下面的方法进行赋值。

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');

gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);

gl.enableVertexAttribArray(a_Position);

getAttribLocationprogram, name

js中,我们首先通过getAttribLocation()获取到变量 a_Position 在渲染器中的地址。 该方法接收两个变量

  • program project对象
  • name 变量名称

接下来我们通过vertexAttribPointer()方法,指定了如何处理渲染器中的值。

vertexAttribPointer(index, size, type, normalized, stride, offset);

比较复杂的一个方法

  • index 绑定在gl.ARRAY_BUFFER中的目标索引,比较难理解,但是通常情况下我们可以通过getAttribLocation来获取这个值。
  • size 每个属性的长度 [1234(default)]
  • type 类型 指定数据类型 我们有两个选择,[gl.FLOAT (default)|gl.FIXED]
  • normalized 是否初始化传入的数据 [gl.FALSE|gl.TRUE]
  • stride 每组数据的个数,[0-255]
  • offset 这一组中的数据起始位置(从0开始)

接下来启用 enableVertexAttribArray()

enableVertexAttribArray(index);

  • index vertexAttribPointer中的index

开启深度检测确定是否将其画出来,就是所谓的物体遮挡面是否显示 gl.enable(gl.DEPTH_TEST)开启隐藏面消除功能,当然还有多边形偏移的功能

在绘制之前,清除深度缓冲区gl.clear(gl_DEPTH_BUFFER_BIT);通常时绘制每一帧之前

深度缓冲区一般是用来存储深度信息的,深度一般都是Z轴方向,所以又称Z缓冲区

Gl.desable()关闭某种功能

 

A_Normal   

创建着色器对象

 

着色器对象管理一个顶点着色器或一个片元着色器,每一个着色器都有一个着色器对象

程序对象是管理着色器对象的容器,webGL中,一个程序对象必须包含一个顶点着色器和一个片元着色器

 

 

程序对象一旦被创建,就需要附上两个着色器对象

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!