WebGL异步绘制多点

异步绘制线段

1.先画一个点
在这里插入图片描述
2.一秒钟后,在左下角画一个点
在这里插入图片描述
3.两秒钟后,我再画一条线段
在这里插入图片描述

<!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 id="webgl" width="200" height="200"></canvas>
    <script>
        const webgl = document.getElementById('webgl')
        const gl = webgl.getContext('webgl')

        // 创建着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER)
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
        // 绑定数据源
        // 声明顶点着色器 attribute 变量
        gl.shaderSource(vertexShader, `
            attribute vec4 a_Position;
            void main(){
               gl_Position = a_Position;
               gl_PointSize = 20.0;
            }
        `)
        gl.shaderSource(fragmentShader, `
            void main(){
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
            }
        `)

        // 编译着色器
        gl.compileShader(vertexShader)
        gl.compileShader(fragmentShader)
        // 创建着色器程序
        const program = gl.createProgram()
        // 绑定着色器
        gl.attachShader(program, vertexShader)
        gl.attachShader(program, fragmentShader)
        // 连接着色器
        gl.linkProgram(program)
        // 使用着色器
        gl.useProgram(program)

        // 顶点数据
        let points = [0, 0.2]
        // 创建缓冲区
        const vertexBuffer = gl.createBuffer()
        // 绑定缓冲区 
        // target 要把缓冲区放在webgl系统中的什么位置, buffer 缓冲区
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
        // 写入数据
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW)
        // 获取到顶点着色器中变量
        const a_Position = gl.getAttribLocation(program, 'a_Position')
        // 从当前绑定的缓冲区中读取顶点数据(index, size, type, normalized是否顶点数据归一, stride相邻两个顶点间的字节数, offset从缓冲区的什么位置开始存储变量)
        gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
        // 开启顶点数据的批处理功能
        gl.enableVertexAttribArray(a_Position)

        gl.drawArrays(gl.POINTS, 0, 1);

        // 一秒钟后,向顶点数据中再添加的一个顶点,修改缓冲区数据,然后清理画布,绘制顶点
        setTimeout(() => {
            points.push(-0.2, -0.1)
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW)
            gl.drawArrays(gl.POINTS, 0, 2);
        }, 1000)

        // 两秒钟后,清理画布,绘制顶点,绘制线条
        setTimeout(() => {
            gl.drawArrays(gl.POINTS, 0, 2);
            gl.drawArrays(gl.LINE_STRIP, 0, 2);
        }, 2000)
    </script>
</body>

</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 id="webgl" width="200" height="200"></canvas>
    <script>
        const webgl = document.getElementById('webgl')
        const gl = webgl.getContext('webgl')

        // 创建着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER)
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
        // 绑定数据源
        // 声明顶点着色器 attribute 变量
        gl.shaderSource(vertexShader, `
            attribute vec4 a_Position;
            void main(){
               gl_Position = a_Position;
               gl_PointSize = 20.0;
            }
        `)
        gl.shaderSource(fragmentShader, `
            void main(){
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
            }
        `)

        // 编译着色器
        gl.compileShader(vertexShader)
        gl.compileShader(fragmentShader)
        // 创建着色器程序
        const program = gl.createProgram()
        // 绑定着色器
        gl.attachShader(program, vertexShader)
        gl.attachShader(program, fragmentShader)
        // 连接着色器
        gl.linkProgram(program)
        // 使用着色器
        gl.useProgram(program)


        const defAttr = () => ({
            gl: null,
            vertices: [],
            geoData: [],
            size: 2,
            attrName: 'a_Position',
            count: 0,
            types: ['POINTS'],
        })
        class Poly {
            constructor(attr) {
                Object.assign(this, defAttr(), attr)
                this.init()
            }
            init() {
                const { attrName, size, gl } = this
                if (!gl) { return }
                const vertexBuffer = gl.createBuffer()
                gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
                this.updateBuffer()
                const a_Position = gl.getAttribLocation(program, attrName)
                gl.vertexAttribPointer(a_Position, size, gl.FLOAT, false, 0, 0)
                gl.enableVertexAttribArray(a_Position)
            }
            addVertice(...params) {
                this.vertices.push(...params)
                this.updateBuffer()
            }
            popVertice() {
                const { vertices, size } = this
                const len = vertices.length
                vertices.splice(len - size, len)
                this.updateCount()
            }
            setVertice(ind, ...params) {
                const { vertices, size } = this
                const i = ind * size
                params.forEach((param, paramInd) => {
                    vertices[i + paramInd] = param
                })
            }
            updateBuffer() {
                const { gl, vertices } = this
                this.updateCount()
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
            }
            updateCount() {
                this.count = this.vertices.length / this.size
            }
            updateVertices(params) {
                const { geoData } = this
                const vertices = []
                geoData.forEach(data => {
                    params.forEach(key => {
                        vertices.push(data[key])
                    })
                })
                this.vertices = vertices
            }
            draw(types = this.types) {
                const { gl, count } = this
                for (let type of types) {
                    gl.drawArrays(gl[type], 0, count);
                }
            }
        }

        const poly = new Poly({
            gl,
            vertices: [0, 0.2]
        })
        poly.draw(['POINTS'])

        setTimeout(() => {
            poly.addVertice(-0.2, -0.1)
            poly.draw(['POINTS'])
        }, 1000)

        setTimeout(() => {
            poly.draw(['POINTS', 'LINE_STRIP'])
        }, 2000)
    </script>
