WebGL学习【焕新计划】

WebGL基础

在正式进入webgl之前,我想有必要简单了解一下渲染管线,毕竟它贯穿webgl学习的整个过程。

渲染管线流程图:

webgl着色器简单语法:

在GLSL(OpenGL Shading Language)中,常见的变量类型可以归纳为以下几种,并根据提供的参考文章进行详细的解释:

  1. 基本数据类型
    • 整型(int):用于存放自然数的变量。
    • 浮点型(float):用于存放小数点数值的变量。
    • 布尔型(bool):用于存放0值或非0值,0代表false,1代表true。
  2. 向量(vec)
    • GLSL中的向量类型可以是2维(vec2)、3维(vec3)或4维(vec4)。
    • 向量中的数据类型可以是整数、浮点数或布尔值。
    • 向量在GLSL中常用于描述颜色、坐标和纹理等。
  3. GLSL内置变量
    • 顶点属性(attribute):指顶点的信息,如顶点坐标、纹理坐标、颜色等。GLSL会为这些传统的顶点属性设置内置变量与之对应,以便在需要时可以在顶点或片元着色器中直接引用。
    • 一致变量(uniform):是外部应用程序传递给(顶点和片元)着色器的变量。它在着色器程序内部是不可修改的,通常用于表示变换矩阵、材质、光照参数和颜色等信息。
    • 易变变量(varying):由顶点程序向片元程序插值传递的变量。这些变量在顶点着色器中定义,并在片元着色器中使用,用于传递从顶点计算得到的插值数据。
    • 其他内置变量:如gl_ModelViewMatrix(模型视图变换矩阵)、gl_ProjectMatrix(投影矩阵)等,这些变量描述了OpenGL的状态,并在着色器之间共享。
  4. 特殊变量
    • gl_Position:在顶点着色器中用于写顶点位置,其类型是highp vec4
    • gl_FragColor:在片元着色器中用于写片元颜色,其类型是mediump vec4
    • gl_PointSize:在顶点着色器中用于写光栅化后的点大小,其类型是mediump float

向量与矩阵

常见内置变量

常见数据类型

常见修饰符

vec3

gl_PointSize

float

attribute

vec4

gl_Position

int

uniform

mat3

gl_FragColor

bool

varying

mat4

gl_FragCoord

sampler2D

gl_ModelViewMatrix
gl_ProjectMatrix

说明:

  • in和out

   glsl版本不同,attribute和uniform在glsl 新本本对应着inout是两种特殊的变量限定符,它们用于在着色器阶段之间传递数据。

  • in限定符
  • 用途in用于在着色器阶段之间输入数据。在顶点着色器中,in变量通常表示从CPU传入的顶点数据,如顶点位置、纹理坐标等。在片元着色器中,in变量表示从顶点着色器插值传递过来的数据。

  • 示例

// 顶点着色器示例  
#version 330 core  
layout(location = 0) in vec3 aPos;    // aPos是一个in变量,表示顶点位置  
out vec4 vertexColor;                 // vertexColor是一个out变量,用于将颜色数据传递给片元着色器  
  
void main() {  
    gl_Position = vec4(aPos, 1.0);    // 设置顶点位置  
    vertexColor = vec4(1.0, 0.0, 0.0, 1.0); // 设置顶点颜色为红色  
}
  •  out限定符
  • 用途out用于在着色器阶段之间输出数据。在顶点着色器中,out变量用于将计算得到的数据传递给下一个着色器阶段(通常是片元着色器)。在片元着色器中,out变量表示最终的像素颜色或其他输出数据。

  • 示例

// 片元着色器示例  
#version 330 core  
in vec4 vertexColor;    // vertexColor是一个in变量,从顶点着色器接收颜色数据  
out vec4 FragColor;     // FragColor是一个out变量,表示最终的像素颜色  
  
void main() {  
    FragColor = vertexColor; // 直接将顶点着色器传递过来的颜色作为最终像素颜色  
}
  • 坐标变换:【3D基础】坐标转换——地理坐标投影到平面_将世界地理坐标投影到平面的方法-CSDN博客
  • Three.js和Cesium.js中坐标_threejs 物体缩放后需要更新世界坐标吗-CSDN博客
  • 最后在正式开始前,先大致了解整个流程:
  • 整个流程:

 

 

 (注:上述内容为搭建一个框架,此外课程学习前可能需一些计算机图形学和线性代数的基本知识,若还未学习相关内容的小伙伴可以去B站学习相关内容,推荐《计算机图形学入门》和《线性代数的本质》两门课程,当然学习webgl还是很推荐郭老师的课程哈!课程+博客!)

