webgl

webgl

第一章 webgl绘图流程


一、创建canvas画布

<!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>Document</title>
    <style>
        body{
            margin: 0;
            overflow: hidden;
        }
        #canvas{
            background-color: pink;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
</body>
</html>

二、js获取canvas画布

const canvas = document.querySelector('#canvas')
        canvas.width = window.innerWidth
        canvas.height = window.innerHeight

三、使用canvas获取webgl绘图上下文

 const gl = canvas.getContext("webgl")
        gl.clearColor(1,0,0,1)
        gl.clear(gl.COLOR_BUFFER_BIT)

四、在script中建立顶点着色器和顶点着色器(glsl es语言)

<!-- 顶点着色器 -->
<script id="vertexShader" type="x-shader/x-vertex">
        void main(){
            gl_Position = vec4(0,0,0,1);
            gl_PointSize = 50.0;
        }
</script>
    <!-- 片元着色器 -->
<script id="fragmentShader" type="x-shader/x-fragment">
        void main(){
            gl_FragColor = vec4(1,1,1,1);
        }
</script>

五、在js中获取顶点着色器和片元着色器

//顶点着色器文本
const vsSource = document.getElementById('vertexShader').innerText;
//片元舍色器文本
const fsSourse = document.getElementById('fragmentShader').innerText;

六、初始化节点

 //初始化着色器
 initShaders(gl,vsSource,fsSourse);

七、指定将来用来清空绘图区的颜色

//指定将要用来清理绘图区的颜色
gl.clearColor(0,0,0,1);

八、使用指定颜色清空绘图区

//清理绘图区
gl.clear(gl.COLOR_BUFFER_BIT);

九、绘制顶点

//绘制顶点(点模式)
gl.drawArrays(gl.POINTS,0,1);

十、初始化节点具体代码实现

function initShaders(gl,vsSource,fsSourse){
//创建程序对象
const program = gl.createProgram();
//建立着色对象
const vertexShader = loadShader(gl,gl.VERTEX_SHADER,vsSource);
const fragmentShader = loadShader(gl,gl.FRAGMENT_SHADER,fsSourse);
//将顶点着色对象装进程序对象中
gl.attachShader(program, vertexShader);
//将片元着色对象装进程序对象中
         gl.attachShader(program,fragmentShader);
//连接webgl上下文对象和程序对象
gl.linkProgram(program);
//启动程序对象
gl.useProgram(program);
//将程序对象挂到上下文对象
gl.program = program;
return true;
}

function loadShader(gl,type,source){
//根据着色器的类型,建立着色器对象
const shader = gl.createShader(type);
//将着色器源文件传入着色器对象中
gl.shaderSource(shader, source);
//编译着色器对象
gl.compileShader(shader);
//返回着色器对象
return shader;
}

第二章 用js控制点位

一、js中声明attribute变量

<script id="vertexShader" type="x-shader/x-vertex">
//声明变量类型vec4,导出变量a_Position
        attribute vec4 a_Position;
//修改顶点尺寸和修改顶点位置是一样的原理,只是类型为float
        void main(){
            gl_Position = a_Position;
            gl_PointSize = 50.0;
        }
    </script>

二、js获取attribute变量

//设置attribute变量
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');

三、在js中修改attribute变量

//修改attribute变量值
gl.vertexAttrib3f(a_Position,0,0,0)
//参数(变量名,x,y,z)
//也有许多同族函数
gl.vertexAttrib1f(a_Position,0)
gl.vertexAttrib2f(a_Position,0,0)
gl.vertexAttrib3f(a_Position,0,0,0)
gl.vertexAttrib4f(a_Position,0,0,0,0)

案例小节(鼠标控制点位)

1.获取鼠标在canvas中的位置

//获取鼠标点在canvas中的位置
canvas.addEventListener('click',(event)=>{
const {clientX,clientY} = event;
const {left,top} = canvas.getBoundingClientRect();
const [cssX,cssY]=[clientX - left,clientY - top];
        })

//解决canvas和webgl差异
//1.解决原点差异
const [halfWidth,halfHeight] = [width/2,height/2];
const [xBaseCenter,yBaseCenter] = [cssX - halfWidth,cssY - halfHeight];
//2.解决y轴轴向差异
const yBaseCenterTop = -yBaseCenter;
//3.解决基底差异
const [x,y] = [xBaseCenter/halfWidth,yBaseCenterTop/halfHeight];