</body>

</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 id="webgl"></canvas>
    <script>
        const webgl = document.getElementById('webgl')
        webgl.width = window.innerWidth
        webgl.height = window.innerHeight
        const gl = webgl.getContext('webgl')

        // 创建着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER)
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
        // 绑定数据源
        // 声明顶点着色器 attribute 变量
        gl.shaderSource(vertexShader, `
            attribute vec4 a_Position;
            void main(){
               gl_Position = a_Position;
               gl_PointSize = 20.0;
            }
        `)
        gl.shaderSource(fragmentShader, `
            void main(){
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
            }
        `)

        // 编译着色器
        gl.compileShader(vertexShader)
        gl.compileShader(fragmentShader)
        // 创建着色器程序
        const program = gl.createProgram()
        // 绑定着色器
        gl.attachShader(program, vertexShader)
        gl.attachShader(program, fragmentShader)
        // 连接着色器
        gl.linkProgram(program)
        // 使用着色器
        gl.useProgram(program)


        const defAttr = () => ({
            gl: null,
            vertices: [],
            geoData: [],
            size: 2,
            attrName: 'a_Position',
            count: 0,
            types: ['POINTS'],
        })
        class Poly {
            constructor(attr) {
                Object.assign(this, defAttr(), attr)
                this.init()
            }
            init() {
                const { attrName, size, gl } = this
                if (!gl) { return }
                const vertexBuffer = gl.createBuffer()
                gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
                this.updateBuffer()
                const a_Position = gl.getAttribLocation(program, attrName)
                gl.vertexAttribPointer(a_Position, size, gl.FLOAT, false, 0, 0)
                gl.enableVertexAttribArray(a_Position)
            }
            addVertice(...params) {
                this.vertices.push(...params)
                this.updateBuffer()
            }
            popVertice() {
                const { vertices, size } = this
                const len = vertices.length
                vertices.splice(len - size, len)
                this.updateCount()
            }
            setVertice(ind, ...params) {
                const { vertices, size } = this
                const i = ind * size
                params.forEach((param, paramInd) => {
                    vertices[i + paramInd] = param
                })
            }
            updateBuffer() {
                const { gl, vertices } = this
                this.updateCount()
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
            }
            updateCount() {
                this.count = this.vertices.length / this.size
            }
            updateVertices(params) {
                const { geoData } = this
                const vertices = []
                geoData.forEach(data => {
                    params.forEach(key => {
                        vertices.push(data[key])
                    })
                })
                this.vertices = vertices
            }
            draw(types = this.types) {
                const { gl, count } = this
                for (let type of types) {
                    gl.drawArrays(gl[type], 0, count);
                }
            }
        }

        const poly = new Poly({
            gl,
            types: ['POINTS', 'LINE_STRIP']
        })

        webgl.addEventListener('click', (e) => {
            // 获取鼠标距离视口尺寸的距离
            const { clientX, clientY } = e
            // 获取cavans坐标宽高,距离视口尺寸的距离
            const { left, top, width, height } = webgl.getBoundingClientRect()
            // 鼠标在canvas中的位置
            const [cssX, cssY] = [clientX - left, clientY - top]

            // canvas坐标转webgl坐标
            // canvas画布的中心位置
            const [halfWidth, halfHeight] = [width / 2, height / 2]
            // 鼠标基于webgl坐标的中心位置
            const [xBaseCenter, yBaseCenter] = [cssX - halfWidth, cssY - halfHeight]
            // 解决y方向的差异
            const yBaseCenterTop = -yBaseCenter
            // 解决坐标基底的差异
            const [x, y] = [xBaseCenter / halfWidth, yBaseCenterTop / halfHeight]
            
            poly.addVertice(x, y)
            poly.draw()
        })

    </script>
</body>