了解大致流程后,开始正式webgl入门课程的学习:

1.绘制一个点

1.1实现效果:

1.2实现代码:
<!DOCTYPE html>
 <html lang="en">
  <head>
      <meta charset="UTF-8">
      <title>使用WebGL绘制一个点</title>
  </head>
  <body>
  <!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布-->
  <canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas>
<!-- 写法一(推荐) -->
<!-- 顶点着色器源码 -->
<script id="vertexShader" type="x-shader/x-vertex">
    void main() {
      //给内置变量gl_PointSize赋值像素大小
      gl_PointSize=20.0;
      //顶点位置,位于坐标原点
      gl_Position =vec4(0.0,0.0,0.0,1.0);
    }
  </script>
  <!-- 片元着色器源码 -->
  <script id="fragmentShader" type="x-shader/x-fragment">
    void main() {
      gl_FragColor = vec4(1.0,0.0,0.0,1.0);
    }
  </script>

  <!-- 写法二(仅大致了解) -->
  <!-- //顶点着色器源码
  var vertexShaderSource = '' +
      'void main(){' +
      //给内置变量gl_PointSize赋值像素大小
      '   gl_PointSize=10.0;' +
      //顶点位置,位于坐标原点
      '   gl_Position =vec4(0.0,0.0,0.0,1.0);' +
      '}';
  //片元着色器源码
  var fragShaderSource = '' +
      'void main(){' +
      //定义片元颜色
      '   gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
      '}'; -->

  <script>
      //通过getElementById()方法获取canvas画布
      var canvas=document.getElementById('webgl');
      //通过方法getContext()获取WebGL上下文
      var gl=canvas.getContext('webgl');
      //顶点着色器源码
      var vertexShaderSource = document.getElementById( 'vertexShader' ).innerText;
      
      //片元着色器源码
      var fragShaderSource = document.getElementById( 'fragmentShader' ).innerText;

      //初始化着色器
      var program = initShader(gl,vertexShaderSource,fragShaderSource);
      
      //开始绘制,显示器显示结果
      gl.drawArrays(gl.POINTS,0,1);
  

      //声明初始化着色器函数
      function initShader(gl,vertexShaderSource,fragmentShaderSource){
          //创建顶点着色器对象
          var vertexShader = gl.createShader(gl.VERTEX_SHADER);
          //创建片元着色器对象
          var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
          //引入顶点、片元着色器源代码
          gl.shaderSource(vertexShader,vertexShaderSource);
          gl.shaderSource(fragmentShader,fragmentShaderSource);
          //编译顶点、片元着色器
          gl.compileShader(vertexShader);
          gl.compileShader(fragmentShader);
  
          //创建程序对象program
          var program = gl.createProgram();
          //附着顶点着色器和片元着色器到program
          gl.attachShader(program,vertexShader);
          gl.attachShader(program,fragmentShader);
          //链接program
          gl.linkProgram(program);
          //使用program
          gl.useProgram(program);
          //返回程序program对象
          return program;
      }
  </script>
  </body>
  </html>

1.3解析:刚入门第一个案例较为简单,通过本案例了解代码编写整个流程即可

2. 绘制一个矩形

2.1实现效果:

2.2实现代码:
<!DOCTYPE html>
 <html lang="en">
  <head>
      <meta charset="UTF-8">
      <title>使用WebGL绘制一个矩形</title>
  </head>
  <body>
  <!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布-->
  <canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas>
  
  <!-- 顶点着色器源码 -->