2.绘制图层

gl.vertexAttrib2f(a_Position,x,y);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS,0,1);

四、webgl同步绘图原理

​ webgl 的同步绘图的现象,其实是由于 webgl 底层内置颜色缓冲区导致的.它在电脑中会占用一块内存,在我们使用 webgl 绘图的时候,是在颜色缓冲区中画出来,但是图片暂时还未渲染出来.只有 webgl 自己知道.如果我们想要将图像的时候,那就照着缓冲区的图像去画,这个步骤是 webgl 内部自己完成的,我们只需要执行绘图命令就行了,颜色缓冲区存储的图像,只有当前线程有效,比如我们先在 js 主线程绘图的时候,主线程结束后,会在执行信息队列的异步线程,子啊执行异步线程时,颜色缓冲区会被 webgl 重置,导致颜色缓冲器绘制的图形被清除,导致以前绘制的图像也会消失.

第三章 用js控制顶点颜色

一、js中声明uniform变量

<script id="fragmentShader" type="x-shader/x-fragment">
 //将float精度设置为中等
 precision mediump float;
 uniform vec4 u_FragColor;
 void main(){
    gl_FragColor = u_FragColor;
 }
</script>

二、js获取uniform变量

const u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');

三、在js中修改uniform变量值

//一个一个传递参数
gl.uniform4f(u_FragColor, 0.0, 1.0, 0.0, 1.0);
//可以以数组的形式传递参数
//Float32Array为一种32位浮点型数组,在浏览器中的运行效率比Array要高的多
const color = new Float32Array([1.0,1.0,0.0,1.0]);
gl.uniform4v(u_FragColor,color);

四、片元着色器的绘图原理

​ 我们所看到的图形都是由多个片元组成的,那么想要绘制相应图形,只需要渲染范围内的片元就可以了

具体代码实现:

<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_FragColor;
void main(){
  //distance为计算两个点的距离 distance(p1,p2)
  //gl_PointCoord片元在一个点中的位置是统一化的
  //discard丢弃,不会进行渲染
float dist = distance(gl_PointCoord,vec2(0.5,0.5));
if(dist < 0.5){
  //进入范围就渲染
    gl_FragColor = u_FragColor;
}else{
    discard;
}
}
</script>

五、片元着色器合成功能

开启着色器合成功能和声明合成方式才可以生效

//开启片元着色器合成功能
gl.enable(gl.BLEND);
//设置片元着色器合成方式
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

第四章 架构补间动画

完成透明度动画

一、建立合成对象

export default class Compose {
    constructor(){
//parent 父对象,合成对象可以嵌套
        this.parent = null;
//children 子对象集合,其集合可以是时间轨,也可以是合成对象
        this.children = [];
    }
//添加子对象方法
    add(obj){
        obj.parent = this;
        this.children.push(obj);
    }
//基于当前时间更新子对象状态的方法
    update(t) {
        this.children.forEach((ele)=>{
            ele.update(t);
        })
    }
}

二、建立时间轨

export default class Track{
    constructor(target){
        this.target = target;
        this.parent = null;
        this.start = 0;
        this.timeLen = 5;
        this.loop = false;
        this.keyMap = new Map();
    }
    update(t) {
        const {target,start,timeLen,loop,keyMap} = this;
        //本地时间
        let time = t - start;
        if(loop){
            time = time % timeLen;
        }
        //遍历关键帧集合,判断在两个关键帧之前还是之后
        for(const [key,fms] of keyMap){
            const last = fms.length - 1;
            if(time < fms[0][0]){
                target[key] = fms[0][1]; 
            }else if(time > fms[0][1]){
                target[key] = fms[last][0];
            }else{
                target[key] = getValBetweenFms(time,fms,last);
            }
        }
    }
}

//获取两个关键帧之间的补间状态的方法
function getValBetweenFms(time,fms,last){
    for(let i = 0;i < last;i++){
        //两个关键帧fm1和fm2
        const fm1 = fms[i];
        const fm2 = fms[i + 1];
        //如果在两个关键帧之间,基于两个关键帧的时间和状态求点斜式
        if(time >= fm1[0] && time <= fm2[0]){
            const delta = {
                x: fm2[0] - fm1[0],
                y: fm2[1] - fm1[1],
            };
            //获取斜率k和截距b
            const k = delta.y / delta.x;
            const b = fm1[1] - fm1[0] * k;
            //根据点斜式求当前时间对应的状态
            return k * time + b;
        }
    }
}

