6、webgl 基本概念 + 四边形纹理

基本概念

片元着色器执行过程

顶点坐标 -》 图形装配 -》 光栅化 -》执行片元着色器

光栅概念

图形装配过程 :这一步的任务是,将孤立的顶点坐标装配成几何图形,几何图形的类别由 gl.drawArrays() 函数的第一个参数决定。
光栅化过程:这一步的任务是,将装配好的几何图形转换为片元。

备注: gl_Position 实际上是 几何图形装配(geometric shape assembly) 阶段的输入数据。注意,几何图形装配过程又被称为图元装配过程(primitive assembly process) ,因为被装配出的基本图形(点、线、面)又被称为 图元(primitive)

纹理映射

可以理解为皮肤,我们可以给绘制的图形贴上一张图片。
纹理映射就是为图像添加上皮肤,将一张图片/图像映射到一个几何图形上面去,即将一张真实世界的图片贴到一个由两个三角形组成的矩形上,这样矩形表面看上去就是这样图片,这张图片也称为纹理图像或纹理。

作用 : 根据纹理图像,为之前光栅化的每个片元涂上颜色。

组成纹理图像的像素又被称为纹素。

纹理映射过程

1、准备纹理图形 ---》 new Image();

2、配置纹理映射方式 ---》 将纹理图像如何放置在图形上;

3、加载纹理图像,并且对其配置,以在 webgl 中使用;

4、在片元着色器中将相应的纹素从纹理中取出,并将纹素的颜色赋值给片元

注意:纹理图像的适用需要打开服务器(因为纹理图像在使用的过程中会收到同源策略的影响)

简单的添加纹理程序

1、顶点着色器中接收顶点的纹理坐标,光栅化后传递给片元着色器

2、片元着色器根据片元的纹理坐标,从纹理图像中抽取出纹素颜色,赋给当前片元

3、色值顶点的纹理坐标(initVertexBuffers())

4、准备待加载的纹理图像,令浏览器读取它(initTextures())

5、监听纹理图像的加载事件,一旦加载完成,就在 webgl 系统中使用纹理(loadTexture())

创建纹理

1、顶点着色器中接收顶点的纹理坐标,光栅化后传递给片元着色器

2、片元着色器根据片元的纹理坐标,从纹理图像中取出纹素颜色赋给当前片元

取样器类型:sampler2D,samplerCube

取样器只能是 uniform 变量

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

sampler2D:2d 的图像

samplerCube: 立方体的图像

3、设置顶点的纹理坐标

4、配置和加载纹理

4.1 创建纹理对象--- gl.createTexture() 方法可以创建纹理对象

4.2 删除纹理对象 --- gl.deleteTexture(texture)

5、为 webgl 配置纹理
(1)图片坐标轴旋转(沿 y 轴旋转)
(2)激活纹理单元

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

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

在使用纹理单元之前,还需要调用 gl.activeTexture() 来激活它
(3)绑定纹理对象

gl.bindTexture(gl.TEXTURE_2D, texture)
纹理类型

二维纹理: gl.TEXTURE_2D

立方体纹理: gl.TEXTURE_CUBE_MAP

(4)配置纹理参数

gl.texParameteri(target, pname, param)

target: gl.TEXTURE_2D 或者 gl.TEXTURE_CUBE_MAP

pname: 纹理参数【放大方法(gl.TEXTURE_MAG_FILTER)、缩小方法(gl.TEXTURE_MIN_FILTER)、水平填充方法(gl.TEXTURE_WRAP_S)、垂直填充方法(gl.TEXTURE_WRAP_T)】

param: 纹理参数的值

param 的可选值:

1)可以赋值给【 放大方法(gl.TEXTURE_MAG_FILTER)、缩小方法(gl.TEXTURE_MIN_FILTER)】 的非金字塔纹理类型常量:

gl.NEAREST : 使用原纹理上距离映射后像素(新像素)中心最近的哪个像素的颜色值,作为新像素的值(使用曼哈顿距离)

gl.LINEAR:使用距离新像素中心最近的四个像素的颜色值的加权平均,作为新像素的值

2)可以赋值给【水平填充方法(gl.TEXTURE_WRAP_S)、垂直填充方法(gl.TEXTURE_WRAP_T)】的常量

gl.REPEAT: 平铺式的重复纹理

gl.MIRRORED_REPEAT: 镜像对称式的重复纹理

gl.CLAM_TO_EDGE: 使用纹理图像边缘值
(5)配置纹理图像

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
(6)将纹理单元传递给片元着色器

注意:图片自身的坐标系统跟 webgl 的是不一致的,贴图片的时候需要将坐标位置进行调整

纹理

给四边形进行纹理绘制