<script id="vertexShader" type="x-shader/x-vertex">
    //非单点,用类型化数组  
    //attribute声明vec4类型变量apos 
    attribute vec4 apos;
    void main() {
    //顶点坐标apos赋值给内置变量gl_Position
    //逐顶点处理数据
        gl_Position = apos;
    }
  </script>
  <!-- 片元着色器源码 -->
  <script id="fragmentShader" type="x-shader/x-fragment">
    void main() {
      gl_FragColor = vec4(1.0,0.0,0.0,1.0);
    }
  </script>
  <script>
      //通过getElementById()方法获取canvas画布
      var canvas=document.getElementById('webgl');
      //通过方法getContext()获取WebGL上下文
      var gl=canvas.getContext('webgl');
       //顶点着色器源码
       var vertexShaderSource = document.getElementById( 'vertexShader' ).innerText;
      //片元着色器源码
      var fragShaderSource = document.getElementById( 'fragmentShader' ).innerText;
      //初始化着色器
      var program = initShader(gl,vertexShaderSource,fragShaderSource);
      //获取顶点着色器的位置变量ap os,即aposLocation指向apos变量。
      var aposLocation = gl.getAttribLocation(program,'apos');
   

      //类型数组构造函数Float32Array创建顶点数组
      var data=new Float32Array([0.5,0.5,-0.5,0.5,-0.5,-0.5,0.5,-0.5]);
        //创建缓冲区对象
        var buffer=gl.createBuffer();
        //绑定缓冲区对象,激活buffer
        // gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
        gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
        //顶点数组data数据传入缓冲区
        gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
        //缓冲区中的数据按照一定的规律传递给位置变量apos
        gl.vertexAttribPointer(aposLocation,2,gl.FLOAT,false,0,0);
        //允许数据传递
        gl.enableVertexAttribArray(aposLocation);

      //开始绘制,显示器显示结果
      gl.drawArrays(gl.LINE_LOOP,0,4);
  
      //声明初始化着色器函数
      function initShader(gl,vertexShaderSource,fragmentShaderSource){
          //创建着色器对象
          var vertexShader = gl.createShader(gl.VERTEX_SHADER);
          var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
          //引入着色器源代码
          gl.shaderSource(vertexShader,vertexShaderSource);
          gl.shaderSource(fragmentShader,fragmentShaderSource);
          //编译着色器
          gl.compileShader(vertexShader);
          gl.compileShader(fragmentShader);
          //创建程序对象program
          var program = gl.createProgram();
          //附着着色器到program
          gl.attachShader(program,vertexShader);
          gl.attachShader(program,fragmentShader);
          //链接program
          gl.linkProgram(program);
          //使用program
          gl.useProgram(program);
          //返回程序program对象
          return program;
      }
  </script>
  </body>
  </html>

2.3解析:本案例,在之前案例的基础上,增加了声明变量和为变量传递数据的过程。在webgl中先需创建一个类型化数组存储“顶点”信息,然后在绘制的过程,指定绘制的类型,这时进行图元装配时就会将点按指定要求绘制成几何体。如本案例就是绘制类型gl.LINE_LOOP(闭合线)就会将点连城闭合线(矩形)

3.绘制三角形|投影(UV坐标)

3.1实现效果

[-1.0-1.0]

3.2实现代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>绘制三角形|投影</title>
</head>

<body>
  <!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布-->
  <canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas>

  <!-- 顶点着色器源码 -->
  <script id="vertexShader" type="x-shader/x-vertex">
    //非单点,用类型化数组  
    //attribute声明vec4类型变量apos 
    attribute vec4 apos;
    void main() {
    //顶点坐标apos赋值给内置变量gl_Position
    //逐顶点处理数据
    gl_Position=apos; 
    }
  </script>
  <!-- 片元着色器源码 -->
  <script id="fragmentShader" type="x-shader/x-fragment">
    void main() {
      gl_FragColor = vec4(1.0,0.0,0.0,1.0);
    }
  </script>
  <script>
    //通过getElementById()方法获取canvas画布
    var canvas = document.getElementById('webgl');
    //通过方法getContext()获取WebGL上下文
    var gl = canvas.getContext('webgl');
    //顶点着色器源码
    var vertexShaderSource = document.getElementById('vertexShader').innerText;
    //片元着色器源码
    var fragShaderSource = document.getElementById('fragmentShader').innerText;
    //初始化着色器
    var program = initShader(gl, vertexShaderSource, fragShaderSource);
    //获取顶点着色器的位置变量apos,即aposLocation指向apos变量。
    var aposLocation = gl.getAttribLocation(program, 'apos');


    //类型数组构造函数Float32Array创建顶点数组
    var data = new Float32Array([
      // 将对z轴进行投影,投影为二维平面,所以第一个顶点的1.0,可以取值[-1.0,1.0],效果都一样,若超过1.0或少于-1.0,测试不难发现会有部分被裁掉,因为在webgl中坐标为[-1.0,1.0]
      0.0, 0.0, 2.0, //三角形顶点1坐标
      0.0, 1.0, 0.0, //三角形顶点2坐标
      1.0, 0.0, 0.0 //三角形顶点3坐标
    ]);

    //创建缓冲区对象
    var buffer = gl.createBuffer();
    //绑定缓冲区对象,激活buffer
    // gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    //顶点数组data数据传入缓冲区
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    //缓冲区中的数据按照一定的规律传递给位置变量apos
    gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, 0, 0);
    //允许数据传递
    gl.enableVertexAttribArray(aposLocation);

    //开始绘制,显示器显示结果
    gl.drawArrays(gl.TRIANGLES, 0, 3);

    //声明初始化着色器函数
    function initShader(gl, vertexShaderSource, fragmentShaderSource) {
      //创建着色器对象
      var vertexShader = gl.createShader(gl.VERTEX_SHADER);
      var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
      //引入着色器源代码
      gl.shaderSource(vertexShader, vertexShaderSource);
      gl.shaderSource(fragmentShader, fragmentShaderSource);
      //编译着色器
      gl.compileShader(vertexShader);
      gl.compileShader(fragmentShader);
      //创建程序对象program
      var program = gl.createProgram();
      //附着着色器到program
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      //链接program
      gl.linkProgram(program);
      //使用program
      gl.useProgram(program);
      //返回程序program对象
      return program;
    }
  </script>
