9、webgl 基本概念 + 复合变换 + 平面内容复习

基本概念

先平移后旋转

先平移 ---》得到平移后的坐标(通过矩阵乘积)

再旋转 ---》旋转矩阵 * 平移后的坐标

变换矩阵------旋转

左边矩阵的列数要是右边矩阵的行数才可以相乘

绕着 Z 轴旋转

绕着 X 轴旋转

绕着 Y 轴旋转

复合变换

对四边形进行平移+旋转+缩放操作

需要将矩阵传入顶点着色器中,然后在顶点着色器中进行位置计算

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">
        attribute vec4 a_position;
        attribute vec4 a_color;
        uniform mat4 a_Translate;
        uniform mat4 rotateMatrix;
        uniform mat4 scaleMatrix;
        varying vec4 v_color;
        void main() {
            // 先平移,再旋转,最后缩放
            gl_Position = a_Translate * rotateMatrix * scaleMatrix * a_position;
            gl_PointSize = 10.0;
            v_color = a_color;
        }
    </script>
    <script type="fragment" id="fragment">
        precision mediump float;
        varying vec4 v_color;
        void main() {
            gl_FragColor = v_color;
        }
    </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_Translate = gl.getUniformLocation(program, 'a_Translate');
        let rotateMatrix = gl.getUniformLocation(program, 'rotateMatrix');
        let scaleMatrix = gl.getUniformLocation(program, 'scaleMatrix');
        let a_color = gl.getAttribLocation(program, 'a_color');
        let deg = 45;
        

        let Tx = 0.5, Ty = -0.5, Tz = 0;// 平移
        let Sx = 0.5, Sy = 0.5, Sz = 0; // 缩放
         gl.uniformMatrix4fv(a_Translate, false, new Float32Array([
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            Tx, Ty, Tz, 1
        ]));

        
        
        gl.uniformMatrix4fv(scaleMatrix, false, new Float32Array([
            Sx, 0, 0, 0,
            0, Sy, 0, 0,
            0, 0, Sz, 0,
            0, 0, 0, 1
        ]));
        let positionBuffer = gl.createBuffer();
        let indexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

        function rotate() {
            deg += 5;
            let cosb = Math.cos(deg * Math.PI /180);
            let sinb = Math.sin(deg * Math.PI /180);
            // 四个分量的时候矩阵传值
            gl.uniformMatrix4fv(rotateMatrix, false, new Float32Array([
                cosb, sinb, 0, 0,
                -sinb, cosb, 0, 0,
                0, 0, 1, 0,
                0, 0, 0, 1
            ]));
            draw()
        }
        rotate()
        setInterval(rotate, 30)
        function draw() {
             // 矩形的正面,要逆时针的顶点进行写, 矩形就是两个三角形,也就是需要写 6个 顶点,  gl.ARRAY_BUFFER 这个缓冲区需要把所有的顶点都写上,但其实例如矩形的时候有些顶点是重复的,当面越来越多就会造成严重的性能问题,此时我们可以使用 gl.ELEMENT_ARRAY_BUFFER(也就是顶点索引)缓冲区来进行优化-只需要传不重复的点
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
                0, 0, 0, 0, 1, 0, 
                -0.5, 0.5, 0, 1, 0, 0, 
                0.5, 0.5, 0, 0, 1, 0, 
                0.5, -0.5, 0, 0, 0, 1, 
                -0.5, -0.5, 0, 1, 1, 0,
                -0.5, 0.5, 1, 0, 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, 3, gl.FLOAT, false, 4 * 6, 0);
            gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 6, 4* 3);
            // 启用这个位置数据
            gl.enableVertexAttribArray(a_position);
            gl.enableVertexAttribArray(a_color);
            // 绘制三角形 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.TRIANGLE_FAN, 0, 6);
        }
      
    </script>
</body>
</html>

使用模型矩阵来实现复合变换

将 平移、旋转、缩放 的计算放到外部