图片:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <canvas width="500" height="500" id="myCanvas"></canvas>

    <script type="vertex" id="vertex">
        /*
            a_position: 顶点坐标
            a_TexCoord: 纹理坐标
            screenSize:canvas 画布大小
            v_TexCoord: 传递给片元着色器的纹理坐标
        */
        attribute vec2 a_position;
        attribute vec2 a_TexCoord;
        uniform vec2 screenSize;
        varying vec2 v_TexCoord;
        void main() {
            /*
            坐标转换, 将 canvas 坐标转化为 webgl 的坐标
            */
            float x = a_position.x * 2.0 / screenSize.x - 1.0;
            float y = 1.0 - a_position.y * 2.0 / screenSize.y;
            gl_Position = vec4(x, y, 0, 1);
            gl_PointSize = 10.0;
            /*
            将纹理坐标传递给片元着色器 
            */
            v_TexCoord = a_TexCoord;
        }
    </script>
    <script type="fragment" id="fragment">
        /*
        片元着色器:绘制颜色或者纹理的 ------ 水彩的作用
        */
        precision mediump float;
        varying vec2 v_TexCoord;
        /*
        纹理图像源数据
        */
        uniform sampler2D u_Sampler;
        void main() {
            /*
            texture2D: 将纹理图像上指定的位置(纹理坐标)纹素颜色信息取出
            */
            gl_FragColor = texture2D(u_Sampler, v_TexCoord);
        }
    </script>
    <script>
        let myCanvas = document.getElementById("myCanvas");
        let gl = myCanvas.getContext("webgl");// IE8 之前的浏览器不兼容
        if(!gl) {
            alert("浏览器不支持 webgl!")
        }
        // 创建着色器
        function createShader(gl, type, source) {
            const shader = gl.createShader(type);
            gl.shaderSource(shader, source);
            gl.compileShader(shader);
            let isSuccess = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
            return isSuccess ? shader : console.log(gl.getShaderInfoLog(shader))
        }
        // 获取着色器中的文本内容字符串
        function getInnerText(id) {
            return document.getElementById(id).innerText;
        }
        const vertexStr = getInnerText("vertex");
        const fragmentStr = getInnerText("fragment");
        const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexStr)
        const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentStr);
        // 创建程序
        function createProgram(gl, vertexShader, fragmentShader) {
            let program = gl.createProgram();
            gl.attachShader(program, vertexShader);
            gl.attachShader(program, fragmentShader);
            gl.linkProgram(program);
            let isSuccess = gl.getProgramParameter(program, gl.LINK_STATUS);
            return isSuccess ? program : console.log(gl.getProgramInfoLog(program));
        }

        let program = createProgram(gl, vertexShader, fragmentShader);
        gl.useProgram(program);
        
        // 获取顶点着色器中变量
        let a_position = gl.getAttribLocation(program, 'a_position');
        let screenSize = gl.getUniformLocation(program, 'screenSize');
        let a_TexCoord = gl.getAttribLocation(program, 'a_TexCoord');
        let u_Sampler = gl.getUniformLocation(program, "u_Sampler");

        // 获取画布的宽高,因为顶点着色器中已经
        gl.uniform2f(screenSize, myCanvas.width, myCanvas.height);
        let positionBuffer = gl.createBuffer();
        let indexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

        let img = new Image()
        img.src = './1.jpg';
        let texture = gl.createTexture();
        img.onload = () => {
            // 激活纹理单元
            gl.activeTexture(gl.TEXTURE0);
            // 绑定纹理对象s
            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(u_Sampler, 0);
            draw();
            
        }
        function draw() {
             // 矩形的正面,要逆时针的顶点进行写, 矩形就是两个三角形,也就是需要写 6个 顶点,  gl.ARRAY_BUFFER 这个缓冲区需要把所有的顶点都写上,但其实例如矩形的时候有些顶点是重复的,当面越来越多就会造成严重的性能问题,此时我们可以使用 gl.ELEMENT_ARRAY_BUFFER(也就是顶点索引)缓冲区来进行优化-只需要传不重复的点
            //  图片自身的坐标系统跟 webgl 的是不一致的,贴图片的时候需要将坐标位置进行调整
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
                50, 50, 0, 0, 
                50, 200, 0, 1, 
                250, 200, 1, 1,  
                250, 200, 1, 1,  
                250, 50, 1, 0,
                50, 50, 0, 0,
            ]), gl.STATIC_DRAW);
            // gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([
            //     0, 1, 2,
            //     2, 3, 0
            // ]), gl.STATIC_DRAW);
            gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 4 * 4, 0);
            // 为纹理坐标传递数据
            gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 4 * 4, 4* 2);
            // 启用这个位置数据
            gl.enableVertexAttribArray(a_position);
            gl.enableVertexAttribArray(a_TexCoord);
            // 绘制三角形 drawArrays 直接从数据的缓冲区取数据
            // gl.drawElements(mode, count, type, offset) 
            // mode: 制定绘制图元的类型, gl.POINTS, gl.TRIANGLES, gl.LINES...
            // count: 指定绘制图形的顶点个数
            // type:指定索引缓冲区的值的类型,常用的值有两个 gl.UNSINGED_TYPE(无符号8位整数值) 和 gl.UNSINGED_SHORT(无符号短整型16位)
            // offset:指定索引数组中开始绘制的位置,以字节为单位
            // gl.drawElements(gl.TRIANGLES_FAN, 6, gl.UNSIGNED_SHORT, 0);
            gl.drawArrays(gl.TRIANGLES, 0, 6);
        }
            draw()
    </script>
</body>
</html>
相关推荐
刘一说6 小时前
深入理解 Spring Boot Web 开发中的全局异常统一处理机制
前端·spring boot·后端
啃火龙果的兔子6 小时前
前端导出大量数据到PDF方案
前端·pdf
Lj2_jOker6 小时前
QT 给Qimage数据赋值,显示异常,像素对齐的坑
开发语言·前端·qt
csj507 小时前
前端基础之《React(7)—webpack简介-ESLint集成》
前端·react
Jonathan Star7 小时前
CSS margin 折叠现象的实际代码示例
javascript·css·css3
咚咚咚小柒7 小时前
【前端】Webpack相关(长期更新)
前端·javascript·webpack·前端框架·node.js·vue·scss
2501_916008897 小时前
前端工具全景实战指南,从开发到调试的效率闭环
android·前端·小程序·https·uni-app·iphone·webview
诸葛韩信7 小时前
Webpack与Vite的常用配置及主要差异分析
前端·webpack·node.js
IT_陈寒7 小时前
Vite 5震撼发布!10个新特性让你的开发效率飙升200% 🚀
前端·人工智能·后端