</body>

</html>

3.3解析:本案例将对z轴进行投影,投影为二维平面,所以第一个顶点的1.0,可以取值[-1.0,1.0],效果都一样,若超过1.0或少于-1.0,测试不难发现会有部分被裁掉,因为在webgl中坐标为[-1.0 ,1.0],所以效果左边的图设置三角形顶点1的z值[-1.0,1.0]皆是那个效果,右图为z值为2.0的效果

中级案例:

绘制一个时钟

 1.实现效果:

2.实现代码:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>时针</title>
    <style>
        #myCanvas{
            background-color: rgb(246, 246, 249);
        }
    </style>
    <script src="glMatrix-0.9.6.min.js"></script>
    <script>
        let vertexstring = `
        attribute vec4 a_position;
        uniform mat4 u_formMatrix;
        void main(void){
            gl_Position =u_formMatrix * a_position;
        }
        `;
        let fragmentstring = `
        precision mediump float;
        void main(void){
          gl_FragColor =vec4(0.0,0.0,0.0,1.0);
        }
        `;
        var projMat4 = mat4.create();
        var webgl;
        var uniformTexture = 0;
        var uniformTexture1 = 0;
        var uniformAnim = 0;
        var count = 0;
     
        function webglStart() {
            init();
            tick();
        }
        function tick() {
             window.setTimeout(tick, 1000);
             initbuffer(0.8);
             initbuffer(0.5);
             initbuffer(0.4);
            count = count +1;
        };
        function init() {
            initWebgl();
            initShader();
        }
        function initWebgl() {
            let webglDiv = document.getElementById('myCanvas');
            webgl = webglDiv.getContext("webgl");
            webgl.viewport(0, 0, webglDiv.clientWidth, webglDiv.clientHeight);
            //mat4.ortho(0, webglDiv.clientWidth, webglDiv.clientHeight, 0, -1.0, 1.0, projMat4)
        }
        function initShader() {

            let vsshader = webgl.createShader(webgl.VERTEX_SHADER);
            let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);

            webgl.shaderSource(vsshader, vertexstring);
            webgl.shaderSource(fsshader, fragmentstring);

            webgl.compileShader(vsshader);
            webgl.compileShader(fsshader);
            if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {
                var err = webgl.getShaderInfoLog(vsshader);
                alert(err);
                return;
            }
            if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {
                var err = webgl.getShaderInfoLog(fsshader);
                alert(err);
                return;
            }
            let program = webgl.createProgram();
            webgl.attachShader(program, vsshader);
            webgl.attachShader(program, fsshader)

            webgl.linkProgram(program);
            webgl.useProgram(program);

            webgl.program = program
        }
        
        function initbuffer(radius){
           //秒分时针,根据长度分且角度需合理设置
            let angle;
            if(radius>0.6){
            //角度为负值
             angle = -Math.PI/30 *count;
             
            }
            else if(radius>0.4){
            angle = -Math.PI/1800 *count;
            }
            else{
            angle = -Math.PI/1800 *count *5/60;
            // angle = -Math.PI/30 *count;
            }
            const cos = Math.cos(angle)*radius;
            const sin = Math.sin(angle)*radius;
            var matArr= new Float32Array([
                cos,sin,0,0,
                -sin,cos,0,0,
                0,0,1,0,
                0,0,0,1
            ]);
            let arr = [
                0, 0, 0, 1,
                0, 0.05, 0, 1,
                radius, 0, 0, 1,
                radius, 0, 0, 1,
                0, 0.05, 0, 1,
                radius, 0.05, 0, 1
            ]
            let pointPosition = new Float32Array(arr);
            let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");
            let triangleBuffer = webgl.createBuffer();
            webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);
            webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW);
            webgl.enableVertexAttribArray(aPsotion);
            webgl.vertexAttribPointer(aPsotion, 4, webgl.FLOAT, false, 0, 0);
            let uniformMatrix = webgl.getUniformLocation(webgl.program, "u_formMatrix");
            webgl.uniformMatrix4fv(uniformMatrix, false, matArr)
            webgl.drawArrays(webgl.TRIANGLES, 0, 6);
        }