传递到顶点着色器的矩阵应该是 (旋转 * 平移)的转置= 平移 的转置 * 旋转的转置

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">
        attribute vec4 a_position;
        attribute vec4 a_color;
        uniform mat4 moduleMatrix;
        varying vec4 v_color;
        void main() {
            // 先平移,再旋转,最后缩放
            gl_Position = moduleMatrix * a_position;
            gl_PointSize = 10.0;
            v_color = a_color;
        }
    </script>
    <script type="fragment" id="fragment">
        precision mediump float;
        varying vec4 v_color;
        void main() {
            gl_FragColor = v_color;
        }
    </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 moduleMatrix = gl.getUniformLocation(program, 'moduleMatrix');
        let a_color = gl.getAttribLocation(program, 'a_color');
        let deg = 35;
        

        let Tx = 0.5, Ty = -0.5, Tz = 0;// 平移
        let Sx = 0.5, Sy = 0.5, Sz = 0; // 缩放
        
        let matrix = {
            tranlate: (Tx, Ty, Tz) => {
                return [
                    1, 0, 0, 0,
                    0, 1, 0, 0,
                    0, 0, 1, 0,
                    Tx, Ty, Tz, 1
                ]
            },
            rotateZ: (deg) => {
                 let cosb = Math.cos(deg * Math.PI /180);
                 let sinb = Math.sin(deg * Math.PI /180);
                return [
                    cosb, sinb, 0, 0,
                    -sinb, cosb, 0, 0,
                    0, 0, 1, 0,
                    0, 0, 0, 1
                ]
            },
            mutiply: (a, b) => {
                let a00 = a[0 * 4 + 0]
                let a01 = a[0 * 4 + 1]
                let a02 = a[0 * 4 + 2]
                let a03 = a[0 * 4 + 3]

                let a10 = a[1 * 4 + 0]
                let a11 = a[1 * 4 + 1]
                let a12 = a[1 * 4 + 2]
                let a13 = a[1 * 4 + 3]

                let a20 = a[2 * 4 + 0]
                let a21 = a[2 * 4 + 1]
                let a22 = a[2 * 4 + 2]
                let a23 = a[2 * 4 + 3]

                let a30 = a[3 * 4 + 0]
                let a31 = a[3 * 4 + 1]
                let a32 = a[3 * 4 + 2]
                let a33 = a[3 * 4 + 3]

                let b00 = a[0 * 4 + 0]
                let b01 = a[0 * 4 + 1]
                let b02 = a[0 * 4 + 2]
                let b03 = a[0 * 4 + 3]

                let b10 = a[1 * 4 + 0]
                let b11 = a[1 * 4 + 1]
                let b12 = a[1 * 4 + 2]
                let b13 = a[1 * 4 + 3]

                let b20 = a[2 * 4 + 0]
                let b21 = a[2 * 4 + 1]
                let b22 = a[2 * 4 + 2]
                let b23 = a[2 * 4 + 3]
                
                let b30 = a[3 * 4 + 0]
                let b31 = a[3 * 4 + 1]
                let b32 = a[3 * 4 + 2]
                let b33 = a[3 * 4 + 3]

                return [
                    a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30,
                    a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31,
                    a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32,
                    a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33,

                    a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30,
                    a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31,
                    a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32,
                    a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33,

                    a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30,
                    a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31,
                    a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32,
                    a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33,

                    a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30,
                    a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31,
                    a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32,
                    a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33,
                    
                ]

            }

        }

        // 传入的是转置
        gl.uniformMatrix4fv(moduleMatrix, false, new Float32Array(matrix.mutiply(matrix.rotateZ(deg), matrix.tranlate(Tx, Ty, Tz))));

        let positionBuffer = gl.createBuffer();
        let indexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

        draw()
        function draw() {
             // 矩形的正面,要逆时针的顶点进行写, 矩形就是两个三角形,也就是需要写 6个 顶点,  gl.ARRAY_BUFFER 这个缓冲区需要把所有的顶点都写上,但其实例如矩形的时候有些顶点是重复的,当面越来越多就会造成严重的性能问题,此时我们可以使用 gl.ELEMENT_ARRAY_BUFFER(也就是顶点索引)缓冲区来进行优化-只需要传不重复的点
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
                0, 0, 0, 0, 1, 0, 
                -0.5, 0.5, 0, 1, 0, 0, 
                0.5, 0.5, 0, 0, 1, 0, 
                0.5, -0.5, 0, 0, 0, 1, 
                -0.5, -0.5, 0, 1, 1, 0,
                -0.5, 0.5, 1, 0, 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, 3, gl.FLOAT, false, 4 * 6, 0);
            gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 6, 4* 3);
            // 启用这个位置数据
            gl.enableVertexAttribArray(a_position);
            gl.enableVertexAttribArray(a_color);
            // 绘制三角形 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.TRIANGLE_FAN, 0, 6);
        }
      
    </script>
</body>
</html>

平面内容复习

html 复制代码
1、GLSL :着色器语言
(1)变量声明:
	1)存储变量类型(修饰符):
		attribute(属性修饰符)
		uniform(全局变量)所有顶点都用到这个
		varying (可变变量修饰符)---》顶点着色器和片元着色器共用的变量
	2)数据类型:
		vec2 ---> 两个分量的浮点型数据
		vec3 ---> 三个分量
		vec4 ---> 四个分量,第四位代表齐次坐标,前面的 数不能比最后一位大,会将前面的点除以最后一位
	3)precision 声明精准度
		highp:高精准度
		mediump:中精准度
		lowp: 低精准度
