关于javascript:图形学之纹理简单介绍WebGL进行coding

10次阅读

共计 4955 个字符,预计需要花费 13 分钟才能阅读完成。

背景

本篇收录于《数据可视化和图形学》专栏

首先 Canvas 2D 和 WebGL 上篇文章做了简略的比照, 能够理解到 WebGL 的 API 相对来说比拟难懂。所以我感觉后续的 coding 更多是采取 WebGL 来实现咱们的成果(欸, 就是玩~) 当然知识点还是从简略到简单。。。如果一上来就介绍视觉物理, 光照 / 全局光照, 抗锯齿, 提早渲染, 实时渲染 …. 或者我创立专栏的初衷就丢掉了; 当然如果有想深刻探讨的能够私下交换。

上文实现了简略的 2d 图形 有须要能够参考上文

本篇纲要

  1. 什么是纹理?2d 与 3d 纹理贴图区别?
  2. 纹理管线 The Texturing Pipeline
  3. coding (WebGL 中简略应用纹理)

1. 什么是纹理?2d 与 3d 纹理贴图区别?

在计算机图形学中,纹理贴图(Texturing)是应用图像、函数或其余数据源来扭转物体外表外观的技术。

2d 纹理

二维纹理 (2D texture) 是一张简略的位图图片,用于为三维模型提供外表点的色彩值

3d 纹理

三维纹理(3D texture),能够被认为由很多张 2D 纹理组成,用于形容三维空间数据的图片。

2. 通用纹理管线 The Texturing Pipeline

渲染管线对于日常应用可能是个黑盒 然而了解这个对你的编程是有莫大的进步 …(不懂也没关系原本这块介绍的也很浅 大部分从别的中央 copy 的. 哈哈)
简略来说, 纹理 (Texturing) 是一种针对物体外表属性进行“建模”的高效技术。图像纹理中的像素通常被称为纹素(Texels), 区别于屏幕上的像素。

请看下图 — 单个纹理利用纹理贴图的具体过程

  1. 投射函数(projector function) 就是将空间中的三维点转化为纹理坐标,也就是获取外表的地位并将其投影到参数空间中。例如有球形、圆柱、以及立体投影相干的函数
  2. 映射函数(The Corresponder Function)的作用是将参数空间坐标(parameter-space coordinates)转换为纹理空间地位(texture space locations)。
  • 第一步。通过将 投影方程 (projector function) 使用于空间中的点,从而失去一组称为 参数空间值(parameter-space values) 的对于纹理的数值。
  • 第二步。在应用这些新值拜访纹理之前,能够应用一个或者多个 映射函数 (corresponder function) 参数空间值(parameter-space values) 转换到纹理空间。
  • 第三步。应用这些 纹理空间值 (texture-space locations) 从纹理中获取 相应的值(obtain value)。例如,能够应用图像纹理的数组索引来检索像素值。
  • 第四步。再应用 值变换函数(value transform function) 对检索后果进行值变换,最初应用失去的新来扭转外表属性,如材质或者着色法线等等。

法线 / 法向量相干常识须要自行补充 在图形学中十分重要(常常会见到)

coding (WebGL 中简略应用纹理贴图 2d)

简略用 WebGL 实现一个纹理(贴图), 并通过一个矩阵进行动画(旋转) 插入的 gif 有点卡 大略示意 …

1. shader 编写 减少了纹理坐标 批改了顶点坐标(采纳矩阵 / 旋转)

//vertex shader
attribute vec4 a_position;
attribute vec2 a_texcoord;  

uniform mat4 u_matrix;

varying vec2 v_texcoord;

void main() {
   gl_Position = u_matrix * a_position;
   // 纹理坐标传递
   v_texcoord = a_texcoord;
}
// fragment shader
precision mediump float;

varying vec2 v_texcoord;

uniform sampler2D u_texture;

void main() {gl_FragColor = texture2D(u_texture, v_texcoord);
}

2. 初始化 context(渲染上下文) 以及 着色器程序

var canvas = document.querySelector("#canvas");
var gl = canvas.getContext("webgl");
if (!gl) {return;}

3. 初始设置 GLSL 程序并新增缓冲区(纹理)

// 设置 GLSL 程序
  var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader", "fragment-shader"]);
  // 获取顶点坐标须要绑定的中央
  var positionLocation = gl.getAttribLocation(program, "a_position");
  var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");
  // 获取 uniforms
  var matrixLocation = gl.getUniformLocation(program, "u_matrix");
  var textureLocation = gl.getUniformLocation(program, "u_texture");
 
 
  // 创立缓冲区
  var positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  var positions = [
    -1, -1,
    -1,  1,
     1, -1,
     1, -1,
    -1,  1,
     1,  1,
  ];
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
  // 为纹理坐标创立缓冲区
  var texcoordBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
  var texcoords = [
    0, 0,
    0, 1,
    1, 0,
    1, 0,
    0, 1,
    1, 1,
  ];
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);

4. 创立并加载纹理

 // 解决图片跨域问题
 function requestCORSIfNotSameOrigin(img, url) {if ((new URL(url, window.location.href)).origin !== window.location.origin) {img.crossOrigin = "";}
  }
 // 创立一个纹理 {width: w, height: h, texture: tex}
 // 初始化 1x1 px 像素 图片加载完更新
 function loadImageAndCreateTextureInfo(url) {var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    // Fill the texture with a 1x1 blue pixel.
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
                  new Uint8Array([0, 0, 255, 255]));

    // let's assume all images are not a power of 2
    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);
      render()});
    requestCORSIfNotSameOrigin(img, url);
    img.src = url;

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

5. 绘制相干设置 纹理坐标并赋值矩阵坐标。应用着色程序 绘制。

function render(time) {
    time *= 0.001; // time 加速度 旋转图片
    // 从新设置 canvas 大小
    webglUtils.resizeCanvasToDisplaySize(gl.canvas);

    // 裁剪像素区
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.bindTexture(gl.TEXTURE_2D, texInfo.texture);

    // 应用着色器程序
    gl.useProgram(program);

    // 设置参数,让咱们能够绘制任何尺寸的图像
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
    gl.enableVertexAttribArray(texcoordLocation);
    gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);

    var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    var matrix = m4.scaling(1, aspect, 1);  // 可是试试将 aspect 改为 0.1 看看成果 - -
    matrix = m4.zRotate(matrix, time);
    matrix = m4.scale(matrix, 0.5, 0.5, 1);
    // 设置矩阵
    gl.uniformMatrix4fv(matrixLocation, false, matrix);

    // 从第 0 个 unit 开始获取纹理
    gl.uniform1i(textureLocation, 0);

    // 绘制 (2 triangles, 6 vertices)
    gl.drawArrays(gl.TRIANGLES, 0, 6);

    requestAnimationFrame(render);
}
  1. webgl-utils.js webgl 相干函数封装工具库
  2. m4.js 矩阵相干数学函数库
    残缺代码示例 请点击 git 仓库查看代码示例

2D 渲染方面你可能须要理解的有

  1. 纹理缓存
  2. 纹理压缩
  3. 2D/2D 纹理优化
  4. 渲染优化 …

最初

最初强烈心愿大家学习相干理论知识; 实践可能日常用到的中央很少, 然而它能决定你走多远。(有的人问难怎么办, 勤于练习吧), 写作速度呢 该专栏我减速(一周 1 - 2 篇) 其余专栏(1 篇) 计算机图形学相干的基础知识都会带一遍。而后后续次要写数据可视化方向。

正文完
 0