3.核心讲解:

3.1动画设置:每隔1秒钟绘制新的图形(图形不断进行旋转)

 function tick() {
             window.setTimeout(tick, 1000);
             initbuffer(0.8);
             initbuffer(0.5);
             initbuffer(0.4);
            count = count +1;
        };

3.2时针旋转角度计算、旋转矩阵构建:根据常识计算旋转角度,需要注意的是这角度为负值,旋转方向才为顺时针,旋转矩阵可看作绕Z轴旋转所得的矩阵

           //秒分时针,根据长度分且角度需合理设置
            let angle;
            if(radius>0.6){
            //角度为负值
             angle = -Math.PI/30 *count;
             
            }
            else if(radius>0.4){
            angle = -Math.PI/1800 *count;
            }
            else{
            angle = -Math.PI/1800 *count *5/60;
            // angle = -Math.PI/30 *count;
            }
            const cos = Math.cos(angle)*radius;
            const sin = Math.sin(angle)*radius;
            var matArr= new Float32Array([
                cos,sin,0,0,
                -sin,cos,0,0,
                0,0,1,0,
                0,0,0,1
            ]);

多层纹理实现雾效果

 1.实现效果:

2.实现代码:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>多重纹理</title>
</head>

<body>
  <!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布-->
  <canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas>

  <!-- 顶点着色器源码 -->
  <script id="vertexShader" type="x-shader/x-vertex">
    attribute vec4 a_Position;//顶点位置坐标
  attribute vec2 a_TexCoord;//纹理坐标
  varying vec2 v_TexCoord;//插值后纹理坐标
  void main() {
    //顶点坐标apos赋值给内置变量gl_Position
    gl_Position = a_Position;
    //纹理坐标插值计算
    v_TexCoord = a_TexCoord;
  }
  </script>
  <!-- 片元着色器源码 -->
  <script id="fragmentShader" type="x-shader/x-fragment">
    //所有float类型数据的精度是highp
      precision highp float;
      // 接收插值后的纹理坐标
      varying vec2 v_TexCoord;
      // 纹理图片像素数据
      uniform sampler2D u_Sampler;
      uniform sampler2D u_Sampler1;
      uniform float u_Ani;
      void main() {
        // 采集纹素,逐片元赋值像素值
       vec4 color1 = texture2D(u_Sampler,v_TexCoord);

       vec4 color2 = texture2D(u_Sampler1,vec2(v_TexCoord.x + u_Ani , v_TexCoord.y));

      gl_FragColor = color1 + color2;

      }
  </script>
  <script>
    //通过getElementById()方法获取canvas画布
    var canvas = document.getElementById('webgl');
    //通过方法getContext()获取Gl上下文
    var gl = canvas.getContext('webgl');
    //顶点着色器源码
    var vertexShaderSource = document.getElementById('vertexShader').innerText;
    //片元着色器源码
    var fragShaderSource = document.getElementById('fragmentShader').innerText;
    var count = 0;
    //初始化着色器
    var program = initShader(gl, vertexShaderSource, fragShaderSource);
    var a_Position = gl.getAttribLocation(program, 'a_Position');
    var a_TexCoord = gl.getAttribLocation(program, 'a_TexCoord');
    var u_Sampler = gl.getUniformLocation(program, 'u_Sampler');
    var u_Sampler1 = gl.getUniformLocation(program, 'u_Sampler1');
    var u_Ani = gl.getUniformLocation(program, 'u_Ani');


    //拉伸改变其坐标值
    var data = new Float32Array([
      -0.5, 0.5, //左上角——v0
      -0.5, -0.5, //左下角——v1
      0.5, 0.5, //右上角——v2
      0.5, -0.5 //右下角——v3
    ]);
    /**
     * 创建UV纹理坐标数据textureData
     **/
    var textureData = new Float32Array([
      0, 1, //左上角——uv0
      0, 0, //左下角——uv1
      1, 1, //右上角——uv2
      1, 0 //右下角——uv3
    ]);

    //创建缓冲区对象
    var buffer = gl.createBuffer();
    //绑定缓冲区对象,激活buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    //顶点数组data数据传入缓冲区
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    //缓冲区中的数据按照一定的规律传递给位置变量apos
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
    //允许数据传递
    gl.enableVertexAttribArray(a_Position);


    //创建缓冲区对象
    var textureBuffer = gl.createBuffer();
    //绑定缓冲区对象,激活buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);
    //顶点数组data数据传入缓冲区
    gl.bufferData(gl.ARRAY_BUFFER, textureData, gl.STATIC_DRAW);
    //缓冲区中的数据按照一定的规律传递给位置变量apos
    gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 0, 0);
    //允许数据传递
    gl.enableVertexAttribArray(a_TexCoord);
    //要调用呀!写了函数不用是什么鬼
    loop();


    /**
     创建缓冲区textureBuffer,传入图片纹理数据,然后执行绘制方法drawArrays()
    **/


    texture1 = initTexture("fog.png");
    texture0 = initTexture("山水.png");


    function handleLoadedTexture(texture) {

      gl.bindTexture(gl.TEXTURE_2D, texture);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
      // 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);

    }

    function initTexture(imageFile) {
      let textureHandle = gl.createTexture();
      textureHandle.image = new Image();
      textureHandle.image.src = imageFile;
      textureHandle.image.onload = function () {
        handleLoadedTexture(textureHandle)
      }
      return textureHandle;
    }

    function loop() {
      requestAnimationFrame(loop);
      draw();

    }

    function draw() {
      gl.clearColor(0.0, 1.0, 0.0, 1.0);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      gl.enable(gl.DEPTH_TEST);

      count = count + 0.01;
      // console.log(count);
      gl.uniform1f(u_Ani, count);

      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, texture0);
      gl.uniform1i(u_Sampler, 0);

      gl.activeTexture(gl.TEXTURE1);
      gl.bindTexture(gl.TEXTURE_2D, texture1);
      gl.uniform1i(u_Sampler1, 1);

      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    }


    //声明初始化着色器函数
    function initShader(gl, vertexShaderSource, fragmentShaderSource) {
      //创建着色器对象
      var vertexShader = gl.createShader(gl.VERTEX_SHADER);
      var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
      //引入着色器源代码
      gl.shaderSource(vertexShader, vertexShaderSource);
      gl.shaderSource(fragmentShader, fragmentShaderSource);
      //编译着色器
      gl.compileShader(vertexShader);
      gl.compileShader(fragmentShader);
      //创建程序对象program
      var program = gl.createProgram();
      //附着着色器到program
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      //链接program
      gl.linkProgram(program);
      //使用program
      gl.useProgram(program);
      //返回程序program对象
      return program;
    };
  </script>
