美文网首页
webgl 12.Texture (纹理)

webgl 12.Texture (纹理)

作者: lesliefang | 来源:发表于2017-08-29 20:01 被阅读323次

在 fragment shader 中计算颜色虽然灵活但不好实现复杂的效果。更常用的方法从一张图片采样得到颜色,把一张图片贴到几何体的表面,通常叫做纹理贴图,而采样的图片就叫做 texture image (纹理图片) 或 texture (纹理) 。

我们来实现把一张图片贴到正方形上面

texture.png

先来看看纹理坐标

texture coordinate system.png

这里用 (s, t) 表示, 有些书籍用的是 (u, v)
纹理坐标跟图片的大小无关,无论图片是长方形还是正方形,左下角都是 (0,0) 右上角是 (1,1)

纹理贴图要做的就是把图片的坐标映射到几何体坐标。这里我们采用下图的映射方式。

texture map.png

shader 中要加入纹理坐标

// vertex shader
var VERTEX_SHADER_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
var FRAGMENT_SHADER_SOURCE =
    'precision mediump float;\n' +
    'varying vec2 v_TexCoord;\n' +
    'uniform sampler2D u_Sampler;\n' +

    'void main() {\n' +
    '   gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
    '}\n';
var vertices = new Float32Array([
    -0.5, 0.5, 0.0, 1.0, // 前 2 位是位置坐标, 后 2 位是纹理坐标
    -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 image = new Image();
image.onload = function () {
    loadTexture(image);
    draw();
};
image.src = 'images/sky.jpg';

加载图片是一个异步的过程,对图片的处理要放在回调中
如果浏览器不允许加载本地图片,就自己用 node.js 等起个本地静态文件服务器

function loadTexture(image) {
    var texture = gl.createTexture();
    // 翻转 Y 轴
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    gl.activeTexture(gl.TEXTURE0);
    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);

    var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
    gl.uniform1i(u_Sampler, 0);
}

function draw() {
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}

下面我们重点分析一下 loadTexture 方法

  • gl.createTexture()

新建一个 texture object

  • gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1)

翻转 Y 轴,因为图片的 Y 轴和纹理的 Y 轴正好是反的

flip y.png
  • gl.activeTexture(texUnit)

激活一个 texUnit

texture unit.png

webgl 通过 Texture Unit 来支持多个纹理图片,意思是你可以传多个纹理图片到 webgl 中使用。

  • gl.bindTexture(target, texture)

把 texture 绑定到 target 上。
target 可取 gl.TEXTURE_2D 和 gl.TEXTURE_CUBE_MAP ,用来告诉 webgl 纹理图片的类型。
gl.TEXTURE_2D 表示一个二维的纹理图片

  • gl.texParameteri(target, pname, param)

设置纹理参数

有 4 种纹理参数可以设置:
gl.TEXTURE_MAG_FILTER
gl.TEXTURE_MIN_FILTER
gl.TEXTURE_WRAP_S
gl.TEXTURE_WRAP_T

因为纹理图片的大小跟几何图形的大小不一定是相等的,如果大小相等很简单每个像素一一对应就行了。如果不相等就得通过参数指定映射的方法。

gl.TEXTURE_MAG_FILTER : 用于当纹理图片大小小于集合体大小的时候,这时纹理图片需要被放大,通过这个参数指定纹理图片放大后怎么映射。
gl.TEXTURE_MIN_FILTER : 用于当纹理图片大小大于几何体大小的时候,这时纹理图片需要被缩小,通过这个参数指定纹理图片缩小后怎么映射。
gl.TEXTURE_WRAP_S: 用于指定当把图片映射到几何体的部分子区域时,左右两边剩余的区域怎么填充。
gl.TEXTURE_WRAP_T : 用于指定当把图片映射到几何体的部分子区域时,上下两边剩余的区域怎么填充。

看看下图会清晰一点

texture parameter.png

参数可以设置的值看下表

texture parameter value.png

参数默认值看下表

texture parameter default value.png

我对这几个参数的理解也不是特别清晰,读者可参考其它资料,有懂的可以留言分享一下

  • gl.texImage2D(target, level, internalformat, format, type, image)

把图片拷贝到 GPU 中并和 texture object 绑定在一起
level:0 (设置为 0 ,本书不讲解这个参数)
internalformat: 指定图片的格式
format : 图片格式,跟 internalformat 值相同
type : 指定纹理的数据类型

图片格式可以取以下值:
gl.RGB
gl.RGBA
gl.ALPHA
gl.LUMINANCE
gl.LUMINANCE_ALPHA

