7

图形学之WebGL实现3D效果/ 纹理的使用

 3 years ago
source link: https://segmentfault.com/a/1190000040364334
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本篇收录于《前端读书会》专栏

上文介绍了纹理相关的内容,并使用WebGL进行了2d的实现,今天延续上节课的内容对于纹理的3D实现 也算是吸取了MDN-Tutorial的模式,感觉这样的学习轨道 也算是初衷(每次长篇大论的讲图形学的理论我怕我和你都坚持不下去)目前来说最好的方式了,对于WebGL的学习还是图形学的学习都能有一个认识。话不多说开始今天的内容(大部分是code的讲解 尽可能快速过完一些基础的API...第一步成为一个API工程师)

上文介绍了纹理相关的内容,有需要可以参考上文

  1. 2D与3D的区别?绘制思路?
  2. coding (WebGL中简单使用纹理)

coding部分引入webgl-utils(方面一些useprogram,linkProgram的简写 无太大魔力)&martix(矩阵的一些生成/计算..)工具类库

1.2d与3d的区别

首先我们都知道2D只是一个平面 3D可以理解为是6个平面,那么本篇文章最主要解决如何给添加5个平面就好(当然添加会伴随着一些问题,没关系 一步步的来)

本篇会对于上文的一些反馈进行改正 例如code 注释太少。。ok 我尽量多写点

WebGl coding 首先回顾一下步骤

  1. shader
  2. 创建program
  3. 处理坐标还有纹理相关
  4. 获取并绑定坐标信息
  5. 绘制draw

看着是如此简单 那么接下来我们一起来写一下

coding

coding开始之前先看看效果

缺一个gif图 明天找个有电脑的地方补充一下。

image.png

image.png

1. shader

有朋友反馈shader部分要照顾新手, 那么我逐行加下注释介绍下。当然更多细节信息还是得去看看相关资料呢。 我讲的不好(-- 主要是懒癌患者)

// vertex shader  
// attribute变量 只能作用于顶点着色器 vec4 是矢量 a_position 一个变量名
attribute vec4 a_position;
// attribute变量 只能作用于顶点着色器 vec4 是矢量 a_position 一个变量名
attribute vec2 a_texcoord;
// uniform变量(全局) 可以在顶点和片元使用(如果俩个着色命名同名 可进行共享) mat4 是矩阵 u_matrix变量名
uniform mat4 u_matrix;
// varying变量(全局) 目的就是为了给片元传输数据 vec2 矢量 v_texcoord变量名
varying vec2 v_texcoord;
// main类似主执行函数 但是是必须的 C/C++用法类似
void main() {
    // gl_Position 顶点坐标
   gl_Position = u_matrix * a_position;
   // 将纹理坐标传给片断着色器
   v_texcoord = a_texcoord;
}

// fragment shader 
// 精度限定  mediump中度精度 float数据类型 对了SL语言中是区分整型和浮点型的哦~
precision mediump float;
// varying变量(全局) 接收传输过来的数据 vec2 矢量 v_texcoord变量名
varying vec2 v_texcoord;
// uniform变量(只读 全局)  sampler2D纹理 u_texture变量名
uniform sampler2D u_texture;
// 主执行函数
void main() {
   // gl_FragColor 颜色信息  此处是纹理  texture2D是取样器纹理 v_texcoord纹理坐标 看js代码穿插理解
   gl_FragColor = texture2D(u_texture, v_texcoord);
}

我这个注释写的 我都佩服。哈哈 不懂记得留言。我都让你们问,你们也不问。要知道不会就问没什么不好意思的 学会了那是自己的 强大自己升职加薪 加油

2. 创建program

// 用到了webgl-Utils 其实就是useVertex 和useFragment 然后linkProgram...
var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-3d", "fragment-shader-3d"]);

3. 处理坐标还有纹理相关