</body>

</html>
3.核心拆解:

3.1 片元着色器中调用 texture2D(texture,texcoord),方法有两个参数,一个为所需要的纹理,另一部分为纹理坐标。纹理坐标好办,创建类型化数组传递即可。本案例涉及多重纹理叠加,所以最后片源着色器的颜色为两种颜色的和。

       vec4 color1 = texture2D(u_Sampler,v_TexCoord);
       vec4 color2 = texture2D(u_Sampler1,vec2(v_TexCoord.x + u_Ani , v_TexCoord.y));
       gl_FragColor = color1 + color2;

本案例还涉及动态雾,所以需要利用requestAnimationFrame()方法,进行不断绘制。

function tick() {
            requestAnimFrame(tick)
            draw();
        };

下面重点讲述纹理数据的创建与传递,纹理的创建可参考缓冲区的创建:

1.webgl.createTexture创建纹理对象

2.配置纹理对象的属性 .image   .image.src   .image.onload

3.绑定纹理、设置二维纹理、设置纹理属性,将其封装归类到handleLoaderTexture()方法中

4.最后在绘制函数draw()中激活、绑定、传递纹理数据

        function handleLoadedTexture(texture) {


            webgl.bindTexture(webgl.TEXTURE_2D, texture);
            webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, webgl.RGBA, webgl.UNSIGNED_BYTE, texture.image);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.LINEAR);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.LINEAR);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.REPEAT);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.REPEAT);

            // webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE);
            // webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE);

        }
        function initTexture(imageFile, num) {
            let textureHandle = webgl.createTexture();
            textureHandle.image = new Image();
            textureHandle.image.src = imageFile;
            textureHandle.image.onload = function () {
                handleLoadedTexture(textureHandle, num)
            }
            return textureHandle;
        }
        function draw() {
            webgl.clearColor(0.0, 1.0, 0.0, 1.0);
            webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
            webgl.enable(webgl.DEPTH_TEST);
            //纹理变动
            uniformAnim = webgl.getUniformLocation(webgl.program, "anim");
            count = count + 0.01;
            webgl.uniform1f(uniformAnim, count);
            
            webgl.activeTexture(webgl.TEXTURE0);
            webgl.bindTexture(webgl.TEXTURE_2D, texture0);
            webgl.uniform1i(uniformTexture, 0);

            webgl.activeTexture(webgl.TEXTURE1);
            webgl.bindTexture(webgl.TEXTURE_2D, texture1);
            webgl.uniform1i(uniformTexture1, 1);

            webgl.drawArrays(webgl.TRIANGLES, 0, 6);
        }