图片数据类型 type 可以取以下值:
gl.UNSIGNED_BYTE (Each color component has 1 byte)
gl.UNSIGNED_SHORT_5_6_5 (RGB: Each component has 5, 6, and 5 bits)
gl.UNSIGNED_SHORT_4_4_4_4 (RGBA: Each component has 4, 4, 4, and 4 bits)
gl.UNSIGNED_SHORT_5_5_5_1 (RGBA: Each RGB component has 5 bits, and A has 1 bit)

  • gl.uniform1i(u_Sampler, 0)

把 0 赋值给 u_Sampler, 因为我们用了 TEXTURE0
fragment shader 中 sampler2D 其实是一个整型

  • vec4 texture2D(sampler2D sampler, vec2 coord)

fragment shader 中的 texture2D 是一个内置函数, 用纹理坐标 coord 从图片 sampler 中采样,返回的是采样的颜色

完整代码

// vertex shader
var VERTEX_SHADER_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
var FRAGMENT_SHADER_SOURCE =
    'precision mediump float;\n' +
    'varying vec2 v_TexCoord;\n' +
    'uniform sampler2D u_Sampler;\n' +

    'void main() {\n' +
    '   gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
    '}\n';

var canvas = document.getElementById("canvas");
var gl = canvas.getContext('webgl');
gl.clearColor(0.0, 0.0, 0.0, 1.0);

if (!initShaders(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)) {
    alert('Failed to init shaders');
}

var vertices = new Float32Array([
    -0.5, 0.5, 0.0, 1.0, // 前 2 位是位置坐标, 后 2 位是纹理坐标
    -0.5, -0.5, 0.0, 0.0,
    0.5, 0.5, 1.0, 1.0,
    0.5, -0.5, 1.0, 0.0
]);

initVertexBuffers(gl, vertices);

var image = new Image();
image.onload = function () {
    loadTexture(image);
    draw();
};
image.src = 'images/sky.jpg';

function initVertexBuffers(gl, vertices) {
    var vertexBuffer = gl.createBuffer();
    if (!vertexBuffer) {
        console.log('Failed to create buffer object');
        return -1;
    }

    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    var FSIZE = vertices.BYTES_PER_ELEMENT;

    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 4 * FSIZE, 0);
    gl.enableVertexAttribArray(a_Position);

    var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
    gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 4 * FSIZE, 2 * FSIZE);
    gl.enableVertexAttribArray(a_TexCoord);
}

function loadTexture(image) {
    var texture = gl.createTexture();
    // 翻转 Y 轴
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    gl.activeTexture(gl.TEXTURE0);
    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);

    var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
    gl.uniform1i(u_Sampler, 0);
}

function draw() {
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}

查看源码

相关文章

  • webgl 12.Texture (纹理)

    在 fragment shader 中计算颜色虽然灵活但不好实现复杂的效果。更常用的方法从一张图片采样得到颜色,把...

  • webgl 纹理

    前言前面了解了彩色图形的绘制,虽然这种功能很强大,但是对于一些更加复杂的情况下任然不能胜任.比如绘制一只动画,或者...

  • webgl 纹理

    对于绘制多个点,并且多个点的颜色需要不一样。单个attribute是不能够完成的,这时候需要多个attribute...

  • WebGL和OpenGL的差异 - UNPACK_PREMULT

    WebGL可以使用UNPACK_PREMULTIPLY_ALPHA_WEBGL功能,向GPU传输纹理数据时自动给R...

  • WebGL2系列之不可变纹理

    纹理背景知识 在WebGL1中,纹理包括2D纹理和立方体纹理,在实际的使用中,如果纹理的图片是宽和高是2的幂,可以...

  • WebGL 纹理压缩 crunch

    1、WebGL支持将压缩过的纹理传入到显卡,显卡自带解压功能,这就减少了传输数据量,加快纹理传输到显卡; 2、该功...

  • WebGL 纹理颜色原理

    本文由云+社区发表 作者:ivweb qcyhust 导语 WebGL绘制图像时,往着色器中传入颜色信息就可以给图...

  • WebGL编程指南笔记三 第五章 颜色和纹理

    参考【《WebGL编程指南》读书笔记-颜色与纹理】[https://blog.csdn.net/floating_...

  • WebGL2系列之采样器对象

    前言 在WebGL1中,纹理的图片和采样信息都是写在纹理对象之中. 采样信息告诉GPU如何去读取贴图上图片的信息。...

  • 深度纹理生成及可视化渲染(three.js webgl_dept

    three.js中的webgl_depth_texture例子首先将场景渲染到深度纹理(depth texture...

网友评论

      本文标题:webgl 12.Texture (纹理)

      本文链接:https://www.haomeiwen.com/subject/lhradxtx.html