(2)顶点着色器中的内置变量
	gl_Position : 必须赋值,四个分量进行赋值
	gl_PointSize : 默认是1.0 ,点的大小

(3)片元着色器中的内置变量
	gl_fragColor: 片元颜色

(4)坐标转换
	通过 canvas 的坐标计算 webgl 的坐标
(5)坐标轴
	webg坐标系 原点在canvas 的中心点 水平方向(从左到右)-1 --- 1 竖直方向(从下到上) -1 ---- 1 垂直于页面向里的为 z 轴
	canvas坐标系 原点在 canvas的左上角 水平向右为 x 正轴 竖直向下为 y 轴正轴 垂直页面向上为 z轴
	纹理坐标系 原点在图片的左下角 水平向右为正轴 图片宽度为1 竖直向上为 y 轴 图片高度为1
(6)Webgl api
	着色器 shader:
		gl.createShader(type); 创建着色器对象, type 是着色器类型, type: gl.VERTEX_SHADER gl.FRAGMENT_SHADER
		gl.shaderSource(shader, source); 为着色器添加数据源 shader 是着色器对象, source 是数据源即我们写的着色器代码
      	gl.compileShader(shader);编译着色器,将字符串编译成着色器语言
      	gl.getShaderParameter(shader, gl.COMPILE_STATUS) 返回着色器编译的成功状态 
      	gl.getShaderInfoLog(shader) 打印着色器编译失败的日志
      	
    创建程序 program:
    		gl.createProgram(); 创建程序
	 		gl.attachShader(program, shader); 添加着色器 program 程序,shader要往程序里面添加着色器对象
	 		gl.linkProgram(program)连接着色器
	 		gl.useProgram(program); 使用程序
	 		
    attribute 属性:
    		gl.getAttribLocation(program, 'a_position') 获取attribute 变量位置
    		gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 4 * 6, 0); 为变量添加数据
    	
    uniform 属性:
    	 gl.getUniformLocation(program, 'screenSize');
    	 gl.uniformMatrix4fv(moduleMatrix, false, new Float32Array(matrix.mutiply(matrix
    		
    缓冲区对象:
    		let positionBuffer = gl.createBuffer(); 创建缓冲区对象
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
                0, 0, 0, 0, 1, 0, 
                -0.5, 0.5, 0, 1, 0, 0, 
                0.5, 0.5, 0, 0, 1, 0, 
                0.5, -0.5, 0, 0, 0, 1, 
                -0.5, -0.5, 0, 1, 1, 0,
                -0.5, 0.5, 1, 0, 0, 0
            ]), gl.STATIC_DRAW);
        gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 4 * 6, 0);
        gl.enableVertexAttribArray(a_position); 启用这个位置数据
        
    绘制:
     			gl.drawElements(gl.TRIANGLES_FAN, 6, gl.UNSIGNED_SHORT, 0);
            gl.drawArrays(gl.TRIANGLE_FAN, 0, 6);
            
    图元:
    gl.POINTS
    gl.LINES  LINES_STRIP LINES_LOOP
    gl.TRANGLES TRANGLE_FAN TRANGLE_STRIP
    
    纹理:
	    gl.createTexture(); 创建纹理对象
	    gl.pixelStorei(gl.UNPACK_FILP_Y_WEBGL, 1)转换坐标系
	    gl.activeTexture(gl.TEXTURE0);激活纹理单元
	    gl.bindTexture(gl.TEXTURE_2D, texture); 绑定纹理对象
	    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) 设置纹理参数
	    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); 配置纹理图像
		gl.uniform1i(location, 0); 传递纹理数据, 传递纹理单元索引
相关推荐
梦幻通灵2 小时前
Edge浏览器好用插件【持续更新】
前端·edge
sTone873752 小时前
Chrome devtools二次开发准备:获取源码和编译
前端·google
龙泉寺天下行走2 小时前
[Powershell入门教程]第4天:模块、脚本编写、错误处理与 .NET 集成
java·服务器·前端
晴天丨2 小时前
Vite:下一代前端构建工具深度解析与实践指南
前端
多来哈米2 小时前
Jenkins配置vue前端项目(最简单的操作)
运维·前端·jenkins
一只叁木Meow2 小时前
Vue scoped CSS 与 Element Plus Drawer 样式失效问题深度解析
前端
用户92426257007312 小时前
Vue 学习笔记:组件通信(Props / 自定义事件)与插槽(Slot)全解析
前端
Zyx20072 小时前
JavaScript 数组:从内存布局到遍历策略的深度解析
javascript
UIUV2 小时前
Ajax 数据请求学习笔记
前端·javascript·代码规范