</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 id="webgl"></canvas>
    <script>
        const webgl = document.getElementById('webgl')
        webgl.width = window.innerWidth
        webgl.height = window.innerHeight
        const gl = webgl.getContext('webgl')

        // 创建着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER)
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
        // 绑定数据源
        // 声明顶点着色器 attribute 变量
        gl.shaderSource(vertexShader, `
            attribute vec4 a_Position;
            void main(){
               gl_Position = a_Position;
               gl_PointSize = 20.0;
            }
        `)
        gl.shaderSource(fragmentShader, `
            void main(){
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
            }
        `)

        // 编译着色器
        gl.compileShader(vertexShader)
        gl.compileShader(fragmentShader)
        // 创建着色器程序
        const program = gl.createProgram()
        // 绑定着色器
        gl.attachShader(program, vertexShader)
        gl.attachShader(program, fragmentShader)
        // 连接着色器
        gl.linkProgram(program)
        // 使用着色器
        gl.useProgram(program)


        const defAttr = () => ({
            gl: null,
            vertices: [],
            geoData: [],
            size: 2,
            attrName: 'a_Position',
            count: 0,
            types: ['POINTS'],
        })
        class Poly {
            constructor(attr) {
                Object.assign(this, defAttr(), attr)
                this.init()
            }
            init() {
                const { attrName, size, gl } = this
                if (!gl) { return }
                const vertexBuffer = gl.createBuffer()
                gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
                this.updateBuffer()
                const a_Position = gl.getAttribLocation(program, attrName)
                gl.vertexAttribPointer(a_Position, size, gl.FLOAT, false, 0, 0)
                gl.enableVertexAttribArray(a_Position)
            }
            addVertice(...params) {
                this.vertices.push(...params)
                this.updateBuffer()
            }
            popVertice() {
                const { vertices, size } = this
                const len = vertices.length
                vertices.splice(len - size, len)
                this.updateCount()
            }
            setVertice(ind, ...params) {
                const { vertices, size } = this
                const i = ind * size
                params.forEach((param, paramInd) => {
                    vertices[i + paramInd] = param
                })
            }
            updateBuffer() {
                const { gl, vertices } = this
                this.updateCount()
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
            }
            updateCount() {
                this.count = this.vertices.length / this.size
            }
            updateVertices(params) {
                const { geoData } = this
                const vertices = []
                geoData.forEach(data => {
                    params.forEach(key => {
                        vertices.push(data[key])
                    })
                })
                this.vertices = vertices
            }
            draw(types = this.types) {
                const { gl, count } = this
                for (let type of types) {
                    gl.drawArrays(gl[type], 0, count);
                }
            }
        }

        // 容器来承载(多线)绘制
        class Sky {
            constructor(gl) {
                this.gl = gl
                this.children = []
            }
            add(obj) {
                obj.gl = this.gl
                this.children.push(obj)
            }
            updateVertices(params) {
                this.children.forEach(ele => {
                    ele.updateVertices(params)
                })
            }
            draw() {
                this.children.forEach(ele => {
                    ele.init()
                    ele.draw()
                })
            }
        }

        const sky = new Sky(gl)
        // 当前正在绘制的多边形
        let poly = null

        //删除最后一个顶点
        function popVertice() {
            poly.popVertice()
            poly = null
        }

        // 取消右击提示
        webgl.oncontextmenu = function () {
            return false
        }

        // 鼠标点击事件
        webgl.addEventListener("mousedown", (event) => {
            if (event.button === 2) {
                // 右击删除正在绘制的点
                popVertice()
            } else {
                // 获取鼠标距离视口尺寸的距离
                const { clientX, clientY } = event
                // 获取cavans坐标宽高,距离视口尺寸的距离
                const { left, top, width, height } = webgl.getBoundingClientRect()
                // 鼠标在canvas中的位置
                const [cssX, cssY] = [clientX - left, clientY - top]
   
                // canvas坐标转webgl坐标
                // canvas画布的中心位置
                const [halfWidth, halfHeight] = [width / 2, height / 2]
                // 鼠标基于webgl坐标的中心位置
                const [xBaseCenter, yBaseCenter] = [cssX - halfWidth, cssY - halfHeight]
                // 解决y方向的差异
                const yBaseCenterTop = -yBaseCenter
                // 解决坐标基底的差异
                const [x, y] = [xBaseCenter / halfWidth, yBaseCenterTop / halfHeight]
                if (poly) {
                    poly.addVertice(x, y)
                } else {
                    crtPoly(x, y)
                }
            }
            render()
        });
        //鼠标移动
        webgl.addEventListener("mousemove", (event) => {
            if (poly) {
                // 获取鼠标距离视口尺寸的距离
                const { clientX, clientY } = event
                // 获取cavans坐标宽高,距离视口尺寸的距离
                const { left, top, width, height } = webgl.getBoundingClientRect()
                // 鼠标在canvas中的位置
                const [cssX, cssY] = [clientX - left, clientY - top]

                // canvas坐标转webgl坐标
                // canvas画布的中心位置
                const [halfWidth, halfHeight] = [width / 2, height / 2]
                // 鼠标基于webgl坐标的中心位置
                const [xBaseCenter, yBaseCenter] = [cssX - halfWidth, cssY - halfHeight]
                // 解决y方向的差异
                const yBaseCenterTop = -yBaseCenter
                // 解决坐标基底的差异
                const [x, y] = [xBaseCenter / halfWidth, yBaseCenterTop / halfHeight]
                poly.setVertice(poly.count - 1, x, y)
                render()
            }
        });

        //创建多边形
        function crtPoly(x, y) {
            poly = new Poly({
                vertices: [x, y, x, y],
                types: ['POINTS', 'LINE_STRIP']
            })
            sky.add(poly)
        }
        // 渲染方法
        function render() {
            sky.draw()
        }

    </script>
</body>

</html>
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值