// 顶点坐标 发生了变换 6个面 
var positions = new Float32Array(
    [
    -0.5, -0.5,  -0.5,
    -0.5,  0.5,  -0.5,
     0.5, -0.5,  -0.5,
    -0.5,  0.5,  -0.5,
     0.5,  0.5,  -0.5,
     0.5, -0.5,  -0.5,

    -0.5, -0.5,   0.5,
     0.5, -0.5,   0.5,
    -0.5,  0.5,   0.5,
    -0.5,  0.5,   0.5,
     0.5, -0.5,   0.5,
     0.5,  0.5,   0.5,

    -0.5,   0.5, -0.5,
    -0.5,   0.5,  0.5,
     0.5,   0.5, -0.5,
    -0.5,   0.5,  0.5,
     0.5,   0.5,  0.5,
     0.5,   0.5, -0.5,

    -0.5,  -0.5, -0.5,
     0.5,  -0.5, -0.5,
    -0.5,  -0.5,  0.5,
    -0.5,  -0.5,  0.5,
     0.5,  -0.5, -0.5,
     0.5,  -0.5,  0.5,

    -0.5,  -0.5, -0.5,
    -0.5,  -0.5,  0.5,
    -0.5,   0.5, -0.5,
    -0.5,  -0.5,  0.5,
    -0.5,   0.5,  0.5,
    -0.5,   0.5, -0.5,

     0.5,  -0.5, -0.5,
     0.5,   0.5, -0.5,
     0.5,  -0.5,  0.5,
     0.5,  -0.5,  0.5,
     0.5,   0.5, -0.5,
     0.5,   0.5,  0.5,

    ]);
// 解决图片跨域问题  当然方式很多你也可以绘制canvas2d 然后创建纹理 canvas在标准上支持了图片跨域 有兴趣可以查查
function requestCORSIfNotSameOrigin(img, url) {
    if ((new URL(url, window.location.href)).origin !== window.location.origin) {
      img.crossOrigin = "";
    }
}
// 加载图片 并在load完成绑定纹理
function loadImageAndCreateTextureInfo(url) {
    var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
   
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
                  new Uint8Array([0, 0, 255, 255]));

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

    var textureInfo = {
      width: 1,  
      height: 1,
      texture: tex,
    };
    var img = new Image();
    img.addEventListener('load', function() {
      textureInfo.width = img.width;
      textureInfo.height = img.height;

      gl.bindTexture(gl.TEXTURE_2D, textureInfo.texture);
      // 调用 texImage2D() 把已经加载的图片图形数据写到纹理
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
    });
    requestCORSIfNotSameOrigin(img, url);
    img.src = url;

    return textureInfo;
  }
  var texInfo = loadImageAndCreateTextureInfo('https://webglfundamentals.org/webgl/resources/leaves.jpg');

4. 获取并绑定坐标信息

  var positionLocation = gl.getAttribLocation(program, "a_position");
  var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");

  var matrixLocation = gl.getUniformLocation(program, "u_matrix");
  var textureLocation = gl.getUniformLocation(program, "u_texture");

  var positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  setGeometry(gl);

  var texcoordBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
  setTexcoords(gl);

  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
                new Uint8Array([0, 0, 255, 255]));

5. 使用program并提供buffer取数据 然后绘制

gl.vertexAttribPointer(
    positionLocation, size, type, normalize, stride, offset);

// 启用texcoord属性
gl.enableVertexAttribArray(texcoordLocation);

// 绑定texcoord buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);

....
gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);

ok, 效果就这样实现了.为了大家的练习, 说明一下需要注意的地方是上面的代码 纹理是采用了一个纹理 如果要改多个图片可以采取多个纹理。也可以合并成一个纹理 通过坐标信息进行调节。

全部代码请去github下载并使用

最后强烈希望大家学习相关理论知识;理论可能日常用到的地方很少,但是它能决定你走多远。(有的人问难怎么办,勤于练习吧),写作速度呢 该专栏我加速(一周1-2篇) 其他专栏(1篇) 计算机图形学相关的基础知识都会带一遍。然后后续主要写数据可视化方向。

下一篇写点什么呢? 个人准备把MDN-Tutorial部分的内容结个尾。然后开始正式的可视化环节(可以介绍几个大家常用的库 2D 3D的都可以,大家看看有什么好用的库需要我一起过一下的呢)。或者大家提提意见!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK