首页 > 其他分享 >WebGL拖动控制点绘制贝塞尔曲线——以三次贝塞尔曲线为例

WebGL拖动控制点绘制贝塞尔曲线——以三次贝塞尔曲线为例

时间:2024-08-04 17:54:29浏览次数:17  
标签:const 为例 BUFFER 0.0 曲线 贝塞尔 color ARRAY gl

为了实现该功能,这里将功能分成两部分。第一部分是控制点的拖动功能,第二部分是贝塞尔曲线的绘制功能。

控制点的拖动功能:鼠标按下选择点->鼠标移动修改点->鼠标松开释放点。选择点通过发生mousedown事件后遍历控制点数组,判断点击的位置是否和某个点的距离小于一定值,选择第一个满足条件的控制点,并向canvas绑定mousemove事件。修改点为鼠标移动时,将选择的点颜色的R分量改为1.0,即点变为红色,修改点的X和Y坐标为鼠标在画布中的坐标。释放点通过向canvas绑定mouseup事件,解绑mousemove即可。

贝塞尔曲线的绘制功能:直接套贝塞尔曲线的一般参数公式即可

实现的效果

初始状态                                                        选中点并拖动

改变四个点位置

js代码

    //获取上下文
    const canvas = document.getElementById("my_Canvas");
    gl = canvas.getContext("experimental-webgl");

    //编写Shader代码,并编译
    const vertCode = `
      attribute vec3 coordinates;
      attribute vec3 color;

      varying vec3 vColor;

      void main(void){
        gl_Position = vec4(coordinates,1.0);
        gl_PointSize = 10.0;
        vColor = color;
      }
      `;

    const vertShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertShader, vertCode);
    gl.compileShader(vertShader);

    const fragCode = `
      precision mediump float;
      varying vec3 vColor;
      void main(void){
      gl_FragColor = vec4(vColor, 1.0);
      }
      `;

    const fragShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragShader, fragCode);
    gl.compileShader(fragShader);

    //创建并设置着色器程序
    const shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertShader);
    gl.attachShader(shaderProgram, fragShader);
    gl.linkProgram(shaderProgram);
    gl.useProgram(shaderProgram);

    //定义控制点的顶点,索引
    var vertices = new Float32Array([
      -0.75, 0.0, 0.0,
      -0.25, 0.0, 0,
      0.25, 0.0, 0,
      0.75, 0.0, 0.0,
    ]);

    var indices = new Uint16Array([
      0, 1, 2, 3,
    ]);

    var colors = new Float32Array([
      0.0, 0.0, 0.0,
      0.0, 0.0, 0.0,
      0.0, 0.0, 0.0,
      0.0, 0.0, 0.0,
    ])

    var choose = null;

    //创建缓冲区并向缓冲区传入数据
    const vertex_buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    const coord = gl.getAttribLocation(shaderProgram, "coordinates");
    gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(coord);

    const index_Buffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_Buffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

    const color_buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
    gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);

    const color = gl.getAttribLocation(shaderProgram, "color");
    gl.vertexAttribPointer(color, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(color);



    //进行绘制初始图形
    gl.clearColor(0.5, 0.5, 0.5, 1.0);
    gl.enable(gl.DEPTH_TEST);
    drawPoint()
    drawBezier()

    //传入控制点数据并绘制控制点
    function drawPoint() {
      gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
      gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
      gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
      gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.drawElements(gl.POINTS, indices.length, gl.UNSIGNED_SHORT, 0);

    }

    //通过鼠标的坐标,计算出在[-1,1]区域的坐标,并判断选中了哪个控制点
    function computePosition(clientX, clientY) {
      const click_x = clientX;
      const click_y = clientY;
      const x = (click_x / canvas.width) * 2 - 1;
      const y = 1 - (click_y / canvas.height) * 2;

      for (let i = 0; i <= indices.length; i++) {
        let pointX = vertices[i * 3];
        let pointY = vertices[i * 3 + 1];
        let absX = Math.abs(pointX - x);
        let absY = Math.abs(pointY - y);
        if (absX <= 0.1 && absY <= 0.1) {
          return i;
        }
      }
      return null;
    }

    //鼠标移动时就进行一次刷新画面
    function move(event) {
      let x = (event.clientX / canvas.width) * 2 - 1;
      let y = 1 - (event.clientY / canvas.height) * 2;
      vertices[choose * 3] = x;
      vertices[choose * 3 + 1] = y;
      colors[choose * 3] = 1.0
      drawPoint();
      drawBezier()
    }

    //鼠标按下时选中点
    canvas.addEventListener('mousedown', function (e) {
      choose = computePosition(e.clientX, e.clientY);
      if (choose != null) {
        canvas.addEventListener('mousemove', move)
      }
    })

    //鼠标松开时释放选中的点
    canvas.addEventListener('mouseup', function (event) {
      canvas.removeEventListener('mousemove', move);
      colors[choose * 3] = 0.0;
      drawPoint();
      drawBezier()
    })

    //通过控制点,绘制贝塞尔曲线
    function drawBezier() {
      let b_points = []
      let b_indices = []
      let cnt = 0;
      for (let t = 0; t <= 1; t = t + 0.01) {
        let x = (1 - t) ** 3 * vertices[0] + 3 * (1 - t) ** 2 * t * vertices[3] + 3 * (1 - t) * t ** 2 * vertices[6] + t ** 3 * vertices[9];
        let y = (1 - t) ** 3 * vertices[1] + 3 * (1 - t) ** 2 * t * vertices[4] + 3 * (1 - t) * t ** 2 * vertices[7] + t ** 3 * vertices[10];
        b_points.push(x, y, 0);
        b_indices.push(cnt);
        cnt++;
      }
      gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(b_points), gl.STATIC_DRAW);

      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(b_indices), gl.STATIC_DRAW);

      gl.drawElements(gl.LINE_STRIP, b_indices.length, gl.UNSIGNED_SHORT, 0);
    }