(注:在WebGL渲染纹理时,纹理的像素也有要求的,需要为2的幂次方,所以有时用自己的纹理图片去贴图时,明明代码都一样却总出来一个黑色照片,不是代码的问题,是原始数据的问题)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/713983.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

企业化运维(4)_tomcat

###1.配置tomcat### 可以将tomcat部署在server2主机&#xff0c;与nginx主服务器分开&#xff0c;便于进行交互存储。 下载安装jdk与tomcat&#xff0c;并开启服务&#xff0c;便可以在浏览器进行访问。 [rootserver3 ~]# rpm -ivh jdk-8u121-linux-x64.rpm [rootserver3 ~]#…

Mybatis-Plus多种批量插入方案对比

背景 六月某日上线了一个日报表任务&#xff0c;因是第一次上线&#xff0c;故需要为历史所有日期都初始化一次报表数据 在执行过程中发现新增特别的慢&#xff1a;插入十万条左右的数据&#xff0c;SQL执行耗费高达三分多钟 因很早就听闻过mybatis-plus的[伪]批量新增的问题&…

万事开头难——Java实现俄罗斯小方块【第一步】

目录 技术实现&#xff1a; 1.初始化游戏窗口&#xff1b; 1.1 什么是窗口&#xff1a; 1.2 Swing 1.3 JFrame创建窗口&#xff1a; 1.3.1创建窗口的逻辑 1.3.2.设置简单的页面 1.3.3.优化 1.3.4.设置标题 1.4 创建游戏窗口 技术实现&#xff1a; 1.初始化游戏窗口&am…

【CT】LeetCode手撕—20. 有效的括号

题目 原题连接&#xff1a;20. 有效的括号 1- 思路 模式识别 模式1&#xff1a;括号左右匹配 ——> 借助栈来实现 ——> Deque<Character> deque new LinkedList<>()模式2&#xff1a;顺序匹配 ——> 用 if 判断 具体思路 1.遇到左括号 直接入栈相应…

ARM32开发--电源管理单元

知不足而奋进 望远山而前行 目录 文章目录 前言 学习目标 学习内容 PMU 电源域 VDD/VDDA域 备份域 1.2V域 省电模式 睡眠模式 深度睡眠模式 待机模式 几种模式总结 WFI和WFE指令 案例需求 模式初始化 源码 总结 前言 在嵌入式系统中&#xff0c;有效的电池管…

【AI基础】第六步:纯天然保姆喂饭级-安装并运行qwen2-7b

整体步骤类似于 【AI基础】第五步&#xff1a;纯天然保姆喂饭级-安装并运行chatglm3-6b-CSDN博客。 此系列文章列表&#xff1a; 【AI基础】概览 【AI基础】第一步&#xff1a;安装python开发环境-windows篇_下载安装ai环境python 【AI基础】第一步&#xff1a;安装python开发环…

面试题 17.05. 字母与数字

链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a;把字母看成1&#xff0c;把数字看为-1&#xff0c;将题目变为求的和为0的最长连续子数组 class Solution { public:vector<string> findLongestSubarray(vector<string>& array) …

MySQL查询练习题1.平均工资2.查询各部门的总薪水3.查询总薪水排名第二的部门4.查询姓名重复的员工信息5.查询各部门薪水大于900的男性员工的平均薪水