三、使用合成对象和轨道对象制作补间动画

//引入合成对象Compose和轨道对象Track
import Compose from './utils/Compose.js'
import Track from './utils/ComposeOne.js'
const compose = new Compose();
const obj = {x,y,size};
starts.push(obj);
//建立轨道对象
const track = new Track(obj);
track.start = new Date();
track.timeLen = 2000;
track.loop = true;
track.keyMap = new Map([
            [
                'size',
                [
                    [500,size],
                    [1000,0],
                    [1500,size]
                ]
            ]
           ])
compose.add(track);
//连续渲染
!(function ani(){
            compose.update(new Date());
            render();
            requestAnimationFrame(ani);
 })()
 //渲染方法
 function render(){
gl.clear(gl.COLOR_BUFFER_BIT);
starts.forEach(({x,y,size})=>{
   gl.vertexAttrib2f(a_Position,x,y);
   gl.vertexAttrib1f(a_PointSize,size);
   gl.uniform4f(u_FragColor, 1.0, 1.0,1.0, Math.random()*1);
   
  gl.drawArrays(gl.POINTS,0,1);
            })
        }

第五章 绘制多点图形

一、绘制多点步骤

和之前的webgl绘图流程是一样的只是赋值attribute不一样

<!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>webgl多点绘制</title>
    <style>
        body{
            margin: 0;
            overflow: hidden;
        }
        #canvas{
            background-color: antiquewhite;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <!-- 顶点着色器 -->
    <script id="vertexShader" type="x-shader/x-vertex">
        attribute vec4 a_Position;
        void main(){
            gl_Position = a_Position;
            gl_PointSize = 50.0;
        }
    </script>
    <!-- 片元着色器 -->
    <script id="fragmentShader" type="x-shader/x-fragment">
        void main(){
            gl_FragColor = vec4(1,1,1,1);
        }
    </script>
    <script type="module">
        import {initShaders} from './utils/index.js'
        //canvas画布,获取上下文对象
        const canvas = document.querySelector('#canvas');
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        //webgl画笔
        const gl = canvas.getContext("webgl");
        //顶点着色器文本
        const vsSource = document.getElementById('vertexShader').innerText;
        //片元舍色器文本
        const fsSourse = document.getElementById('fragmentShader').innerText;
        //初始化着色器
        initShaders(gl,vsSource,fsSourse);
      //设置attribute变量
        const a_Position =                        gl.getAttribLocation(gl.program, 'a_Position');
        //在这开始修改

        //指定将要用来清理绘图区的颜色
        gl.clearColor(0,0,0,1);
        //清理绘图区
        gl.clear(gl.COLOR_BUFFER_BIT);
        //绘制顶点
        gl.drawArrays(gl.POINTS,0,1);
    </script>
</body>
</html>

二、绘制多点

//如何向attribute变量中写入多点,并绘制多点
//创建顶点数据
const vertices =new Float32Array([0.0,0.1,-0.1,-0.1,0.1,-0.1])
//建立缓存对象,独立缓存区
const vertexBuffer = gl.createBuffer();
//绑定缓存对象
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
//写入数据
gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);
//获取attribute变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
//开启批处理功能
gl.enableVertexAttribArray(a_Position);
//添加顶点的时候,也要修改绘制顶点的数量 
//绘制顶点
 gl.drawArrays(gl.POINTS,0,3);

三、绘制三角面

//不需要顶点大小了
//修改绘图方式就可以了
gl.drawArrays(gl.TRIANGLES,0,3);

四、绘制线

绘制线的方式主要是修改顶点个数和绘图方式gl.drawArrays(mode,first,count);

  • POINTS 可视的点
  • LINES 单独线段
  • LINE_STRIP 线条
  • LINE_LOOP闭合线条
  • TRIANGLES 单独三角形
  • TRIANGLE_STRIP 三角带
  • TRIANGLE_FAN 三角扇

五、三角带和三角扇的规则

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2022-2023 alan_mf
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信