阅读 196

WebGL中的纹理映射之入门篇

概念

纹理映射能为图像添加上皮肤,将一张图片或者图像映射到几何图形上,其作用是根据纹理图像,为之前光栅化的每个片元涂上颜色 (组成纹理图像的像素又被称为纹素)

纹理坐标系(左) 与 图片坐标系(右)

在纹理坐标系中,向上为y轴的正方向,图片坐标系则相反

截屏2021-07-12 下午7.49.12.png

纹理坐标系是理想状态,实际渲染纹理时 webgl 使用的图片坐标系。因此在向缓冲区对象中传递几何图形的点位和纹理图像的点位时,应该使用图片坐标系作为参考。

取样器类型

取样器只能是 uniform 变量,唯一能给取样器变量赋值的是纹理单元编号,必须使用 gl.uniform1i()赋值

  • sampler2D - 2维平面取样器
  • samplerCube - 3维取样器
    /* fragmentShader */
    precision mediump float;
    uniform sampler2D u_sampler;
    varying vec2 v_textureCoord;
    void main() {
        gl_FragColor = texture2D(u_sampler, v_textureCoord);
    }
复制代码

纹理单元

webgl通过一种叫纹理单元的机制来同时使用多个纹理,每个纹理单元有一个单元编号来管理一张纹理图像。即使当前程序只需要一个纹理,也需要为其指定一个纹理单元。

系统支持的纹理单元数取决于硬件和浏览器的webgl实现,默认情况下webgl支持8个纹理单元,一些其他系统支持的更多。内置变量 gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2..., gl.TEXTURE7各表示一个纹理单元

纹理类型

  • gl.TEXTURE_2D (二维纹理)
  • gl.TEXTURE_CUBE_MAP (立方体纹理)

纹理数据的数据格式

格式描述
gl.UNSIGNED_BYTE无符号整型,每个颜色分量占1字节
gl.UNSIGNED_SHORT_5_6_5RGB:每个分量分别占 5,6,5 比特
gl.UNSIGNED_SHORT_4_4_4_4RGBA:每个分量分别占 4,4,4,4 比特
gl.UNSIGNED_SHORT_5_5_5_1RGBA:每个分量分别占 5,5,5,1 比特

webgl API

名称参数描述
createTexture创建纹理对象
activeTexturetexUnit - 指定纹理单元激活纹理单元
bindTexturetarget - 纹理类型
texture - 绑定的纹理单元
绑定纹理对象
texParameteritarget
pname
param
配置纹理图像
texImage2Dtarget
level
internalformat
format
type
image
将纹理图像分配给纹理对象

实现纹理映射的大致过程

  • 准备纹理图像 - new Image()
  • 配置纹理映射方式 - 将纹理图像如何放置在图形上
  • 加载纹理图像,并对其配置,以在webgl中使用
  • 在片元着色器中将相应的纹素从纹理中取出,并将纹素的颜色赋给片元

纹理映射实现

效果

首先实现一个矩形

截屏2021-07-12 下午8.59.57.png

添加纹理映射

截屏2021-07-12 下午8.59.57.png

shader & js Code

/*
    顶点着色器
    a_pointPosition: 顶点坐标
    a_textureCoord: 纹理坐标
    u_canvasSize: canvas画布大小
    v_textureCoord: 传递给片元着色器的纹理坐标
*/
attribute vec2 a_pointPosition;
attribute vec2 a_textureCoord;
uniform vec2 u_canvasSize;
varying vec2 v_textureCoord;
void main() {
    float x = a_pointPosition.x;
    float y = a_pointPosition.y;

    x = x / u_canvasSize.x * 2.0 - 1.0;
    y = 1.0 - y / u_canvasSize.y * 2.0;

    gl_PointSize = 10.0;
    gl_Position = vec4(x, y, 0.0, 1.0);

    v_textureCoord = a_textureCoord;
}
复制代码
/*
    片元着色器
    u_sampler: 取样器
    v_textureCoord: 纹理坐标
*/
precision mediump float;
uniform sampler2D u_sampler;
varying vec2 v_textureCoord;
void main() {
    gl_FragColor = texture2D(u_sampler, v_textureCoord);
}
复制代码
// js主要逻辑
import WebGLProgram from '../gl/base.js'
import defineVertexShaderSource from './shader/vertexShader.js';
import defineFragmentShaderSource from './shader/fragmentShader.js';

import { getRandomColor } from '../utils/index.js'

const glProgram = new WebGLProgram({
    id: 'root',
    defineVertexShaderSource,
    defineFragmentShaderSource
});
const { gl, program } = glProgram;

const root = document.getElementById('root');

// 获取glsl变量的内存地址
const positionLocation = gl.getAttribLocation(program, 'a_pointPosition');
const textureCoordLocation = gl.getAttribLocation(program, 'a_textureCoord')
const sizeLocation = gl.getUniformLocation(program, 'u_canvasSize');
const samplerLocation = gl.getUniformLocation(program, 'u_sampler');

// 将 canvas 画布大小传入到 顶点着色器中
gl.uniform2f(sizeLocation, root.width, root.height);

// 创建缓冲区对象
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
// 创建缓冲区索引对象
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

const img = new Image();
img.src = './image/01.jpeg';

img.onload = function () {
    // 创建纹理对象
    const texture = gl.createTexture();
    // 激活纹理单元
    gl.activeTexture(gl.TEXTURE0);
    // 将纹理对象绑定到 webgl 系统中
    gl.bindTexture(gl.TEXTURE_2D, texture);
    // 配置纹理图像 映射到 图形上的(放大,缩小,拉伸)方式
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
    // 向片元着色器传递纹理单元的编号
    gl.uniform1i(samplerLocation, 0);

    render();
}

// 渲染函数
function render() {
    // 向顶点缓冲区填充数据
    gl.bufferData(
        gl.ARRAY_BUFFER,
        new Float32Array([
            50, 50, 0, 0,
            50, 300, 0, 1,
            400, 300, 1, 1,
            400, 50, 1, 0,
        ]),
        gl.STATIC_DRAW
    );

    // 向索引缓冲区填充数据
    gl.bufferData(
        gl.ELEMENT_ARRAY_BUFFER,
        new Uint16Array([
            0, 1, 3, 2
        ]),
        gl.STATIC_DRAW
    )
    // 指定缓冲区对象中 数据 与 顶点变量 的映射关系
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 4 * 4, 0);
    // 指定缓冲区对象中 数据 与 纹理变量 的映射关系
    gl.vertexAttribPointer(textureCoordLocation, 2, gl.FLOAT, false, 4 * 4, 4 * 2);
    // 启用变量
    gl.enableVertexAttribArray(positionLocation);
    gl.enableVertexAttribArray(textureCoordLocation);
    // 用三角带扇绘制矩形
    gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, 0);
}
复制代码
文章分类
前端
文章标签