背景
本篇收录于《数据可视化和图形学》专栏
首先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篇) 计算机图形学相干的基础知识都会带一遍。而后后续次要写数据可视化方向。
发表回复