共计 4955 个字符,预计需要花费 13 分钟才能阅读完成。
背景
本篇收录于《数据可视化和图形学》专栏
首先 Canvas 2D 和 WebGL 上篇文章做了简略的比照, 能够理解到 WebGL 的 API 相对来说比拟难懂。所以我感觉后续的 coding 更多是采取 WebGL 来实现咱们的成果(欸, 就是玩~) 当然知识点还是从简略到简单。。。如果一上来就介绍视觉物理, 光照 / 全局光照, 抗锯齿, 提早渲染, 实时渲染 …. 或者我创立专栏的初衷就丢掉了; 当然如果有想深刻探讨的能够私下交换。
上文实现了简略的 2d 图形 有须要能够参考上文
本篇纲要
- 什么是纹理?2d 与 3d 纹理贴图区别?
- 纹理管线 The Texturing Pipeline
- coding (WebGL 中简略应用纹理)
1. 什么是纹理?2d 与 3d 纹理贴图区别?
在计算机图形学中,纹理贴图(Texturing)是应用图像、函数或其余数据源来扭转物体外表外观的技术。
2d 纹理
二维纹理 (2D texture) 是一张简略的位图图片,用于为三维模型提供外表点的色彩值
3d 纹理
三维纹理(3D texture),能够被认为由很多张 2D 纹理组成,用于形容三维空间数据的图片。
2. 通用纹理管线 The Texturing Pipeline
渲染管线对于日常应用可能是个黑盒 然而了解这个对你的编程是有莫大的进步 …(不懂也没关系原本这块介绍的也很浅 大部分从别的中央 copy 的. 哈哈)
简略来说, 纹理 (Texturing) 是一种针对物体外表属性进行“建模”的高效技术。图像纹理中的像素通常被称为纹素(Texels), 区别于屏幕上的像素。
请看下图 — 单个纹理利用纹理贴图的具体过程
- 投射函数(projector function) 就是将空间中的三维点转化为纹理坐标,也就是获取外表的地位并将其投影到参数空间中。例如有球形、圆柱、以及立体投影相干的函数
- 映射函数(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);
}
webgl-utils.js
webgl 相干函数封装工具库m4.js
矩阵相干数学函数库
残缺代码示例 请点击 git 仓库查看代码示例
2D 渲染方面你可能须要理解的有
- 纹理缓存
- 纹理压缩
- 2D/2D 纹理优化
- 渲染优化 …
最初
最初强烈心愿大家学习相干理论知识; 实践可能日常用到的中央很少, 然而它能决定你走多远。(有的人问难怎么办, 勤于练习吧), 写作速度呢 该专栏我减速(一周 1 - 2 篇) 其余专栏(1 篇) 计算机图形学相干的基础知识都会带一遍。而后后续次要写数据可视化方向。