标签:const,为例,BUFFER,0.0,曲线,贝塞尔,color,ARRAY,gl
From: https://blog.csdn.net/weixin_50780569/article/details/140908961

相关文章

  • logback下日志输出前处理操作——以日志脱敏为例
    使用lockback目前JavaSpring服务在打印日志时一般使用slf4j和logback这种组合,其基本原理图如下具体的:大多数会先定义一个loackback-dev.xml文件,而后使用<appender>标签定义输出格式<appendername="file"class="ch.qos.logback.core.rolling.RollingFileAppender">......
  • 整数二分(以数的范围例题为例)
    整数二分算法是一种在有序数组序列中查找特定元素的高效算法。‌ 它通过反复将搜索范围缩小一半来进行搜索,‌从而快速找到目标元素的位置。‌这种算法适用于处理已排序的数组,‌通过不断地将搜索区间一分为二,‌来缩小查找范围,‌直到找到目标元素或者搜索区间为空。‌整数二分算......
  • 曲线取经Android SDK
    本文已经失效了,现在可以很快地从AndroidStudio下载所需的SDK。目前保留本文,以备不时之需。上下文在装AndroidStudio时,或者说无论干啥,想开发Android就需要AndroidSDK。有人说可以直接用什么镜像作为代理,但没用。所以,我突然想到我本来就有AndroidSDK,是安装VisualStudio时顺带......
  • Python的GDAL库绘制多波段、长时序遥感影像时间曲线图
      本文介绍基于Python中的gdal模块,对大量长时间序列的栅格遥感影像文件,绘制其每一个波段中、若干随机指定的像元的时间序列曲线图的方法。  在之前的文章中,我们就已经介绍过基于gdal模块,对大量多时相栅格图像,批量绘制像元时间序列折线图的方法。不过当时文章中的需求,每1个时......
  • 下一代云电脑技术来临,为什么PC Farm才是未来,以ToDesk为例
    近年来飞速发展的云电脑技术,正在挤压传统电脑的生存空间。由于用户对电脑计算能力的要求日益增高,而传统电脑往往会受限于硬件性能无法更新,更换花费较高等因素,难以满足用户对高性能电脑的期待。与此同时,下一代的云电脑技术中PCFarm模式,以其卓越的性能、灵活性和成本效益进入用户......
  • Gromacs-2024.1 GPU版本编译,--以RockyLinux系统为例
    1、首先安装好gcc套件、gcc-toolset-9、cmake、nvidia_driver、cuda、openmpi等软件;2、解压gromacs的源码包;3、编译:a.节点内并行多线程版本,首先sclenablegcc-toolset-9bash加载gcc9以支持C++17特性,cdgromacs-2024.2&&mkdirbuild&&cmake…/-DGMX_BUILD_OWN_FF......
  • C# Chart折线图使用鼠标滚轮放大、缩小和平移曲线方式
    使用鼠标滚轮滚动放大和缩小X轴的宽度,鼠标左键按住拖动实现曲线的左右平移,不再使用滚动条。添加鼠标滚轮事件在chart控件自带的鼠标事件中并没有鼠标的滚轮事件,因此需要手动添加一下,在窗体的Designer.cs文件下的InitializeComponent()函数中添加如下代码this.chart1.MouseWheel......
  • 热力图 以分类为例
    逻辑回归可以分为线性与非线性,也可以根据类的个数分为二分类与多分类问题,使用时需要灵活应用,能够构造损失函数并求梯度,同时能够用算法实现并进行训练预测。事实上,观察后会发现,在逻辑回归中,我们发现是多个输入(即p个指标),最终输出一个结果(0或1),处理过程是输入乘上权重w加偏置b,再对结......
  • 使用 Python 平滑和对称不规则形状和曲线
    我需要完成三项任务:正则化曲线曲线的对称性完成不完整的曲线例如,这里是输入和预期的输出图像:输入输出|||在一般设置中,形状可以由任何SVG曲线基元(贝塞尔曲线、直线、弧线)表示。为了统一表示,示例包含曲线的折线近似。这些折线保存为......
  • Python拟合曲线
    拟合曲线多项式拟合np.ployfit(x,y,deg)importmatplotlib.pyplotaspltimportnumpyasnpx=[1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8]y=[33.40,79.50,122.65,159.05,189.15,214.15,238.65,252.2,267.55,280.50,296.65,301.65,310.......