创建一个员工表emp&#xff0c;包含字段&#xff1a;姓名name&#xff0c;性别sex&#xff0c;部门depart&#xff0c;工资salary create table emp(name varchar(30) not null,sex varchar(30) not null,depart int not null,salary int not null); 插入数据打印为 mysql>…

Windows Server 2008 r2 IIS .NET

Windows Server 2008 r2 IIS .NET

2-2 基于matlab的变邻域

基于matlab的变邻域&#xff0c;含变惯性权重策略的自适应离散粒子群算法&#xff0c;适应函数是多式联运路径优化距离。有10城市、30城市、75城市三个案例。可直接运行。 2-2 路径规划 自适应离散粒子群算法 - 小红书 (xiaohongshu.com)

MySQL基础——多表查询和事务

目录 1多表关系 2多表查询概述 3连接查询 3.1内连接 3.2左外连接 3.3右外连接 3.4自连接 4联合查询 5子查询 5.1标量子查询(子查询结果为单个值) 5.2列子查询(子查询结果为一列) 5.3行子查询(子查询结果为一行) 5.4表子查询(子查询结果为多行多列) 6事务简介和操…

模拟电子技术基础(二)--PN结

PN结的本质 芯片都是由硅晶体制成&#xff0c;单个硅原子最外层有带有4个电子 在纯硅当中这些电子会两两形成共价键&#xff0c;此时周围形成非常稳定的八电子结构 在一个回路中&#xff0c;灯泡不亮&#xff0c;不导通&#xff0c;因为电池无法吸引其中的电子离开&#xff0c…

机器学习在医学领域中的应用|文献精析·24-06-13

小罗碎碎念 2024-06-13&#xff5c;文献精析&#xff1a;机器学习在医学领域中的应用 为了系统性地和大家梳理一下机器学习在医学领域中的应用&#xff0c;我特意去找了一篇文献&#xff0c;把其中有价值的信息筛选出来了。但是我没选的内容不代表不重要&#xff0c;感兴趣的可…

高分论文密码---大尺度空间模拟预测与数字制图

大尺度空间模拟预测和数字制图技术和不确定性分析广泛应用于高分SCI论文之中&#xff0c;号称高分论文密码。大尺度模拟技术可以从不同时空尺度阐明农业生态环境领域的内在机理和时空变化规律&#xff0c;又可以为复杂的机理过程模型大尺度模拟提供技术基础。我们将结合一些经典…

Java SE进阶必备:数组中的命令行参数详解

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

5000天后的世界

为何可以预见未来 1993年&#xff0c;在互联网的黎明时代&#xff0c;凯文凯利创办了《连线》杂志。他曾经采访过以比尔盖茨、史蒂夫乔布斯、杰夫贝佐斯为代表的一众风云创业家。《连线》杂志是全球发行的世界著名杂志&#xff0c;一直致力于报道科学技术带来的经济、社会变革…

Linux screen命令使用

文章目录 1. 前言2. screen是什么?3. screen使用场景描述3. screen常用命令4. 小结5. 参考 1. 前言 实际开发中用到的云服务器&#xff0c;如果项目使用的是python&#xff0c;需要利用项目运行一些时间较长的项目程序脚本的话&#xff0c;由于我们通过ssh连接远端服务器&…

【深度学习】TCN,An Empirical Evaluation of Generic Convolutional【二】

文章目录 膨胀卷积什么是膨胀卷积膨胀卷积公式PyTorch代码 从零开始手动实现一个1D膨胀卷积&#xff0c;不使用PyTorch的nn.Conv1d1. 基本概念2. 手动实现1D膨胀卷积 TCN结构如何使用TCN源码说明1. Chomp1d 类2. TemporalBlock 类3. TemporalConvNet 类 使用方法 膨胀卷积 什么…

计算机毕业设计hadoop+spark+hive知识图谱股票推荐系统 股票数据分析可视化大屏 股票基金爬虫 股票基金大数据 机器学习 大数据毕业设计

哈 尔 滨 理 工 大 学 毕业设计开题报告 题 目&#xff1a; 基于Spark的股票大数据分析及可视化系统 院 系&#xff1a; 计算机科学与技术学院 数据科学与大数据技术系 2023年10月 一、选题的依据…

String常用方法详解

auth&#xff1a;别晃我的可乐 date&#xff1a;2024年06月16日 比较大小 equals(Object obj): 用于比较字符串内容是否相等。compareTo(String anotherString): 按字典顺序比较两个字符串。 String str1 "hello"; String str2 "world";boolean isEqual …